diff options
-rw-r--r-- | editors/vi.c | 156 |
1 files changed, 87 insertions, 69 deletions
diff --git a/editors/vi.c b/editors/vi.c index 71086c901..a978e0fcb 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -306,8 +306,8 @@ struct globals { smallint editing; // >0 while we are editing a file // [code audit says "can be 0, 1 or 2 only"] smallint cmd_mode; // 0=command 1=insert 2=replace - int file_modified; // buffer contents changed (counter, not flag!) - int last_file_modified; // = -1; + int modified_count; // buffer contents changed if !0 + int last_modified_count; // = -1; int save_argc; // how many file names on cmd line int cmdcnt; // repetition count unsigned rows, columns; // the terminal screen is this size @@ -378,37 +378,37 @@ struct globals { char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2]; #if ENABLE_FEATURE_VI_UNDO // undo_push() operations -#define UNDO_INS 0 -#define UNDO_DEL 1 -#define UNDO_INS_CHAIN 2 -#define UNDO_DEL_CHAIN 3 +#define UNDO_INS 0 +#define UNDO_DEL 1 +#define UNDO_INS_CHAIN 2 +#define UNDO_DEL_CHAIN 3 // UNDO_*_QUEUED must be equal to UNDO_xxx ORed with UNDO_QUEUED_FLAG #define UNDO_QUEUED_FLAG 4 -#define UNDO_INS_QUEUED 4 -#define UNDO_DEL_QUEUED 5 -#define UNDO_USE_SPOS 32 -#define UNDO_EMPTY 64 +#define UNDO_INS_QUEUED 4 +#define UNDO_DEL_QUEUED 5 +#define UNDO_USE_SPOS 32 +#define UNDO_EMPTY 64 // Pass-through flags for functions that can be undone -#define NO_UNDO 0 -#define ALLOW_UNDO 1 +#define NO_UNDO 0 +#define ALLOW_UNDO 1 #define ALLOW_UNDO_CHAIN 2 -#if ENABLE_FEATURE_VI_UNDO_QUEUE +# if ENABLE_FEATURE_VI_UNDO_QUEUE #define ALLOW_UNDO_QUEUED 3 char undo_queue_state; int undo_q; char *undo_queue_spos; // Start position of queued operation char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX]; -#else +# else // If undo queuing disabled, don't invoke the missing queue logic #define ALLOW_UNDO_QUEUED 1 -#endif /* ENABLE_FEATURE_VI_UNDO_QUEUE */ +# endif struct undo_object { struct undo_object *prev; // Linking back avoids list traversal (LIFO) - int u_type; // 0=deleted, 1=inserted, 2=swapped int start; // Offset where the data should be restored/deleted int length; // total data size - char *undo_text; // ptr to text that will be inserted + uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped + char undo_text[1]; // text that was deleted (if deletion) } *undo_stack_tail; #endif /* ENABLE_FEATURE_VI_UNDO */ @@ -423,8 +423,8 @@ struct globals { #define vi_setops (G.vi_setops ) #define editing (G.editing ) #define cmd_mode (G.cmd_mode ) -#define file_modified (G.file_modified ) -#define last_file_modified (G.last_file_modified ) +#define modified_count (G.modified_count ) +#define last_modified_count (G.last_modified_count) #define save_argc (G.save_argc ) #define cmdcnt (G.cmdcnt ) #define rows (G.rows ) @@ -485,7 +485,7 @@ struct globals { #define INIT_G() do { \ SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ - last_file_modified = -1; \ + last_modified_count = -1; \ /* "" but has space for 2 chars: */ \ IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \ } while (0) @@ -608,6 +608,7 @@ static char what_reg(void); // what is letter of current YDreg static void check_context(char); // remember context for '' command #endif #if ENABLE_FEATURE_VI_UNDO +static void flush_undo_data(void); static void undo_push(char *, unsigned int, unsigned char); // Push an operation on the undo stack static void undo_pop(void); // Undo the last operation # if ENABLE_FEATURE_VI_UNDO_QUEUE @@ -616,6 +617,7 @@ static void undo_queue_commit(void); // Flush any queued objects to the undo sta # define undo_queue_commit() ((void)0) # endif #else +#define flush_undo_data() ((void)0) #define undo_queue_commit() ((void)0) #endif @@ -725,6 +727,8 @@ static int init_text_buffer(char *fn) int rc; int size = file_size(fn); // file size. -1 means does not exist. + flush_undo_data(); + /* allocate/reallocate text buffer */ free(text); text_size = size + 10240; @@ -735,14 +739,14 @@ static int init_text_buffer(char *fn) current_filename = xstrdup(fn); } if (size < 0) { - // file dont exist. Start empty buf with dummy line + // file doesnt exist. Start empty buf with dummy line char_insert(text, '\n', NO_UNDO); rc = 0; } else { rc = file_insert(fn, text, 1); } - file_modified = 0; - last_file_modified = -1; + modified_count = 0; + last_modified_count = -1; #if ENABLE_FEATURE_VI_YANKMARK /* init the marks. */ memset(mark, 0, sizeof(mark)); @@ -1124,7 +1128,7 @@ static void colon(char *buf) dot_skip_over_ws(); } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file // don't edit, if the current file has been modified - if (file_modified && !useforce) { + if (modified_count && !useforce) { status_line_bold("No write since last change (:%s! overrides)", cmd); goto ret; } @@ -1227,7 +1231,7 @@ static void colon(char *buf) goto ret; } // don't exit if the file been modified - if (file_modified) { + if (modified_count) { status_line_bold("No write since last change (:%s! overrides)", cmd); goto ret; } @@ -1280,10 +1284,10 @@ static void colon(char *buf) // if the insert is before "dot" then we need to update if (q <= dot) dot += ch; - // file_modified++; + // modified_count++; } } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args - if (file_modified && !useforce) { + if (modified_count && !useforce) { status_line_bold("No write since last change (:%s! overrides)", cmd); } else { // reset the filenames to edit @@ -1438,8 +1442,8 @@ static void colon(char *buf) } else { status_line("'%s' %dL, %dC", fn, li, l); if (q == text && r == end - 1 && l == ch) { - file_modified = 0; - last_file_modified = -1; + modified_count = 0; + last_modified_count = -1; } if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n' || cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N' @@ -1940,7 +1944,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' # endif } #else - file_modified++; + modified_count++; #endif /* ENABLE_FEATURE_VI_UNDO */ p++; } else if (c == 27) { // Is this an ESC? @@ -1988,7 +1992,7 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p' # endif } #else - file_modified++; + modified_count++; #endif /* ENABLE_FEATURE_VI_UNDO */ p += 1 + stupid_insert(p, c); // insert the char #if ENABLE_FEATURE_VI_SETOPTS @@ -2206,8 +2210,19 @@ static void showmatching(char *p) #endif /* FEATURE_VI_SETOPTS */ #if ENABLE_FEATURE_VI_UNDO +static void flush_undo_data(void) +{ + struct undo_object *undo_entry; + + while (undo_stack_tail) { + undo_entry = undo_stack_tail; + undo_stack_tail = undo_entry->prev; + free(undo_entry); + } +} + // Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com) -static void undo_push(char *src, unsigned int length, unsigned char u_type) // Add to the undo stack +static void undo_push(char *src, unsigned int length, uint8_t u_type) // Add to the undo stack { struct undo_object *undo_entry; @@ -2275,10 +2290,19 @@ static void undo_push(char *src, unsigned int length, unsigned char u_type) // A u_type = u_type & ~UNDO_QUEUED_FLAG; #endif - // Allocate a new undo object and use it as the stack tail - undo_entry = xzalloc(sizeof(*undo_entry)); - undo_entry->prev = undo_stack_tail; - undo_stack_tail = undo_entry; + // Allocate a new undo object + if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) { + // For UNDO_DEL objects, save deleted text + if ((src + length) == end) + length--; + // If this deletion empties text[], strip the newline. When the buffer becomes + // zero-length, a newline is added back, which requires this to compensate. + undo_entry = xzalloc(offsetof(struct undo_object, undo_text) + length); + memcpy(undo_entry->undo_text, src, length); + } else { + undo_entry = xzalloc(sizeof(*undo_entry)); + } + undo_entry->length = length; #if ENABLE_FEATURE_VI_UNDO_QUEUE if ((u_type & UNDO_USE_SPOS) != 0) { undo_entry->start = undo_queue_spos - text; // use start position from queue @@ -2289,18 +2313,12 @@ static void undo_push(char *src, unsigned int length, unsigned char u_type) // A #else undo_entry->start = src - text; #endif - // For UNDO_DEL objects, copy the deleted text somewhere undo_entry->u_type = u_type; - if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) { - if ((src + length) == end) - length--; - // If this deletion empties text[], strip the newline. When the buffer becomes - // zero-length, a newline is added back, which requires this to compensate. - undo_entry->undo_text = xmalloc(length); - memcpy(undo_entry->undo_text, src, length); - } - undo_entry->length = length; - file_modified++; + + // Push it on undo stack + undo_entry->prev = undo_stack_tail; + undo_stack_tail = undo_entry; + modified_count++; } static void undo_pop(void) // Undo the last operation @@ -2326,9 +2344,8 @@ static void undo_pop(void) // Undo the last operation u_start = text + undo_entry->start; text_hole_make(u_start, undo_entry->length); memcpy(u_start, undo_entry->undo_text, undo_entry->length); - free(undo_entry->undo_text); status_line("Undo [%d] %s %d chars at position %d", - file_modified, "restored", + modified_count, "restored", undo_entry->length, undo_entry->start ); break; @@ -2339,7 +2356,7 @@ static void undo_pop(void) // Undo the last operation u_end = u_start - 1 + undo_entry->length; text_hole_delete(u_start, u_end, NO_UNDO); status_line("Undo [%d] %s %d chars at position %d", - file_modified, "deleted", + modified_count, "deleted", undo_entry->length, undo_entry->start ); break; @@ -2360,7 +2377,7 @@ static void undo_pop(void) // Undo the last operation // Deallocate the undo object we just processed undo_stack_tail = undo_entry->prev; free(undo_entry); - file_modified--; + modified_count--; // For chained operations, continue popping all the way down the chain. if (repeat) { undo_pop(); // Follow the undo chain if one exists @@ -2452,13 +2469,13 @@ static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through break; # endif } - file_modified--; + modified_count--; #endif if (src < text || src > end) goto thd0; if (dest < text || dest >= end) goto thd0; - file_modified++; + modified_count++; if (src >= end) goto thd_atend; // just delete the end of the buffer memmove(dest, src, cnt); @@ -2885,7 +2902,7 @@ static int file_insert(const char *fn, char *p, int update_ro_status) status_line_bold("can't read '%s'", fn); } // if (cnt >= size) -// file_modified++; +// modified_count++; close(fd); fi0: #if ENABLE_FEATURE_VI_READONLY @@ -2922,7 +2939,7 @@ static int file_write(char *fn, char *first, char *last) ftruncate(fd, charcnt); if (charcnt == cnt) { // good write - //file_modified = FALSE; + //modified_count = FALSE; } else { charcnt = 0; } @@ -3144,7 +3161,7 @@ static int format_edit_status(void) int cur, percent, ret, trunc_at; - // file_modified is now a counter rather than a flag. this + // modified_count is now a counter rather than a flag. this // helps reduce the amount of line counting we need to do. // (this will cause a mis-reporting of modified status // once every MAXINT editing operations.) @@ -3154,11 +3171,12 @@ static int format_edit_status(void) // we're on, then we shouldn't have to do this count_lines() cur = count_lines(text, dot); - // reduce counting -- the total lines can't have - // changed if we haven't done any edits. - if (file_modified != last_file_modified) { + // count_lines() is expensive. + // Call it only if something was changed since last time + // we were here: + if (modified_count != last_modified_count) { tot = cur + count_lines(dot, end - 1) - 1; - last_file_modified = file_modified; + last_modified_count = modified_count; } // current line percent @@ -3185,7 +3203,7 @@ static int format_edit_status(void) #if ENABLE_FEATURE_VI_READONLY (readonly_mode ? " [Readonly]" : ""), #endif - (file_modified ? " [Modified]" : ""), + (modified_count ? " [Modified]" : ""), cur, tot, percent); if (ret >= 0 && ret < trunc_at) @@ -3814,7 +3832,7 @@ static void do_cmd(int c) if (strncmp(p, "quit", cnt) == 0 || strncmp(p, "q!", cnt) == 0 // delete lines ) { - if (file_modified && p[1] != '!') { + if (modified_count && p[1] != '!') { status_line_bold("No write since last change (:%s! overrides)", p); } else { editing = 0; @@ -3829,8 +3847,8 @@ static void do_cmd(int c) if (cnt == -1) status_line_bold("Write error: %s", strerror(errno)); } else { - file_modified = 0; - last_file_modified = -1; + modified_count = 0; + last_modified_count = -1; status_line("'%s' %dL, %dC", current_filename, count_lines(text, end - 1), cnt); if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n' || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N' @@ -3963,7 +3981,7 @@ static void do_cmd(int c) undo_push((dot - 1), 1, UNDO_INS_CHAIN); #else *dot++ = ' '; - file_modified++; + modified_count++; #endif while (isblank(*dot)) { // delete leading WS text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN); @@ -4035,7 +4053,7 @@ static void do_cmd(int c) indicate_error(c); break; } - if (file_modified) { + if (modified_count) { if (ENABLE_FEATURE_VI_READONLY && readonly_mode) { status_line_bold("'%s' is read only", current_filename); break; @@ -4172,7 +4190,7 @@ static void do_cmd(int c) undo_push(dot, 1, UNDO_INS_CHAIN); #else *dot = c1; - file_modified++; + modified_count++; #endif } end_cmd_q(); // stop adding to q @@ -4226,10 +4244,10 @@ static void do_cmd(int c) #else if (islower(*dot)) { *dot = toupper(*dot); - file_modified++; + modified_count++; } else if (isupper(*dot)) { *dot = tolower(*dot); - file_modified++; + modified_count++; } #endif dot_right(); |