Changeset 3621 in MondoRescue for branches/3.3/mindi-busybox/editors/vi.c
- Timestamp:
- Dec 20, 2016, 4:07:32 PM (7 years ago)
- Location:
- branches/3.3
- Files:
-
- 1 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
branches/3.3/mindi-busybox/editors/vi.c
r3232 r3621 18 18 * More intelligence in refresh() 19 19 * ":r !cmd" and "!cmd" to filter text through an external command 20 * A true "undo" facility21 20 * An "ex" line oriented mode- maybe using "cmdedit" 22 21 */ … … 137 136 //config: 138 137 //config: This is not clean but helps a lot on serial lines and such. 138 //config:config FEATURE_VI_UNDO 139 //config: bool "Support undo command 'u'" 140 //config: default y 141 //config: depends on VI 142 //config: help 143 //config: Support the 'u' command to undo insertion, deletion, and replacement 144 //config: of text. 145 //config:config FEATURE_VI_UNDO_QUEUE 146 //config: bool "Enable undo operation queuing" 147 //config: default y 148 //config: depends on FEATURE_VI_UNDO 149 //config: help 150 //config: The vi undo functions can use an intermediate queue to greatly lower 151 //config: malloc() calls and overhead. When the maximum size of this queue is 152 //config: reached, the contents of the queue are committed to the undo stack. 153 //config: This increases the size of the undo code and allows some undo 154 //config: operations (especially un-typing/backspacing) to be far more useful. 155 //config:config FEATURE_VI_UNDO_QUEUE_MAX 156 //config: int "Maximum undo character queue size" 157 //config: default 256 158 //config: range 32 65536 159 //config: depends on FEATURE_VI_UNDO_QUEUE 160 //config: help 161 //config: This option sets the number of bytes used at runtime for the queue. 162 //config: Smaller values will create more undo objects and reduce the amount 163 //config: of typed or backspaced characters that are grouped into one undo 164 //config: operation; larger values increase the potential size of each undo 165 //config: and will generally malloc() larger objects and less frequently. 166 //config: Unless you want more (or less) frequent "undo points" while typing, 167 //config: you should probably leave this unchanged. 139 168 140 169 //applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP)) … … 223 252 // vda: removed "aAiIs" as they switch us into insert mode 224 253 // and remembering input for replay after them makes no sense 225 static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";254 static const char modifying_cmds[] ALIGN1 = "cCdDJoOpPrRxX<>~"; 226 255 #endif 227 256 … … 278 307 // [code audit says "can be 0, 1 or 2 only"] 279 308 smallint cmd_mode; // 0=command 1=insert 2=replace 280 int file_modified; // buffer contents changed (counter, not flag!)281 int last_ file_modified;// = -1;309 int modified_count; // buffer contents changed if !0 310 int last_modified_count; // = -1; 282 311 int save_argc; // how many file names on cmd line 283 312 int cmdcnt; // repetition count … … 348 377 349 378 char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2]; 379 #if ENABLE_FEATURE_VI_UNDO 380 // undo_push() operations 381 #define UNDO_INS 0 382 #define UNDO_DEL 1 383 #define UNDO_INS_CHAIN 2 384 #define UNDO_DEL_CHAIN 3 385 // UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG 386 #define UNDO_QUEUED_FLAG 4 387 #define UNDO_INS_QUEUED 4 388 #define UNDO_DEL_QUEUED 5 389 #define UNDO_USE_SPOS 32 390 #define UNDO_EMPTY 64 391 // Pass-through flags for functions that can be undone 392 #define NO_UNDO 0 393 #define ALLOW_UNDO 1 394 #define ALLOW_UNDO_CHAIN 2 395 # if ENABLE_FEATURE_VI_UNDO_QUEUE 396 #define ALLOW_UNDO_QUEUED 3 397 char undo_queue_state; 398 int undo_q; 399 char *undo_queue_spos; // Start position of queued operation 400 char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX]; 401 # else 402 // If undo queuing disabled, don't invoke the missing queue logic 403 #define ALLOW_UNDO_QUEUED 1 404 # endif 405 406 struct undo_object { 407 struct undo_object *prev; // Linking back avoids list traversal (LIFO) 408 int start; // Offset where the data should be restored/deleted 409 int length; // total data size 410 uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped 411 char undo_text[1]; // text that was deleted (if deletion) 412 } *undo_stack_tail; 413 #endif /* ENABLE_FEATURE_VI_UNDO */ 350 414 }; 351 415 #define G (*ptr_to_globals) … … 359 423 #define editing (G.editing ) 360 424 #define cmd_mode (G.cmd_mode ) 361 #define file_modified (G.file_modified)362 #define last_ file_modified (G.last_file_modified)425 #define modified_count (G.modified_count ) 426 #define last_modified_count (G.last_modified_count) 363 427 #define save_argc (G.save_argc ) 364 428 #define cmdcnt (G.cmdcnt ) … … 409 473 #define get_input_line__buf (G.get_input_line__buf) 410 474 475 #if ENABLE_FEATURE_VI_UNDO 476 #define undo_stack_tail (G.undo_stack_tail ) 477 # if ENABLE_FEATURE_VI_UNDO_QUEUE 478 #define undo_queue_state (G.undo_queue_state) 479 #define undo_q (G.undo_q ) 480 #define undo_queue (G.undo_queue ) 481 #define undo_queue_spos (G.undo_queue_spos ) 482 # endif 483 #endif 484 411 485 #define INIT_G() do { \ 412 486 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ 413 last_ file_modified= -1; \487 last_modified_count = -1; \ 414 488 /* "" but has space for 2 chars: */ \ 415 489 IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \ … … 417 491 418 492 419 static int init_text_buffer(char *); // init from file or create new420 493 static void edit_file(char *); // edit one file 421 494 static void do_cmd(int); // execute a command … … 428 501 static char *end_screen(void); // get pointer to last char on screen 429 502 static int count_lines(char *, char *); // count line from start to stop 430 static char *find_line(int); // find begin ing of line #li503 static char *find_line(int); // find beginning of line #li 431 504 static char *move_to_col(char *, int); // move "p" to column l 432 505 static void dot_left(void); // move dot left- dont leave line … … 438 511 static void dot_scroll(int, int); // move the screen up or down 439 512 static void dot_skip_over_ws(void); // move dot pat WS 440 static void dot_delete(void); // delete the char at 'dot'441 513 static char *bound_dot(char *); // make sure text[0] <= P < "end" 442 514 static char *new_screen(int, int); // malloc virtual screen memory 443 static char *char_insert(char *, char); // insert the char c at 'p' 515 #if !ENABLE_FEATURE_VI_UNDO 516 #define char_insert(a,b,c) char_insert(a,b) 517 #endif 518 static char *char_insert(char *, char, int); // insert the char c at 'p' 444 519 // might reallocate text[]! use p += stupid_insert(p, ...), 445 520 // and be careful to not use pointers into potentially freed text[]! … … 449 524 static char *skip_thing(char *, int, int, int); // skip some object 450 525 static char *find_pair(char *, char); // find matching pair () [] {} 451 static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole 526 #if !ENABLE_FEATURE_VI_UNDO 527 #define text_hole_delete(a,b,c) text_hole_delete(a,b) 528 #endif 529 static char *text_hole_delete(char *, char *, int); // at "p", delete a 'size' byte hole 452 530 // might reallocate text[]! use p += text_hole_make(p, ...), 453 531 // and be careful to not use pointers into potentially freed text[]! 454 532 static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole 455 static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete 533 #if !ENABLE_FEATURE_VI_UNDO 534 #define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d) 535 #endif 536 static char *yank_delete(char *, char *, int, int, int); // yank text[] into register then delete 456 537 static void show_help(void); // display some help info 457 538 static void rawmode(void); // set "raw" mode on tty … … 461 542 static int readit(void); // read (maybe cursor) key from stdin 462 543 static int get_one_char(void); // read 1 char from stdin 463 static int file_size(const char *); // what is the byte size of "fn"464 #if !ENABLE_FEATURE_VI_READONLY465 #define file_insert(fn, p, update_ro_status) file_insert(fn, p)466 #endif467 544 // file_insert might reallocate text[]! 468 545 static int file_insert(const char *, char *, int); … … 479 556 static void status_line(const char *, ...); // print to status buf 480 557 static void status_line_bold(const char *, ...); 558 static void status_line_bold_errno(const char *fn); 481 559 static void not_implemented(const char *); // display "Not implemented" message 482 560 static int format_edit_status(void); // format file status on status line … … 485 563 static void refresh(int); // update the terminal from screen[] 486 564 487 static void Indicate_Error(void); // use flash or beep to indicate error 488 #define indicate_error(c) Indicate_Error() 565 static void indicate_error(void); // use flash or beep to indicate error 489 566 static void Hit_Return(void); 490 567 … … 495 572 static char *get_one_address(char *, int *); // get colon addr, if present 496 573 static char *get_address(char *, int *, int *); // get two colon addrs, if present 574 #endif 497 575 static void colon(char *); // execute the "colon" mode cmds 498 #endif499 576 #if ENABLE_FEATURE_VI_USE_SIGNALS 500 577 static void winch_sig(int); // catch window size changes … … 514 591 // might reallocate text[]! use p += string_insert(p, ...), 515 592 // and be careful to not use pointers into potentially freed text[]! 516 static uintptr_t string_insert(char *, const char *); // insert the string at 'p' 593 # if !ENABLE_FEATURE_VI_UNDO 594 #define string_insert(a,b,c) string_insert(a,b) 595 # endif 596 static uintptr_t string_insert(char *, const char *, int); // insert the string at 'p' 517 597 #endif 518 598 #if ENABLE_FEATURE_VI_YANKMARK … … 521 601 static void check_context(char); // remember context for '' command 522 602 #endif 603 #if ENABLE_FEATURE_VI_UNDO 604 static void flush_undo_data(void); 605 static void undo_push(char *, unsigned int, unsigned char); // Push an operation on the undo stack 606 static void undo_pop(void); // Undo the last operation 607 # if ENABLE_FEATURE_VI_UNDO_QUEUE 608 static void undo_queue_commit(void); // Flush any queued objects to the undo stack 609 # else 610 # define undo_queue_commit() ((void)0) 611 # endif 612 #else 613 #define flush_undo_data() ((void)0) 614 #define undo_queue_commit() ((void)0) 615 #endif 616 523 617 #if ENABLE_FEATURE_VI_CRASHME 524 618 static void crash_dummy(); … … 527 621 #endif 528 622 529 530 623 static void write1(const char *out) 531 624 { … … 539 632 540 633 INIT_G(); 634 635 #if ENABLE_FEATURE_VI_UNDO 636 /* undo_stack_tail = NULL; - already is */ 637 #if ENABLE_FEATURE_VI_UNDO_QUEUE 638 undo_queue_state = UNDO_EMPTY; 639 /* undo_q = 0; - already is */ 640 #endif 641 #endif 541 642 542 643 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME … … 618 719 { 619 720 int rc; 620 int size = file_size(fn); // file size. -1 means does not exist. 721 722 flush_undo_data(); 723 modified_count = 0; 724 last_modified_count = -1; 725 #if ENABLE_FEATURE_VI_YANKMARK 726 /* init the marks */ 727 memset(mark, 0, sizeof(mark)); 728 #endif 621 729 622 730 /* allocate/reallocate text buffer */ 623 731 free(text); 624 text_size = size +10240;732 text_size = 10240; 625 733 screenbegin = dot = end = text = xzalloc(text_size); 626 734 … … 629 737 current_filename = xstrdup(fn); 630 738 } 631 if (size < 0) { 632 // file dont exist. Start empty buf with dummy line 633 char_insert(text, '\n'); 634 rc = 0; 635 } else { 636 rc = file_insert(fn, text, 1); 637 } 638 file_modified = 0; 639 last_file_modified = -1; 640 #if ENABLE_FEATURE_VI_YANKMARK 641 /* init the marks. */ 642 memset(mark, 0, sizeof(mark)); 643 #endif 739 rc = file_insert(fn, text, 1); 740 if (rc < 0) { 741 // file doesnt exist. Start empty buf with dummy line 742 char_insert(text, '\n', NO_UNDO); 743 } 644 744 return rc; 645 745 } … … 756 856 } else { 757 857 crashme = 0; 758 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n" ); // insert the string858 string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n", NO_UNDO); // insert the string 759 859 dot = text; 760 860 refresh(FALSE); … … 909 1009 #endif 910 1010 1011 #endif /* FEATURE_VI_COLON */ 1012 911 1013 // buf must be no longer than MAX_INPUT_LEN! 912 1014 static void colon(char *buf) 913 1015 { 1016 #if !ENABLE_FEATURE_VI_COLON 1017 /* Simple ":cmd" handler with minimal set of commands */ 1018 char *p = buf; 1019 int cnt; 1020 1021 if (*p == ':') 1022 p++; 1023 cnt = strlen(p); 1024 if (cnt == 0) 1025 return; 1026 if (strncmp(p, "quit", cnt) == 0 1027 || strncmp(p, "q!", cnt) == 0 1028 ) { 1029 if (modified_count && p[1] != '!') { 1030 status_line_bold("No write since last change (:%s! overrides)", p); 1031 } else { 1032 editing = 0; 1033 } 1034 return; 1035 } 1036 if (strncmp(p, "write", cnt) == 0 1037 || strncmp(p, "wq", cnt) == 0 1038 || strncmp(p, "wn", cnt) == 0 1039 || (p[0] == 'x' && !p[1]) 1040 ) { 1041 cnt = file_write(current_filename, text, end - 1); 1042 if (cnt < 0) { 1043 if (cnt == -1) 1044 status_line_bold("Write error: %s", strerror(errno)); 1045 } else { 1046 modified_count = 0; 1047 last_modified_count = -1; 1048 status_line("'%s' %dL, %dC", 1049 current_filename, 1050 count_lines(text, end - 1), cnt 1051 ); 1052 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' 1053 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N' 1054 ) { 1055 editing = 0; 1056 } 1057 } 1058 return; 1059 } 1060 if (strncmp(p, "file", cnt) == 0) { 1061 last_status_cksum = 0; // force status update 1062 return; 1063 } 1064 if (sscanf(p, "%d", &cnt) > 0) { 1065 dot = find_line(cnt); 1066 dot_skip_over_ws(); 1067 return; 1068 } 1069 not_implemented(p); 1070 #else 1071 914 1072 char c, *orig_buf, *buf1, *q, *r; 915 1073 char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN]; 916 int i, l, li, ch,b, e;917 int useforce , forced = FALSE;1074 int i, l, li, b, e; 1075 int useforce; 918 1076 919 1077 // :3154 // if (-e line 3154) goto it else stay put … … 937 1095 buf++; // move past the ':' 938 1096 939 li = ch =i = 0;1097 li = i = 0; 940 1098 b = e = -1; 941 1099 q = text; // assume 1,$ for the range … … 1015 1173 r = end_line(dot); 1016 1174 } 1017 dot = yank_delete(q, r, 1, YANKDEL ); // save, then delete lines1175 dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines 1018 1176 dot_skip_over_ws(); 1019 1177 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file 1178 int size; 1179 1020 1180 // don't edit, if the current file has been modified 1021 if ( file_modified&& !useforce) {1181 if (modified_count && !useforce) { 1022 1182 status_line_bold("No write since last change (:%s! overrides)", cmd); 1023 1183 goto ret; … … 1035 1195 } 1036 1196 1037 if (init_text_buffer(fn) < 0) 1038 goto ret; 1197 size = init_text_buffer(fn); 1039 1198 1040 1199 #if ENABLE_FEATURE_VI_YANKMARK … … 1050 1209 // how many lines in text[]? 1051 1210 li = count_lines(text, end - 1); 1052 status_line(" \"%s\"%s"1211 status_line("'%s'%s" 1053 1212 IF_FEATURE_VI_READONLY("%s") 1054 " %dL, %dC", current_filename, 1055 (file_size(fn) < 0 ? " [New file]" : ""), 1213 " %dL, %dC", 1214 current_filename, 1215 (size < 0 ? " [New file]" : ""), 1056 1216 IF_FEATURE_VI_READONLY( 1057 1217 ((readonly_mode) ? " [Readonly]" : ""), 1058 1218 ) 1059 li, ch); 1219 li, (int)(end - text) 1220 ); 1060 1221 } else if (strncmp(cmd, "file", i) == 0) { // what File is this 1061 1222 if (b != -1 || e != -1) { … … 1122 1283 } 1123 1284 // don't exit if the file been modified 1124 if ( file_modified) {1285 if (modified_count) { 1125 1286 status_line_bold("No write since last change (:%s! overrides)", cmd); 1126 1287 goto ret; … … 1146 1307 editing = 0; 1147 1308 } else if (strncmp(cmd, "read", i) == 0) { // read file into text[] 1309 int size; 1310 1148 1311 fn = args; 1149 1312 if (!fn[0]) { … … 1155 1318 } 1156 1319 // read after current line- unless user said ":0r foo" 1157 if (b != 0) 1320 if (b != 0) { 1158 1321 q = next_line(q); 1322 // read after last line 1323 if (q == end-1) 1324 ++q; 1325 } 1159 1326 { // dance around potentially-reallocated text[] 1160 1327 uintptr_t ofs = q - text; 1161 ch= file_insert(fn, q, 0);1328 size = file_insert(fn, q, 0); 1162 1329 q = text + ofs; 1163 1330 } 1164 if ( ch< 0)1331 if (size < 0) 1165 1332 goto ret; // nothing was inserted 1166 1333 // how many lines in text[]? 1167 li = count_lines(q, q + ch- 1);1168 status_line(" \"%s\""1334 li = count_lines(q, q + size - 1); 1335 status_line("'%s'" 1169 1336 IF_FEATURE_VI_READONLY("%s") 1170 " %dL, %dC", fn, 1337 " %dL, %dC", 1338 fn, 1171 1339 IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),) 1172 li, ch); 1173 if (ch > 0) { 1340 li, size 1341 ); 1342 if (size > 0) { 1174 1343 // if the insert is before "dot" then we need to update 1175 1344 if (q <= dot) 1176 dot += ch; 1177 /*file_modified++; - done by file_insert */ 1345 dot += size; 1178 1346 } 1179 1347 } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args 1180 if ( file_modified&& !useforce) {1348 if (modified_count && !useforce) { 1181 1349 status_line_bold("No write since last change (:%s! overrides)", cmd); 1182 1350 } else { … … 1235 1403 size_t len_F, len_R; 1236 1404 int gflag; // global replace flag 1405 #if ENABLE_FEATURE_VI_UNDO 1406 int dont_chain_first_item = ALLOW_UNDO; 1407 #endif 1237 1408 1238 1409 // F points to the "find" pattern … … 1269 1440 uintptr_t bias; 1270 1441 // we found the "find" pattern - delete it 1271 text_hole_delete(found, found + len_F - 1); 1272 // inset the "replace" patern 1273 bias = string_insert(found, R); // insert the string 1442 // For undo support, the first item should not be chained 1443 text_hole_delete(found, found + len_F - 1, dont_chain_first_item); 1444 #if ENABLE_FEATURE_VI_UNDO 1445 dont_chain_first_item = ALLOW_UNDO_CHAIN; 1446 #endif 1447 // insert the "replace" patern 1448 bias = string_insert(found, R, ALLOW_UNDO_CHAIN); 1274 1449 found += bias; 1275 1450 ls += bias; … … 1293 1468 || (cmd[0] == 'x' && !cmd[1]) 1294 1469 ) { 1470 int size; 1471 //int forced = FALSE; 1472 1295 1473 // is there a file name to write to? 1296 1474 if (args[0]) { … … 1299 1477 #if ENABLE_FEATURE_VI_READONLY 1300 1478 if (readonly_mode && !useforce) { 1301 status_line_bold(" \"%s\" Fileis read only", fn);1479 status_line_bold("'%s' is read only", fn); 1302 1480 goto ret; 1303 1481 } … … 1305 1483 // how many lines in text[]? 1306 1484 li = count_lines(q, r); 1307 ch = r - q + 1; 1308 // see if file exists- if not, its just a new file request 1309 if (useforce) { 1485 size = r - q + 1; 1486 //if (useforce) { 1310 1487 // if "fn" is not write-able, chmod u+w 1311 1488 // sprintf(syscmd, "chmod u+w %s", fn); 1312 1489 // system(syscmd); 1313 forced = TRUE;1314 }1490 // forced = TRUE; 1491 //} 1315 1492 l = file_write(fn, q, r); 1316 if (useforce && forced) {1493 //if (useforce && forced) { 1317 1494 // chmod u-w 1318 1495 // sprintf(syscmd, "chmod u-w %s", fn); 1319 1496 // system(syscmd); 1320 forced = FALSE;1321 }1497 // forced = FALSE; 1498 //} 1322 1499 if (l < 0) { 1323 1500 if (l == -1) 1324 status_line_bold ("\"%s\" %s", fn, strerror(errno));1501 status_line_bold_errno(fn); 1325 1502 } else { 1326 status_line(" \"%s\"%dL, %dC", fn, li, l);1327 if (q == text && r == end - 1 && l == ch) {1328 file_modified= 0;1329 last_ file_modified= -1;1503 status_line("'%s' %dL, %dC", fn, li, l); 1504 if (q == text && r == end - 1 && l == size) { 1505 modified_count = 0; 1506 last_modified_count = -1; 1330 1507 } 1331 1508 if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' 1332 1509 || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N' 1333 1510 ) 1334 && l == ch1511 && l == size 1335 1512 ) { 1336 1513 editing = 0; … … 1359 1536 status_line(":s expression missing delimiters"); 1360 1537 #endif 1361 }1362 1363 1538 #endif /* FEATURE_VI_COLON */ 1539 } 1364 1540 1365 1541 static void Hit_Return(void) … … 1508 1684 static char *prev_line(char *p) // return pointer first char prev line 1509 1685 { 1510 p = begin_line(p); // goto begin ing of cur line1686 p = begin_line(p); // goto beginning of cur line 1511 1687 if (p > text && p[-1] == '\n') 1512 1688 p--; // step to prev line 1513 p = begin_line(p); // goto begin ing of prev line1689 p = begin_line(p); // goto beginning of prev line 1514 1690 return p; 1515 1691 } … … 1559 1735 } 1560 1736 1561 static char *find_line(int li) // find begin ing of line #li1737 static char *find_line(int li) // find beginning of line #li 1562 1738 { 1563 1739 char *q; … … 1572 1748 static void dot_left(void) 1573 1749 { 1750 undo_queue_commit(); 1574 1751 if (dot > text && dot[-1] != '\n') 1575 1752 dot--; … … 1578 1755 static void dot_right(void) 1579 1756 { 1757 undo_queue_commit(); 1580 1758 if (dot < end - 1 && *dot != '\n') 1581 1759 dot++; … … 1584 1762 static void dot_begin(void) 1585 1763 { 1764 undo_queue_commit(); 1586 1765 dot = begin_line(dot); // return pointer to first char cur line 1587 1766 } … … 1589 1768 static void dot_end(void) 1590 1769 { 1770 undo_queue_commit(); 1591 1771 dot = end_line(dot); // return pointer to last char cur line 1592 1772 } … … 1614 1794 static void dot_next(void) 1615 1795 { 1796 undo_queue_commit(); 1616 1797 dot = next_line(dot); 1617 1798 } … … 1619 1800 static void dot_prev(void) 1620 1801 { 1802 undo_queue_commit(); 1621 1803 dot = prev_line(dot); 1622 1804 } … … 1626 1808 char *q; 1627 1809 1810 undo_queue_commit(); 1628 1811 for (; cnt > 0; cnt--) { 1629 1812 if (dir < 0) { … … 1653 1836 } 1654 1837 1655 static void dot_delete(void) // delete the char at 'dot'1656 {1657 text_hole_delete(dot, dot);1658 }1659 1660 1838 static char *bound_dot(char *p) // make sure text[0] <= P < "end" 1661 1839 { 1662 1840 if (p >= end && end > text) { 1663 1841 p = end - 1; 1664 indicate_error( '1');1842 indicate_error(); 1665 1843 } 1666 1844 if (p < text) { 1667 1845 p = text; 1668 indicate_error( '2');1846 indicate_error(); 1669 1847 } 1670 1848 return p; … … 1707 1885 static char *char_search(char *p, const char *pat, int dir, int range) 1708 1886 { 1887 struct re_pattern_buffer preg; 1888 const char *err; 1709 1889 char *q; 1710 struct re_pattern_buffer preg;1711 1890 int i; 1712 1891 int size; 1713 1892 1714 1893 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED; 1715 preg.translate = 0; 1716 preg.fastmap = 0; 1717 preg.buffer = 0; 1718 preg.allocated = 0; 1894 if (ignorecase) 1895 re_syntax_options = RE_SYNTAX_POSIX_EXTENDED | RE_ICASE; 1896 1897 memset(&preg, 0, sizeof(preg)); 1898 err = re_compile_pattern(pat, strlen(pat), &preg); 1899 if (err != NULL) { 1900 status_line_bold("bad search pattern '%s': %s", pat, err); 1901 return p; 1902 } 1719 1903 1720 1904 // assume a LIMITED forward search 1721 q = next_line(p);1722 q = end_line(q);1723 1905 q = end - 1; 1724 if (dir == BACK) { 1725 q = prev_line(p); 1906 if (dir == BACK) 1726 1907 q = text; 1727 }1728 // count the number of chars to search over, forward or backward1729 size = q - p;1730 if (size < 0)1731 size = p - q;1732 1908 // RANGE could be negative if we are searching backwards 1733 1909 range = q - p; 1734 1735 q = (char *)re_compile_pattern(pat, strlen(pat), (struct re_pattern_buffer *)&preg);1736 if (q != 0) {1737 // The pattern was not compiled1738 status_line_bold("bad search pattern: \"%s\": %s", pat, q);1739 i = 0; // return p if pattern not compiled1740 goto cs1;1741 }1742 1743 1910 q = p; 1911 size = range; 1744 1912 if (range < 0) { 1913 size = -size; 1745 1914 q = p - size; 1746 1915 if (q < text) … … 1748 1917 } 1749 1918 // search for the compiled pattern, preg, in p[] 1750 // range < 0 -search backward1751 // range > 0 -search forward1919 // range < 0: search backward 1920 // range > 0: search forward 1752 1921 // 0 < start < size 1753 // re_search() < 0 not found or error 1754 // re_search() > 0 index of found pattern 1755 // struct pattern char int int int struct reg 1756 // re_search (*pattern_buffer, *string, size, start, range, *regs) 1757 i = re_search(&preg, q, size, 0, range, 0); 1758 if (i == -1) { 1759 p = 0; 1760 i = 0; // return NULL if pattern not found 1761 } 1762 cs1: 1763 if (dir == FORWARD) { 1922 // re_search() < 0: not found or error 1923 // re_search() >= 0: index of found pattern 1924 // struct pattern char int int int struct reg 1925 // re_search(*pattern_buffer, *string, size, start, range, *regs) 1926 i = re_search(&preg, q, size, /*start:*/ 0, range, /*struct re_registers*:*/ NULL); 1927 regfree(&preg); 1928 if (i < 0) 1929 return NULL; 1930 if (dir == FORWARD) 1764 1931 p = p + i; 1765 } else {1932 else 1766 1933 p = p - i; 1767 }1768 1934 return p; 1769 1935 } … … 1790 1956 len = strlen(pat); 1791 1957 if (dir == FORWARD) { 1792 stop = end - 1; // assume range is p -end-11958 stop = end - 1; // assume range is p..end-1 1793 1959 if (range == LIMITED) 1794 1960 stop = next_line(p); // range is to next line … … 1799 1965 } 1800 1966 } else if (dir == BACK) { 1801 stop = text; // assume range is text -p1967 stop = text; // assume range is text..p 1802 1968 if (range == LIMITED) 1803 1969 stop = prev_line(p); // range is to prev line … … 1816 1982 #endif /* FEATURE_VI_SEARCH */ 1817 1983 1818 static char *char_insert(char *p, char c ) // insert the char c at 'p'1984 static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' 1819 1985 { 1820 1986 if (c == 22) { // Is this an ctrl-V? … … 1823 1989 c = get_one_char(); 1824 1990 *p = c; 1991 #if ENABLE_FEATURE_VI_UNDO 1992 switch (undo) { 1993 case ALLOW_UNDO: 1994 undo_push(p, 1, UNDO_INS); 1995 break; 1996 case ALLOW_UNDO_CHAIN: 1997 undo_push(p, 1, UNDO_INS_CHAIN); 1998 break; 1999 # if ENABLE_FEATURE_VI_UNDO_QUEUE 2000 case ALLOW_UNDO_QUEUED: 2001 undo_push(p, 1, UNDO_INS_QUEUED); 2002 break; 2003 # endif 2004 } 2005 #else 2006 modified_count++; 2007 #endif /* ENABLE_FEATURE_VI_UNDO */ 1825 2008 p++; 1826 file_modified++;1827 2009 } else if (c == 27) { // Is this an ESC? 1828 2010 cmd_mode = 0; 2011 undo_queue_commit(); 1829 2012 cmdcnt = 0; 1830 2013 end_cmd_q(); // stop adding to q … … 1834 2017 } 1835 2018 } else if (c == erase_char || c == 8 || c == 127) { // Is this a BS 1836 // 123456789 1837 if ((p[-1] != '\n') && (dot>text)) { 2019 if (p > text) { 1838 2020 p--; 1839 p = text_hole_delete(p, p ); // shrink buffer 1 char2021 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char 1840 2022 } 1841 2023 } else { 1842 #if ENABLE_FEATURE_VI_SETOPTS1843 2024 // insert a char into text[] 1844 char *sp; // "save p"1845 #endif1846 1847 2025 if (c == 13) 1848 2026 c = '\n'; // translate \r to \n 1849 #if ENABLE_FEATURE_VI_SETOPTS 1850 sp = p; // remember addr of insert 1851 #endif 2027 #if ENABLE_FEATURE_VI_UNDO 2028 # if ENABLE_FEATURE_VI_UNDO_QUEUE 2029 if (c == '\n') 2030 undo_queue_commit(); 2031 # endif 2032 switch (undo) { 2033 case ALLOW_UNDO: 2034 undo_push(p, 1, UNDO_INS); 2035 break; 2036 case ALLOW_UNDO_CHAIN: 2037 undo_push(p, 1, UNDO_INS_CHAIN); 2038 break; 2039 # if ENABLE_FEATURE_VI_UNDO_QUEUE 2040 case ALLOW_UNDO_QUEUED: 2041 undo_push(p, 1, UNDO_INS_QUEUED); 2042 break; 2043 # endif 2044 } 2045 #else 2046 modified_count++; 2047 #endif /* ENABLE_FEATURE_VI_UNDO */ 1852 2048 p += 1 + stupid_insert(p, c); // insert the char 1853 2049 #if ENABLE_FEATURE_VI_SETOPTS 1854 if (showmatch && strchr(")]}", *sp) != NULL) {1855 showmatching( sp);2050 if (showmatch && strchr(")]}", c) != NULL) { 2051 showmatching(p - 1); 1856 2052 } 1857 2053 if (autoindent && c == '\n') { // auto indent the new line … … 1865 2061 p += bias; 1866 2062 q += bias; 2063 #if ENABLE_FEATURE_VI_UNDO 2064 undo_push(p, len, UNDO_INS); 2065 #endif 1867 2066 memcpy(p, q, len); 1868 2067 p += len; … … 1882 2081 p += bias; 1883 2082 *p = c; 1884 //file_modified++; - done by text_hole_make()1885 2083 return bias; 1886 2084 } … … 2011 2209 2012 2210 // find matching char of pair () [] {} 2211 // will crash if c is not one of these 2013 2212 static char *find_pair(char *p, const char c) 2014 2213 { 2015 char match, *q; 2214 const char *braces = "()[]{}"; 2215 char match; 2016 2216 int dir, level; 2017 2217 2018 match = ')'; 2218 dir = strchr(braces, c) - braces; 2219 dir ^= 1; 2220 match = braces[dir]; 2221 dir = ((dir & 1) << 1) - 1; /* 1 for ([{, -1 for )\} */ 2222 2223 // look for match, count levels of pairs (( )) 2019 2224 level = 1; 2020 dir = 1; // assume forward 2021 switch (c) { 2022 case '(': match = ')'; break; 2023 case '[': match = ']'; break; 2024 case '{': match = '}'; break; 2025 case ')': match = '('; dir = -1; break; 2026 case ']': match = '['; dir = -1; break; 2027 case '}': match = '{'; dir = -1; break; 2028 } 2029 for (q = p + dir; text <= q && q < end; q += dir) { 2030 // look for match, count levels of pairs (( )) 2031 if (*q == c) 2225 for (;;) { 2226 p += dir; 2227 if (p < text || p >= end) 2228 return NULL; 2229 if (*p == c) 2032 2230 level++; // increase pair levels 2033 if (* q == match)2231 if (*p == match) { 2034 2232 level--; // reduce pair level 2035 if (level == 0) 2036 break; // found matching pair 2037 } 2038 if (level != 0) 2039 q = NULL; // indicate no match 2040 return q; 2233 if (level == 0) 2234 return p; // found matching pair 2235 } 2236 } 2041 2237 } 2042 2238 … … 2050 2246 q = find_pair(p, *p); // get loc of matching char 2051 2247 if (q == NULL) { 2052 indicate_error( '3'); // no matching char2248 indicate_error(); // no matching char 2053 2249 } else { 2054 2250 // "q" now points to matching pair … … 2062 2258 } 2063 2259 #endif /* FEATURE_VI_SETOPTS */ 2260 2261 #if ENABLE_FEATURE_VI_UNDO 2262 static void flush_undo_data(void) 2263 { 2264 struct undo_object *undo_entry; 2265 2266 while (undo_stack_tail) { 2267 undo_entry = undo_stack_tail; 2268 undo_stack_tail = undo_entry->prev; 2269 free(undo_entry); 2270 } 2271 } 2272 2273 // Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com) 2274 static void undo_push(char *src, unsigned int length, uint8_t u_type) // Add to the undo stack 2275 { 2276 struct undo_object *undo_entry; 2277 2278 // "u_type" values 2279 // UNDO_INS: insertion, undo will remove from buffer 2280 // UNDO_DEL: deleted text, undo will restore to buffer 2281 // UNDO_{INS,DEL}_CHAIN: Same as above but also calls undo_pop() when complete 2282 // The CHAIN operations are for handling multiple operations that the user 2283 // performs with a single action, i.e. REPLACE mode or find-and-replace commands 2284 // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue 2285 // for the INS/DEL operation. The raw values should be equal to the values of 2286 // UNDO_{INS,DEL} ORed with UNDO_QUEUED_FLAG 2287 2288 #if ENABLE_FEATURE_VI_UNDO_QUEUE 2289 // This undo queuing functionality groups multiple character typing or backspaces 2290 // into a single large undo object. This greatly reduces calls to malloc() for 2291 // single-character operations while typing and has the side benefit of letting 2292 // an undo operation remove chunks of text rather than a single character. 2293 switch (u_type) { 2294 case UNDO_EMPTY: // Just in case this ever happens... 2295 return; 2296 case UNDO_DEL_QUEUED: 2297 if (length != 1) 2298 return; // Only queue single characters 2299 switch (undo_queue_state) { 2300 case UNDO_EMPTY: 2301 undo_queue_state = UNDO_DEL; 2302 case UNDO_DEL: 2303 undo_queue_spos = src; 2304 undo_q++; 2305 undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q] = *src; 2306 // If queue is full, dump it into an object 2307 if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX) 2308 undo_queue_commit(); 2309 return; 2310 case UNDO_INS: 2311 // Switch from storing inserted text to deleted text 2312 undo_queue_commit(); 2313 undo_push(src, length, UNDO_DEL_QUEUED); 2314 return; 2315 } 2316 break; 2317 case UNDO_INS_QUEUED: 2318 if (length != 1) 2319 return; 2320 switch (undo_queue_state) { 2321 case UNDO_EMPTY: 2322 undo_queue_state = UNDO_INS; 2323 undo_queue_spos = src; 2324 case UNDO_INS: 2325 undo_q++; // Don't need to save any data for insertions 2326 if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX) 2327 undo_queue_commit(); 2328 return; 2329 case UNDO_DEL: 2330 // Switch from storing deleted text to inserted text 2331 undo_queue_commit(); 2332 undo_push(src, length, UNDO_INS_QUEUED); 2333 return; 2334 } 2335 break; 2336 } 2337 #else 2338 // If undo queuing is disabled, ignore the queuing flag entirely 2339 u_type = u_type & ~UNDO_QUEUED_FLAG; 2340 #endif 2341 2342 // Allocate a new undo object 2343 if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) { 2344 // For UNDO_DEL objects, save deleted text 2345 if ((src + length) == end) 2346 length--; 2347 // If this deletion empties text[], strip the newline. When the buffer becomes 2348 // zero-length, a newline is added back, which requires this to compensate. 2349 undo_entry = xzalloc(offsetof(struct undo_object, undo_text) + length); 2350 memcpy(undo_entry->undo_text, src, length); 2351 } else { 2352 undo_entry = xzalloc(sizeof(*undo_entry)); 2353 } 2354 undo_entry->length = length; 2355 #if ENABLE_FEATURE_VI_UNDO_QUEUE 2356 if ((u_type & UNDO_USE_SPOS) != 0) { 2357 undo_entry->start = undo_queue_spos - text; // use start position from queue 2358 } else { 2359 undo_entry->start = src - text; // use offset from start of text buffer 2360 } 2361 u_type = (u_type & ~UNDO_USE_SPOS); 2362 #else 2363 undo_entry->start = src - text; 2364 #endif 2365 undo_entry->u_type = u_type; 2366 2367 // Push it on undo stack 2368 undo_entry->prev = undo_stack_tail; 2369 undo_stack_tail = undo_entry; 2370 modified_count++; 2371 } 2372 2373 static void undo_pop(void) // Undo the last operation 2374 { 2375 int repeat; 2376 char *u_start, *u_end; 2377 struct undo_object *undo_entry; 2378 2379 // Commit pending undo queue before popping (should be unnecessary) 2380 undo_queue_commit(); 2381 2382 undo_entry = undo_stack_tail; 2383 // Check for an empty undo stack 2384 if (!undo_entry) { 2385 status_line("Already at oldest change"); 2386 return; 2387 } 2388 2389 switch (undo_entry->u_type) { 2390 case UNDO_DEL: 2391 case UNDO_DEL_CHAIN: 2392 // make hole and put in text that was deleted; deallocate text 2393 u_start = text + undo_entry->start; 2394 text_hole_make(u_start, undo_entry->length); 2395 memcpy(u_start, undo_entry->undo_text, undo_entry->length); 2396 status_line("Undo [%d] %s %d chars at position %d", 2397 modified_count, "restored", 2398 undo_entry->length, undo_entry->start 2399 ); 2400 break; 2401 case UNDO_INS: 2402 case UNDO_INS_CHAIN: 2403 // delete what was inserted 2404 u_start = undo_entry->start + text; 2405 u_end = u_start - 1 + undo_entry->length; 2406 text_hole_delete(u_start, u_end, NO_UNDO); 2407 status_line("Undo [%d] %s %d chars at position %d", 2408 modified_count, "deleted", 2409 undo_entry->length, undo_entry->start 2410 ); 2411 break; 2412 } 2413 repeat = 0; 2414 switch (undo_entry->u_type) { 2415 // If this is the end of a chain, lower modification count and refresh display 2416 case UNDO_DEL: 2417 case UNDO_INS: 2418 dot = (text + undo_entry->start); 2419 refresh(FALSE); 2420 break; 2421 case UNDO_DEL_CHAIN: 2422 case UNDO_INS_CHAIN: 2423 repeat = 1; 2424 break; 2425 } 2426 // Deallocate the undo object we just processed 2427 undo_stack_tail = undo_entry->prev; 2428 free(undo_entry); 2429 modified_count--; 2430 // For chained operations, continue popping all the way down the chain. 2431 if (repeat) { 2432 undo_pop(); // Follow the undo chain if one exists 2433 } 2434 } 2435 2436 #if ENABLE_FEATURE_VI_UNDO_QUEUE 2437 static void undo_queue_commit(void) // Flush any queued objects to the undo stack 2438 { 2439 // Pushes the queue object onto the undo stack 2440 if (undo_q > 0) { 2441 // Deleted character undo events grow from the end 2442 undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q, 2443 undo_q, 2444 (undo_queue_state | UNDO_USE_SPOS) 2445 ); 2446 undo_queue_state = UNDO_EMPTY; 2447 undo_q = 0; 2448 } 2449 } 2450 #endif 2451 2452 #endif /* ENABLE_FEATURE_VI_UNDO */ 2064 2453 2065 2454 // open a hole in text[] … … 2094 2483 memmove(p + size, p, end - size - p); 2095 2484 memset(p, ' ', size); // clear new hole 2096 file_modified++;2097 2485 return bias; 2098 2486 } 2099 2487 2100 2488 // close a hole in text[] 2101 static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive 2489 // "undo" value indicates if this operation should be undo-able 2490 static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through "q", inclusive 2102 2491 { 2103 2492 char *src, *dest; … … 2114 2503 hole_size = q - p + 1; 2115 2504 cnt = end - src; 2505 #if ENABLE_FEATURE_VI_UNDO 2506 switch (undo) { 2507 case NO_UNDO: 2508 break; 2509 case ALLOW_UNDO: 2510 undo_push(p, hole_size, UNDO_DEL); 2511 break; 2512 case ALLOW_UNDO_CHAIN: 2513 undo_push(p, hole_size, UNDO_DEL_CHAIN); 2514 break; 2515 # if ENABLE_FEATURE_VI_UNDO_QUEUE 2516 case ALLOW_UNDO_QUEUED: 2517 undo_push(p, hole_size, UNDO_DEL_QUEUED); 2518 break; 2519 # endif 2520 } 2521 modified_count--; 2522 #endif 2116 2523 if (src < text || src > end) 2117 2524 goto thd0; 2118 2525 if (dest < text || dest >= end) 2119 2526 goto thd0; 2527 modified_count++; 2120 2528 if (src >= end) 2121 2529 goto thd_atend; // just delete the end of the buffer … … 2127 2535 if (end <= text) 2128 2536 dest = end = text; // keep pointers valid 2129 file_modified++;2130 2537 thd0: 2131 2538 return dest; … … 2135 2542 // if dist <= 0, do not include, or go past, a NewLine 2136 2543 // 2137 static char *yank_delete(char *start, char *stop, int dist, int yf )2544 static char *yank_delete(char *start, char *stop, int dist, int yf, int undo) 2138 2545 { 2139 2546 char *p; … … 2164 2571 #endif 2165 2572 if (yf == YANKDEL) { 2166 p = text_hole_delete(start, stop );2573 p = text_hole_delete(start, stop, undo); 2167 2574 } // delete lines 2168 2575 return p; … … 2230 2637 // might reallocate text[]! use p += string_insert(p, ...), 2231 2638 // and be careful to not use pointers into potentially freed text[]! 2232 static uintptr_t string_insert(char *p, const char *s ) // insert the string at 'p'2639 static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p' 2233 2640 { 2234 2641 uintptr_t bias; … … 2236 2643 2237 2644 i = strlen(s); 2645 #if ENABLE_FEATURE_VI_UNDO 2646 switch (undo) { 2647 case ALLOW_UNDO: 2648 undo_push(p, i, UNDO_INS); 2649 break; 2650 case ALLOW_UNDO_CHAIN: 2651 undo_push(p, i, UNDO_INS_CHAIN); 2652 break; 2653 } 2654 #endif 2238 2655 bias = text_hole_make(p, i); 2239 2656 p += bias; … … 2387 2804 { 2388 2805 struct pollfd pfd[1]; 2806 2807 if (hund != 0) 2808 fflush_all(); 2389 2809 2390 2810 pfd[0].fd = STDIN_FILENO; … … 2484 2904 } 2485 2905 2486 static int file_size(const char *fn) // what is the byte size of "fn"2487 {2488 struct stat st_buf;2489 int cnt;2490 2491 cnt = -1;2492 if (fn && stat(fn, &st_buf) == 0) // see if file exists2493 cnt = (int) st_buf.st_size;2494 return cnt;2495 }2496 2497 2906 // might reallocate text[]! 2498 static int file_insert(const char *fn, char *p, int update_ro_status)2907 static int file_insert(const char *fn, char *p, int initial) 2499 2908 { 2500 2909 int cnt = -1; … … 2502 2911 struct stat statbuf; 2503 2912 2504 /* Validate file */ 2505 if (stat(fn, &statbuf) < 0) { 2506 status_line_bold("\"%s\" %s", fn, strerror(errno)); 2507 goto fi0; 2508 } 2509 if (!S_ISREG(statbuf.st_mode)) { 2510 // This is not a regular file 2511 status_line_bold("\"%s\" Not a regular file", fn); 2512 goto fi0; 2513 } 2514 if (p < text || p > end) { 2515 status_line_bold("Trying to insert file outside of memory"); 2516 goto fi0; 2517 } 2518 2519 // read file to buffer 2913 if (p < text) 2914 p = text; 2915 if (p > end) 2916 p = end; 2917 2520 2918 fd = open(fn, O_RDONLY); 2521 2919 if (fd < 0) { 2522 status_line_bold("\"%s\" %s", fn, strerror(errno)); 2523 goto fi0; 2524 } 2525 size = statbuf.st_size; 2920 if (!initial) 2921 status_line_bold_errno(fn); 2922 return cnt; 2923 } 2924 2925 /* Validate file */ 2926 if (fstat(fd, &statbuf) < 0) { 2927 status_line_bold_errno(fn); 2928 goto fi; 2929 } 2930 if (!S_ISREG(statbuf.st_mode)) { 2931 status_line_bold("'%s' is not a regular file", fn); 2932 goto fi; 2933 } 2934 size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX); 2526 2935 p += text_hole_make(p, size); 2527 cnt = safe_read(fd, p, size);2936 cnt = full_read(fd, p, size); 2528 2937 if (cnt < 0) { 2529 status_line_bold ("\"%s\" %s", fn, strerror(errno));2530 p = text_hole_delete(p, p + size - 1 ); // un-do buffer insert2938 status_line_bold_errno(fn); 2939 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert 2531 2940 } else if (cnt < size) { 2532 // There was a partial read, shrink unused space text[] 2533 p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert 2534 status_line_bold("can't read all of file \"%s\"", fn); 2535 } 2536 if (cnt >= size) 2537 file_modified++; 2941 // There was a partial read, shrink unused space 2942 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); 2943 status_line_bold("can't read '%s'", fn); 2944 } 2945 fi: 2538 2946 close(fd); 2539 fi0: 2947 2540 2948 #if ENABLE_FEATURE_VI_READONLY 2541 if ( update_ro_status2949 if (initial 2542 2950 && ((access(fn, W_OK) < 0) || 2543 2951 /* root will always have access() … … 2572 2980 if (charcnt == cnt) { 2573 2981 // good write 2574 // file_modified= FALSE;2982 //modified_count = FALSE; 2575 2983 } else { 2576 2984 charcnt = 0; … … 2645 3053 } 2646 3054 2647 static void Indicate_Error(void)3055 static void indicate_error(void) 2648 3056 { 2649 3057 #if ENABLE_FEATURE_VI_CRASHME … … 2718 3126 } 2719 3127 3128 static void status_line_bold_errno(const char *fn) 3129 { 3130 status_line_bold("'%s' %s", fn, strerror(errno)); 3131 } 3132 2720 3133 // format status buffer 2721 3134 static void status_line(const char *format, ...) … … 2789 3202 int cur, percent, ret, trunc_at; 2790 3203 2791 // file_modifiedis now a counter rather than a flag. this3204 // modified_count is now a counter rather than a flag. this 2792 3205 // helps reduce the amount of line counting we need to do. 2793 3206 // (this will cause a mis-reporting of modified status … … 2799 3212 cur = count_lines(text, dot); 2800 3213 2801 // reduce counting -- the total lines can't have 2802 // changed if we haven't done any edits. 2803 if (file_modified != last_file_modified) { 3214 // count_lines() is expensive. 3215 // Call it only if something was changed since last time 3216 // we were here: 3217 if (modified_count != last_modified_count) { 2804 3218 tot = cur + count_lines(dot, end - 1) - 1; 2805 last_ file_modified = file_modified;3219 last_modified_count = modified_count; 2806 3220 } 2807 3221 … … 2830 3244 (readonly_mode ? " [Readonly]" : ""), 2831 3245 #endif 2832 ( file_modified? " [Modified]" : ""),3246 (modified_count ? " [Modified]" : ""), 2833 3247 cur, tot, percent); 2834 3248 … … 2943 3357 } 2944 3358 2945 // see if there are any changes between vi tual screen and out_buf3359 // see if there are any changes between virtual screen and out_buf 2946 3360 changed = FALSE; // assume no change 2947 3361 cs = 0; … … 2980 3394 if (ce > columns - 1) ce = columns - 1; 2981 3395 if (cs > ce) { cs = 0; ce = columns - 1; } 2982 // is there a change between vi tual screen and out_buf3396 // is there a change between virtual screen and out_buf 2983 3397 if (changed) { 2984 3398 // copy changed part of buffer to virtual screen … … 3055 3469 // don't Replace past E-o-l 3056 3470 cmd_mode = 1; // convert to insert 3471 undo_queue_commit(); 3057 3472 } else { 3058 3473 if (1 <= c || Isprint(c)) { 3059 3474 if (c != 27) 3060 dot = yank_delete(dot, dot, 0, YANKDEL ); // delete char3061 dot = char_insert(dot, c ); // insert new char3475 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 3476 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char 3062 3477 } 3063 3478 goto dc1; … … 3069 3484 // insert the char c at "dot" 3070 3485 if (1 <= c || Isprint(c)) { 3071 dot = char_insert(dot, c );3486 dot = char_insert(dot, c, ALLOW_UNDO_QUEUED); 3072 3487 } 3073 3488 goto dc1; … … 3115 3530 //case '_': // _- 3116 3531 //case '`': // `- 3117 //case 'u': // u- FIXME- there is no undo3118 3532 //case 'v': // v- 3119 3533 default: // unrecognized command … … 3182 3596 case 27: // esc 3183 3597 if (cmd_mode == 0) 3184 indicate_error( c);3598 indicate_error(); 3185 3599 cmd_mode = 0; // stop insrting 3600 undo_queue_commit(); 3186 3601 end_cmd_q(); 3187 3602 last_status_cksum = 0; // force status update … … 3200 3615 YDreg = c1; 3201 3616 } else { 3202 indicate_error( c);3617 indicate_error(); 3203 3618 } 3204 3619 break; … … 3218 3633 dot_skip_over_ws(); 3219 3634 } else { 3220 indicate_error( c);3635 indicate_error(); 3221 3636 } 3222 3637 break; … … 3231 3646 mark[c1] = dot; 3232 3647 } else { 3233 indicate_error( c);3648 indicate_error(); 3234 3649 } 3235 3650 break; … … 3258 3673 dot_right(); // move to right, can move to NL 3259 3674 } 3260 string_insert(dot, p ); // insert the string3675 string_insert(dot, p, ALLOW_UNDO); // insert the string 3261 3676 end_cmd_q(); // stop adding to q 3262 3677 break; … … 3265 3680 p = begin_line(dot); 3266 3681 q = end_line(dot); 3267 p = text_hole_delete(p, q ); // delete cur line3268 p += string_insert(p, reg[Ureg] ); // insert orig line3682 p = text_hole_delete(p, q, ALLOW_UNDO); // delete cur line 3683 p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line 3269 3684 dot = p; 3270 3685 dot_skip_over_ws(); … … 3272 3687 break; 3273 3688 #endif /* FEATURE_VI_YANKMARK */ 3689 #if ENABLE_FEATURE_VI_UNDO 3690 case 'u': // u- undo last operation 3691 undo_pop(); 3692 break; 3693 #endif 3274 3694 case '$': // $- goto end of line 3275 3695 case KEYCODE_END: // Cursor Key End … … 3287 3707 p = find_pair(q, *q); 3288 3708 if (p == NULL) { 3289 indicate_error( c);3709 indicate_error(); 3290 3710 } else { 3291 3711 dot = p; … … 3295 3715 } 3296 3716 if (*q == '\n') 3297 indicate_error( c);3717 indicate_error(); 3298 3718 break; 3299 3719 case 'f': // f- forward to a user specified char … … 3424 3844 break; 3425 3845 #endif /* FEATURE_VI_SEARCH */ 3426 case '0': // 0- goto begin ing of line3846 case '0': // 0- goto beginning of line 3427 3847 case '1': // 1- 3428 3848 case '2': // 2- … … 3442 3862 case ':': // :- the colon mode commands 3443 3863 p = get_input_line(":"); // get input line- use "status line" 3444 #if ENABLE_FEATURE_VI_COLON3445 3864 colon(p); // execute the command 3446 #else3447 if (*p == ':')3448 p++; // move past the ':'3449 cnt = strlen(p);3450 if (cnt <= 0)3451 break;3452 if (strncmp(p, "quit", cnt) == 03453 || strncmp(p, "q!", cnt) == 0 // delete lines3454 ) {3455 if (file_modified && p[1] != '!') {3456 status_line_bold("No write since last change (:%s! overrides)", p);3457 } else {3458 editing = 0;3459 }3460 } else if (strncmp(p, "write", cnt) == 03461 || strncmp(p, "wq", cnt) == 03462 || strncmp(p, "wn", cnt) == 03463 || (p[0] == 'x' && !p[1])3464 ) {3465 cnt = file_write(current_filename, text, end - 1);3466 if (cnt < 0) {3467 if (cnt == -1)3468 status_line_bold("Write error: %s", strerror(errno));3469 } else {3470 file_modified = 0;3471 last_file_modified = -1;3472 status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);3473 if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'3474 || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'3475 ) {3476 editing = 0;3477 }3478 }3479 } else if (strncmp(p, "file", cnt) == 0) {3480 last_status_cksum = 0; // force status update3481 } else if (sscanf(p, "%d", &j) > 0) {3482 dot = find_line(j); // go to line # j3483 dot_skip_over_ws();3484 } else { // unrecognized cmd3485 not_implemented(p);3486 }3487 #endif /* !FEATURE_VI_COLON */3488 3865 break; 3489 3866 case '<': // <- Left shift something … … 3492 3869 c1 = get_one_char(); // get the type of thing to delete 3493 3870 find_range(&p, &q, c1); 3494 yank_delete(p, q, 1, YANKONLY ); // save copy before change3871 yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change 3495 3872 p = begin_line(p); 3496 3873 q = end_line(q); … … 3501 3878 if (*p == '\t') { 3502 3879 // shrink buffer 1 char 3503 text_hole_delete(p, p );3880 text_hole_delete(p, p, NO_UNDO); 3504 3881 } else if (*p == ' ') { 3505 3882 // we should be calculating columns, not just SPACE 3506 3883 for (j = 0; *p == ' ' && j < tabstop; j++) { 3507 text_hole_delete(p, p );3884 text_hole_delete(p, p, NO_UNDO); 3508 3885 } 3509 3886 } 3510 3887 } else if (c == '>') { 3511 3888 // shift right -- add tab or 8 spaces 3512 char_insert(p, '\t' );3889 char_insert(p, '\t', ALLOW_UNDO); 3513 3890 } 3514 3891 } … … 3545 3922 dot = dollar_line(dot); // move to before NL 3546 3923 // copy text into a register and delete 3547 dot = yank_delete(save_dot, dot, 0, YANKDEL ); // delete to e-o-l3924 dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l 3548 3925 if (c == 'C') 3549 3926 goto dc_i; // start inserting … … 3590 3967 dc_i: 3591 3968 cmd_mode = 1; // start inserting 3969 undo_queue_commit(); // commit queue when cmd_mode changes 3592 3970 break; 3593 3971 case 'J': // J- join current and next lines together … … 3595 3973 dot_end(); // move to NL 3596 3974 if (dot < end - 1) { // make sure not last char in text[] 3975 #if ENABLE_FEATURE_VI_UNDO 3976 undo_push(dot, 1, UNDO_DEL); 3597 3977 *dot++ = ' '; // replace NL with space 3598 file_modified++; 3978 undo_push((dot - 1), 1, UNDO_INS_CHAIN); 3979 #else 3980 *dot++ = ' '; 3981 modified_count++; 3982 #endif 3599 3983 while (isblank(*dot)) { // delete leading WS 3600 dot_delete();3984 text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN); 3601 3985 } 3602 3986 } … … 3627 4011 case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..." 3628 4012 dot_end(); 3629 dot = char_insert(dot, '\n' );4013 dot = char_insert(dot, '\n', ALLOW_UNDO); 3630 4014 } else { 3631 4015 dot_begin(); // 0 3632 dot = char_insert(dot, '\n' ); // i\n ESC4016 dot = char_insert(dot, '\n', ALLOW_UNDO); // i\n ESC 3633 4017 dot_prev(); // - 3634 4018 } … … 3638 4022 dc5: 3639 4023 cmd_mode = 2; 4024 undo_queue_commit(); 3640 4025 break; 3641 4026 case KEYCODE_DELETE: 3642 c = 'x'; 3643 // fall through 4027 if (dot < end - 1) 4028 dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO); 4029 break; 3644 4030 case 'X': // X- delete char before dot 3645 4031 case 'x': // x- delete the current char … … 3652 4038 if (c == 'X') 3653 4039 dot--; // delete prev char 3654 dot = yank_delete(dot, dot, 0, YANKDEL ); // delete char4040 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 3655 4041 } 3656 4042 } while (--cmdcnt > 0); … … 3663 4049 c1 = get_one_char(); 3664 4050 if (c1 != 'Z') { 3665 indicate_error( c);4051 indicate_error(); 3666 4052 break; 3667 4053 } 3668 if ( file_modified) {4054 if (modified_count) { 3669 4055 if (ENABLE_FEATURE_VI_READONLY && readonly_mode) { 3670 status_line_bold(" \"%s\" Fileis read only", current_filename);4056 status_line_bold("'%s' is read only", current_filename); 3671 4057 break; 3672 4058 } … … 3723 4109 // determine range, and whether it spans lines 3724 4110 ml = find_range(&p, &q, c1); 4111 place_cursor(0, 0); 3725 4112 if (c1 == 27) { // ESC- user changed mind and wants out 3726 4113 c = c1 = 27; // Escape- do nothing … … 3734 4121 } 3735 4122 } 3736 dot = yank_delete(p, q, ml, yf ); // delete word4123 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word 3737 4124 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) { 3738 4125 // partial line copy text into a register and delete 3739 dot = yank_delete(p, q, ml, yf ); // delete word4126 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word 3740 4127 } else if (strchr("cdykjHL+-{}\r\n", c1)) { 3741 4128 // whole line copy text into a register and delete 3742 dot = yank_delete(p, q, ml, yf ); // delete lines4129 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines 3743 4130 whole = 1; 3744 4131 } else { … … 3746 4133 c = c1 = 27; // error- 3747 4134 ml = 0; 3748 indicate_error( c);4135 indicate_error(); 3749 4136 } 3750 4137 if (ml && whole) { 3751 4138 if (c == 'c') { 3752 dot = char_insert(dot, '\n' );4139 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN); 3753 4140 // on the last line of file don't move to prev line 3754 4141 if (whole && dot != (end-1)) { … … 3796 4183 c1 = get_one_char(); // get the replacement char 3797 4184 if (*dot != '\n') { 4185 #if ENABLE_FEATURE_VI_UNDO 4186 undo_push(dot, 1, UNDO_DEL); 3798 4187 *dot = c1; 3799 file_modified++; 4188 undo_push(dot, 1, UNDO_INS_CHAIN); 4189 #else 4190 *dot = c1; 4191 modified_count++; 4192 #endif 3800 4193 } 3801 4194 end_cmd_q(); // stop adding to q … … 3837 4230 case '~': // ~- flip the case of letters a-z -> A-Z 3838 4231 do { 4232 #if ENABLE_FEATURE_VI_UNDO 4233 if (islower(*dot)) { 4234 undo_push(dot, 1, UNDO_DEL); 4235 *dot = toupper(*dot); 4236 undo_push(dot, 1, UNDO_INS_CHAIN); 4237 } else if (isupper(*dot)) { 4238 undo_push(dot, 1, UNDO_DEL); 4239 *dot = tolower(*dot); 4240 undo_push(dot, 1, UNDO_INS_CHAIN); 4241 } 4242 #else 3839 4243 if (islower(*dot)) { 3840 4244 *dot = toupper(*dot); 3841 file_modified++;4245 modified_count++; 3842 4246 } else if (isupper(*dot)) { 3843 4247 *dot = tolower(*dot); 3844 file_modified++; 3845 } 4248 modified_count++; 4249 } 4250 #endif 3846 4251 dot_right(); 3847 4252 } while (--cmdcnt > 0); … … 3873 4278 // if text[] just became empty, add back an empty line 3874 4279 if (end == text) { 3875 char_insert(text, '\n' ); // start empty buf with dummy line4280 char_insert(text, '\n', NO_UNDO); // start empty buf with dummy line 3876 4281 dot = text; 3877 4282 }
Note:
See TracChangeset
for help on using the changeset viewer.