undo.c (97307B)
1 // undo.c: multi level undo facility 2 3 // The saved lines are stored in a list of lists (one for each buffer): 4 // 5 // b_u_oldhead------------------------------------------------+ 6 // | 7 // V 8 // +--------------+ +--------------+ +--------------+ 9 // b_u_newhead--->| u_header | | u_header | | u_header | 10 // | uh_next------>| uh_next------>| uh_next---->NULL 11 // NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | 12 // | uh_entry | | uh_entry | | uh_entry | 13 // +--------|-----+ +--------|-----+ +--------|-----+ 14 // | | | 15 // V V V 16 // +--------------+ +--------------+ +--------------+ 17 // | u_entry | | u_entry | | u_entry | 18 // | ue_next | | ue_next | | ue_next | 19 // +--------|-----+ +--------|-----+ +--------|-----+ 20 // | | | 21 // V V V 22 // +--------------+ NULL NULL 23 // | u_entry | 24 // | ue_next | 25 // +--------|-----+ 26 // | 27 // V 28 // etc. 29 // 30 // Each u_entry list contains the information for one undo or redo. 31 // curbuf->b_u_curhead points to the header of the last undo (the next redo), 32 // or is NULL if nothing has been undone (end of the branch). 33 // 34 // For keeping alternate undo/redo branches the uh_alt field is used. Thus at 35 // each point in the list a branch may appear for an alternate to redo. The 36 // uh_seq field is numbered sequentially to be able to find a newer or older 37 // branch. 38 // 39 // +---------------+ +---------------+ 40 // b_u_oldhead --->| u_header | | u_header | 41 // | uh_alt_next ---->| uh_alt_next ----> NULL 42 // NULL <----- uh_alt_prev |<------ uh_alt_prev | 43 // | uh_prev | | uh_prev | 44 // +-----|---------+ +-----|---------+ 45 // | | 46 // V V 47 // +---------------+ +---------------+ 48 // | u_header | | u_header | 49 // | uh_alt_next | | uh_alt_next | 50 // b_u_newhead --->| uh_alt_prev | | uh_alt_prev | 51 // | uh_prev | | uh_prev | 52 // +-----|---------+ +-----|---------+ 53 // | | 54 // V V 55 // NULL +---------------+ +---------------+ 56 // | u_header | | u_header | 57 // | uh_alt_next ---->| uh_alt_next | 58 // | uh_alt_prev |<------ uh_alt_prev | 59 // | uh_prev | | uh_prev | 60 // +-----|---------+ +-----|---------+ 61 // | | 62 // etc. etc. 63 // 64 // 65 // All data is allocated and will all be freed when the buffer is unloaded. 66 67 // Uncomment the next line for including the u_check() function. This warns 68 // for errors in the debug information. 69 // #define U_DEBUG 1 70 #define UH_MAGIC 0x18dade // value for uh_magic when in use 71 #define UE_MAGIC 0xabc123 // value for ue_magic when in use 72 73 #include <assert.h> 74 #include <fcntl.h> 75 #include <inttypes.h> 76 #include <stdbool.h> 77 #include <stdio.h> 78 #include <stdlib.h> 79 #include <string.h> 80 #include <time.h> 81 #include <uv.h> 82 83 #include "auto/config.h" 84 #include "klib/kvec.h" 85 #include "nvim/ascii_defs.h" 86 #include "nvim/autocmd.h" 87 #include "nvim/buffer.h" 88 #include "nvim/buffer_defs.h" 89 #include "nvim/buffer_updates.h" 90 #include "nvim/change.h" 91 #include "nvim/cursor.h" 92 #include "nvim/drawscreen.h" 93 #include "nvim/edit.h" 94 #include "nvim/errors.h" 95 #include "nvim/eval/funcs.h" 96 #include "nvim/eval/typval.h" 97 #include "nvim/ex_cmds_defs.h" 98 #include "nvim/ex_docmd.h" 99 #include "nvim/ex_getln.h" 100 #include "nvim/extmark.h" 101 #include "nvim/extmark_defs.h" 102 #include "nvim/fileio.h" 103 #include "nvim/fold.h" 104 #include "nvim/garray.h" 105 #include "nvim/garray_defs.h" 106 #include "nvim/getchar.h" 107 #include "nvim/gettext_defs.h" 108 #include "nvim/globals.h" 109 #include "nvim/highlight_defs.h" 110 #include "nvim/macros_defs.h" 111 #include "nvim/mark.h" 112 #include "nvim/mark_defs.h" 113 #include "nvim/mbyte.h" 114 #include "nvim/memline.h" 115 #include "nvim/memline_defs.h" 116 #include "nvim/memory.h" 117 #include "nvim/message.h" 118 #include "nvim/option.h" 119 #include "nvim/option_vars.h" 120 #include "nvim/os/fs.h" 121 #include "nvim/os/fs_defs.h" 122 #include "nvim/os/input.h" 123 #include "nvim/os/os_defs.h" 124 #include "nvim/os/time.h" 125 #include "nvim/os/time_defs.h" 126 #include "nvim/path.h" 127 #include "nvim/pos_defs.h" 128 #include "nvim/sha256.h" 129 #include "nvim/spell.h" 130 #include "nvim/state.h" 131 #include "nvim/strings.h" 132 #include "nvim/types_defs.h" 133 #include "nvim/undo.h" 134 #include "nvim/undo_defs.h" 135 #include "nvim/vim_defs.h" 136 137 /// Structure passed around between undofile functions. 138 typedef struct { 139 buf_T *bi_buf; 140 FILE *bi_fp; 141 } bufinfo_T; 142 143 #include "undo.c.generated.h" 144 145 static const char e_undo_list_corrupt[] 146 = N_("E439: Undo list corrupt"); 147 static const char e_undo_line_missing[] 148 = N_("E440: Undo line missing"); 149 static const char e_write_error_in_undo_file_str[] 150 = N_("E829: Write error in undo file: %s"); 151 152 // used in undo_end() to report number of added and deleted lines 153 static int u_newcount, u_oldcount; 154 155 // When 'u' flag included in 'cpoptions', we behave like vi. Need to remember 156 // the action that "u" should do. 157 static bool undo_undoes = false; 158 159 static int lastmark = 0; 160 161 #if defined(U_DEBUG) 162 // Check the undo structures for being valid. Print a warning when something 163 // looks wrong. 164 static int seen_b_u_curhead; 165 static int seen_b_u_newhead; 166 static int header_count; 167 168 static void u_check_tree(u_header_T *uhp, u_header_T *exp_uh_next, u_header_T *exp_uh_alt_prev) 169 { 170 if (uhp == NULL) { 171 return; 172 } 173 header_count++; 174 if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1) { 175 emsg("b_u_curhead found twice (looping?)"); 176 return; 177 } 178 if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1) { 179 emsg("b_u_newhead found twice (looping?)"); 180 return; 181 } 182 183 if (uhp->uh_magic != UH_MAGIC) { 184 emsg("uh_magic wrong (may be using freed memory)"); 185 } else { 186 // Check pointers back are correct. 187 if (uhp->uh_next.ptr != exp_uh_next) { 188 emsg("uh_next wrong"); 189 smsg(0, "expected: 0x%x, actual: 0x%x", 190 exp_uh_next, uhp->uh_next.ptr); 191 } 192 if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev) { 193 emsg("uh_alt_prev wrong"); 194 smsg(0, "expected: 0x%x, actual: 0x%x", 195 exp_uh_alt_prev, uhp->uh_alt_prev.ptr); 196 } 197 198 // Check the undo tree at this header. 199 for (u_entry_T *uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) { 200 if (uep->ue_magic != UE_MAGIC) { 201 emsg("ue_magic wrong (may be using freed memory)"); 202 break; 203 } 204 } 205 206 // Check the next alt tree. 207 u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp); 208 209 // Check the next header in this branch. 210 u_check_tree(uhp->uh_prev.ptr, uhp, NULL); 211 } 212 } 213 214 static void u_check(int newhead_may_be_NULL) 215 { 216 seen_b_u_newhead = 0; 217 seen_b_u_curhead = 0; 218 header_count = 0; 219 220 u_check_tree(curbuf->b_u_oldhead, NULL, NULL); 221 222 if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL 223 && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL)) { 224 semsg("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead); 225 } 226 if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0) { 227 semsg("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead); 228 } 229 if (header_count != curbuf->b_u_numhead) { 230 emsg("b_u_numhead invalid"); 231 smsg(0, "expected: %" PRId64 ", actual: %" PRId64, 232 (int64_t)header_count, (int64_t)curbuf->b_u_numhead); 233 } 234 } 235 236 #endif 237 238 /// Save the current line for both the "u" and "U" command. 239 /// Careful: may trigger autocommands that reload the buffer. 240 /// Returns OK or FAIL. 241 int u_save_cursor(void) 242 { 243 linenr_T cur = curwin->w_cursor.lnum; 244 linenr_T top = cur > 0 ? cur - 1 : 0; 245 linenr_T bot = cur + 1; 246 247 return u_save(top, bot); 248 } 249 250 /// Save the lines between "top" and "bot" for both the "u" and "U" command. 251 /// "top" may be 0 and "bot" may be curbuf->b_ml.ml_line_count + 1. 252 /// Careful: may trigger autocommands that reload the buffer. 253 /// Returns FAIL when lines could not be saved, OK otherwise. 254 int u_save(linenr_T top, linenr_T bot) 255 { 256 return u_save_buf(curbuf, top, bot); 257 } 258 259 int u_save_buf(buf_T *buf, linenr_T top, linenr_T bot) 260 { 261 if (top >= bot || bot > (buf->b_ml.ml_line_count + 1)) { 262 return FAIL; // rely on caller to do error messages 263 } 264 265 if (top + 2 == bot) { 266 u_saveline(buf, top + 1); 267 } 268 269 return u_savecommon(buf, top, bot, 0, false); 270 } 271 272 /// Save the line "lnum" (used by ":s" and "~" command). 273 /// The line is replaced, so the new bottom line is lnum + 1. 274 /// Careful: may trigger autocommands that reload the buffer. 275 /// Returns FAIL when lines could not be saved, OK otherwise. 276 int u_savesub(linenr_T lnum) 277 { 278 return u_savecommon(curbuf, lnum - 1, lnum + 1, lnum + 1, false); 279 } 280 281 /// A new line is inserted before line "lnum" (used by :s command). 282 /// The line is inserted, so the new bottom line is lnum + 1. 283 /// Careful: may trigger autocommands that reload the buffer. 284 /// Returns FAIL when lines could not be saved, OK otherwise. 285 int u_inssub(linenr_T lnum) 286 { 287 return u_savecommon(curbuf, lnum - 1, lnum, lnum + 1, false); 288 } 289 290 /// Save the lines "lnum" - "lnum" + nlines (used by delete command). 291 /// The lines are deleted, so the new bottom line is lnum, unless the buffer 292 /// becomes empty. 293 /// Careful: may trigger autocommands that reload the buffer. 294 /// Returns FAIL when lines could not be saved, OK otherwise. 295 int u_savedel(linenr_T lnum, linenr_T nlines) 296 { 297 return u_savecommon(curbuf, lnum - 1, lnum + nlines, 298 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, false); 299 } 300 301 /// Return true when undo is allowed. Otherwise print an error message and 302 /// return false. 303 /// 304 /// @return true if undo is allowed. 305 bool undo_allowed(buf_T *buf) 306 { 307 // Don't allow changes when 'modifiable' is off. 308 if (!MODIFIABLE(buf)) { 309 emsg(_(e_modifiable)); 310 return false; 311 } 312 313 // In the sandbox it's not allowed to change the text. 314 if (sandbox != 0) { 315 emsg(_(e_sandbox)); 316 return false; 317 } 318 319 // Don't allow changes in the buffer while editing the cmdline. The 320 // caller of getcmdline() may get confused. 321 if (textlock != 0 || expr_map_locked()) { 322 emsg(_(e_textlock)); 323 return false; 324 } 325 326 return true; 327 } 328 329 /// Get the 'undolevels' value for the current buffer. 330 static OptInt get_undolevel(buf_T *buf) 331 { 332 if (buf->b_p_ul == NO_LOCAL_UNDOLEVEL) { 333 return p_ul; 334 } 335 return buf->b_p_ul; 336 } 337 338 static inline void zero_fmark_additional_data(fmark_T *fmarks) 339 { 340 for (size_t i = 0; i < NMARKS; i++) { 341 XFREE_CLEAR(fmarks[i].additional_data); 342 } 343 } 344 345 /// Common code for various ways to save text before a change. 346 /// "top" is the line above the first changed line. 347 /// "bot" is the line below the last changed line. 348 /// "newbot" is the new bottom line. Use zero when not known. 349 /// "reload" is true when saving for a buffer reload. 350 /// Careful: may trigger autocommands that reload the buffer. 351 /// Returns FAIL when lines could not be saved, OK otherwise. 352 int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, bool reload) 353 { 354 if (!reload) { 355 // When making changes is not allowed return FAIL. It's a crude way 356 // to make all change commands fail. 357 if (!undo_allowed(buf)) { 358 return FAIL; 359 } 360 361 // Saving text for undo means we are going to make a change. Give a 362 // warning for a read-only file before making the change, so that the 363 // FileChangedRO event can replace the buffer with a read-write version 364 // (e.g., obtained from a source control system). 365 if (buf == curbuf) { 366 change_warning(buf, 0); 367 } 368 369 if (bot > buf->b_ml.ml_line_count + 1) { 370 // This happens when the FileChangedRO autocommand changes the 371 // file in a way it becomes shorter. 372 emsg(_("E881: Line count changed unexpectedly")); 373 return FAIL; 374 } 375 } 376 377 #ifdef U_DEBUG 378 u_check(false); 379 #endif 380 381 u_entry_T *uep; 382 u_entry_T *prev_uep; 383 linenr_T size = bot - top - 1; 384 385 // If buf->b_u_synced == true make a new header. 386 if (buf->b_u_synced) { 387 // Need to create new entry in b_changelist. 388 buf->b_new_change = true; 389 390 u_header_T *uhp; 391 if (get_undolevel(buf) >= 0) { 392 // Make a new header entry. Do this first so that we don't mess 393 // up the undo info when out of memory. 394 uhp = xmalloc(sizeof(u_header_T)); 395 kv_init(uhp->uh_extmark); 396 #ifdef U_DEBUG 397 uhp->uh_magic = UH_MAGIC; 398 #endif 399 } else { 400 uhp = NULL; 401 } 402 403 // If we undid more than we redid, move the entry lists before and 404 // including buf->b_u_curhead to an alternate branch. 405 u_header_T *old_curhead = buf->b_u_curhead; 406 if (old_curhead != NULL) { 407 buf->b_u_newhead = old_curhead->uh_next.ptr; 408 buf->b_u_curhead = NULL; 409 } 410 411 // free headers to keep the size right 412 while (buf->b_u_numhead > get_undolevel(buf) 413 && buf->b_u_oldhead != NULL) { 414 u_header_T *uhfree = buf->b_u_oldhead; 415 416 if (uhfree == old_curhead) { 417 // Can't reconnect the branch, delete all of it. 418 u_freebranch(buf, uhfree, &old_curhead); 419 } else if (uhfree->uh_alt_next.ptr == NULL) { 420 // There is no branch, only free one header. 421 u_freeheader(buf, uhfree, &old_curhead); 422 } else { 423 // Free the oldest alternate branch as a whole. 424 while (uhfree->uh_alt_next.ptr != NULL) { 425 uhfree = uhfree->uh_alt_next.ptr; 426 } 427 u_freebranch(buf, uhfree, &old_curhead); 428 } 429 #ifdef U_DEBUG 430 u_check(true); 431 #endif 432 } 433 434 if (uhp == NULL) { // no undo at all 435 if (old_curhead != NULL) { 436 u_freebranch(buf, old_curhead, NULL); 437 } 438 buf->b_u_synced = false; 439 return OK; 440 } 441 442 uhp->uh_prev.ptr = NULL; 443 uhp->uh_next.ptr = buf->b_u_newhead; 444 uhp->uh_alt_next.ptr = old_curhead; 445 if (old_curhead != NULL) { 446 uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr; 447 448 if (uhp->uh_alt_prev.ptr != NULL) { 449 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp; 450 } 451 452 old_curhead->uh_alt_prev.ptr = uhp; 453 454 if (buf->b_u_oldhead == old_curhead) { 455 buf->b_u_oldhead = uhp; 456 } 457 } else { 458 uhp->uh_alt_prev.ptr = NULL; 459 } 460 461 if (buf->b_u_newhead != NULL) { 462 buf->b_u_newhead->uh_prev.ptr = uhp; 463 } 464 465 uhp->uh_seq = ++buf->b_u_seq_last; 466 buf->b_u_seq_cur = uhp->uh_seq; 467 uhp->uh_time = time(NULL); 468 uhp->uh_save_nr = 0; 469 buf->b_u_time_cur = uhp->uh_time + 1; 470 471 uhp->uh_walk = 0; 472 uhp->uh_entry = NULL; 473 uhp->uh_getbot_entry = NULL; 474 uhp->uh_cursor = curwin->w_cursor; // save cursor pos. for undo 475 if (virtual_active(curwin) && curwin->w_cursor.coladd > 0) { 476 uhp->uh_cursor_vcol = getviscol(); 477 } else { 478 uhp->uh_cursor_vcol = -1; 479 } 480 481 // save changed and buffer empty flag for undo 482 uhp->uh_flags = (buf->b_changed ? UH_CHANGED : 0) + 483 ((buf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); 484 485 // save named marks and Visual marks for undo 486 zero_fmark_additional_data(buf->b_namedm); 487 memmove(uhp->uh_namedm, buf->b_namedm, 488 sizeof(buf->b_namedm[0]) * NMARKS); 489 uhp->uh_visual = buf->b_visual; 490 491 buf->b_u_newhead = uhp; 492 493 if (buf->b_u_oldhead == NULL) { 494 buf->b_u_oldhead = uhp; 495 } 496 buf->b_u_numhead++; 497 } else { 498 if (get_undolevel(buf) < 0) { // no undo at all 499 return OK; 500 } 501 502 // When saving a single line, and it has been saved just before, it 503 // doesn't make sense saving it again. Saves a lot of memory when 504 // making lots of changes inside the same line. 505 // This is only possible if the previous change didn't increase or 506 // decrease the number of lines. 507 // Check the ten last changes. More doesn't make sense and takes too 508 // long. 509 if (size == 1) { 510 uep = u_get_headentry(buf); 511 prev_uep = NULL; 512 for (int i = 0; i < 10; i++) { 513 if (uep == NULL) { 514 break; 515 } 516 517 // If lines have been inserted/deleted we give up. 518 // Also when the line was included in a multi-line save. 519 if ((buf->b_u_newhead->uh_getbot_entry != uep 520 ? (uep->ue_top + uep->ue_size + 1 521 != (uep->ue_bot == 0 522 ? buf->b_ml.ml_line_count + 1 523 : uep->ue_bot)) 524 : uep->ue_lcount != buf->b_ml.ml_line_count) 525 || (uep->ue_size > 1 526 && top >= uep->ue_top 527 && top + 2 <= uep->ue_top + uep->ue_size + 1)) { 528 break; 529 } 530 531 // If it's the same line we can skip saving it again. 532 if (uep->ue_size == 1 && uep->ue_top == top) { 533 if (i > 0) { 534 // It's not the last entry: get ue_bot for the last 535 // entry now. Following deleted/inserted lines go to 536 // the re-used entry. 537 u_getbot(buf); 538 buf->b_u_synced = false; 539 540 // Move the found entry to become the last entry. The 541 // order of undo/redo doesn't matter for the entries 542 // we move it over, since they don't change the line 543 // count and don't include this line. It does matter 544 // for the found entry if the line count is changed by 545 // the executed command. 546 prev_uep->ue_next = uep->ue_next; 547 uep->ue_next = buf->b_u_newhead->uh_entry; 548 buf->b_u_newhead->uh_entry = uep; 549 } 550 551 // The executed command may change the line count. 552 if (newbot != 0) { 553 uep->ue_bot = newbot; 554 } else if (bot > buf->b_ml.ml_line_count) { 555 uep->ue_bot = 0; 556 } else { 557 uep->ue_lcount = buf->b_ml.ml_line_count; 558 buf->b_u_newhead->uh_getbot_entry = uep; 559 } 560 return OK; 561 } 562 prev_uep = uep; 563 uep = uep->ue_next; 564 } 565 } 566 567 // find line number for ue_bot for previous u_save() 568 u_getbot(buf); 569 } 570 571 // add lines in front of entry list 572 uep = xmalloc(sizeof(u_entry_T)); 573 CLEAR_POINTER(uep); 574 #ifdef U_DEBUG 575 uep->ue_magic = UE_MAGIC; 576 #endif 577 578 uep->ue_size = size; 579 uep->ue_top = top; 580 if (newbot != 0) { 581 uep->ue_bot = newbot; 582 // Use 0 for ue_bot if bot is below last line. 583 // Otherwise we have to compute ue_bot later. 584 } else if (bot > buf->b_ml.ml_line_count) { 585 uep->ue_bot = 0; 586 } else { 587 uep->ue_lcount = buf->b_ml.ml_line_count; 588 buf->b_u_newhead->uh_getbot_entry = uep; 589 } 590 591 if (size > 0) { 592 uep->ue_array = xmalloc(sizeof(char *) * (size_t)size); 593 linenr_T lnum; 594 int i; 595 for (i = 0, lnum = top + 1; i < size; i++) { 596 fast_breakcheck(); 597 if (got_int) { 598 u_freeentry(uep, i); 599 return FAIL; 600 } 601 uep->ue_array[i] = u_save_line_buf(buf, lnum++); 602 } 603 } else { 604 uep->ue_array = NULL; 605 } 606 607 uep->ue_next = buf->b_u_newhead->uh_entry; 608 buf->b_u_newhead->uh_entry = uep; 609 if (reload) { 610 // buffer was reloaded, notify text change subscribers 611 buf->b_u_newhead->uh_flags |= UH_RELOAD; 612 } 613 buf->b_u_synced = false; 614 undo_undoes = false; 615 616 #ifdef U_DEBUG 617 u_check(false); 618 #endif 619 return OK; 620 } 621 622 // magic at start of undofile 623 #define UF_START_MAGIC "Vim\237UnDo\345" 624 #define UF_START_MAGIC_LEN 9 625 // magic at start of header 626 #define UF_HEADER_MAGIC 0x5fd0 627 // magic after last header 628 #define UF_HEADER_END_MAGIC 0xe7aa 629 // magic at start of entry 630 #define UF_ENTRY_MAGIC 0xf518 631 // magic after last entry 632 #define UF_ENTRY_END_MAGIC 0x3581 633 634 // 2-byte undofile version number 635 #define UF_VERSION 3 636 637 // extra fields for header 638 #define UF_LAST_SAVE_NR 1 639 640 // extra fields for uhp 641 #define UHP_SAVE_NR 1 642 643 static const char e_not_open[] = N_("E828: Cannot open undo file for writing: %s"); 644 645 /// Compute the hash for a buffer text into hash[UNDO_HASH_SIZE]. 646 /// 647 /// @param[in] buf The buffer used to compute the hash 648 /// @param[in] hash Array of size UNDO_HASH_SIZE in which to store the value of 649 /// the hash 650 void u_compute_hash(buf_T *buf, uint8_t *hash) 651 { 652 context_sha256_T ctx; 653 sha256_start(&ctx); 654 for (linenr_T lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { 655 char *p = ml_get_buf(buf, lnum); 656 sha256_update(&ctx, (uint8_t *)p, strlen(p) + 1); 657 } 658 sha256_finish(&ctx, hash); 659 } 660 661 /// Return an allocated string of the full path of the target undofile. 662 /// 663 /// @param[in] buf_ffname Full file name for which undo file location should 664 /// be found. 665 /// @param[in] reading If true, find the file to read by traversing all of the 666 /// directories in &undodir. If false use the first 667 /// existing directory. If none of the directories in 668 /// &undodir option exist then last directory in the list 669 /// will be automatically created. 670 /// 671 /// @return [allocated] File name to read from/write to or NULL. 672 char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) 673 FUNC_ATTR_WARN_UNUSED_RESULT 674 { 675 const char *ffname = buf_ffname; 676 677 if (ffname == NULL) { 678 return NULL; 679 } 680 681 #ifdef HAVE_READLINK 682 char fname_buf[MAXPATHL]; 683 // Expand symlink in the file name, so that we put the undo file with the 684 // actual file instead of with the symlink. 685 if (resolve_symlink(ffname, fname_buf) == OK) { 686 ffname = fname_buf; 687 } 688 #endif 689 690 char dir_name[MAXPATHL + 1]; 691 char *munged_name = NULL; 692 char *undo_file_name = NULL; 693 694 // Loop over 'undodir'. When reading find the first file that exists. 695 // When not reading use the first directory that exists or ".". 696 char *dirp = p_udir; 697 while (*dirp != NUL) { 698 size_t dir_len = copy_option_part(&dirp, dir_name, MAXPATHL, ","); 699 if (dir_len == 1 && dir_name[0] == '.') { 700 // Use same directory as the ffname, 701 // "dir/name" -> "dir/.name.un~" 702 const size_t ffname_len = strlen(ffname); 703 undo_file_name = xmalloc(ffname_len + 6); 704 memmove(undo_file_name, ffname, ffname_len + 1); 705 char *const tail = path_tail(undo_file_name); 706 const size_t tail_len = strlen(tail); 707 memmove(tail + 1, tail, tail_len + 1); 708 *tail = '.'; 709 memmove(tail + tail_len + 1, ".un~", sizeof(".un~")); 710 } else { 711 dir_name[dir_len] = NUL; 712 713 // Remove trailing pathseps from directory name 714 char *p = &dir_name[dir_len - 1]; 715 while (vim_ispathsep(*p)) { 716 *p-- = NUL; 717 } 718 719 bool has_directory = os_isdir(dir_name); 720 if (!has_directory && *dirp == NUL && !reading) { 721 // Last directory in the list does not exist, create it. 722 int ret; 723 char *failed_dir; 724 if ((ret = os_mkdir_recurse(dir_name, 0755, &failed_dir, NULL)) != 0) { 725 semsg(_("E5003: Unable to create directory \"%s\" for undo file: %s"), 726 failed_dir, os_strerror(ret)); 727 xfree(failed_dir); 728 } else { 729 has_directory = true; 730 } 731 } 732 if (has_directory) { 733 if (munged_name == NULL) { 734 munged_name = xstrdup(ffname); 735 for (char *c = munged_name; *c != NUL; MB_PTR_ADV(c)) { 736 if (vim_ispathsep(*c)) { 737 *c = '%'; 738 } 739 } 740 } 741 undo_file_name = concat_fnames(dir_name, munged_name, true); 742 } 743 } 744 745 // When reading check if the file exists. 746 if (undo_file_name != NULL 747 && (!reading || os_path_exists(undo_file_name))) { 748 break; 749 } 750 XFREE_CLEAR(undo_file_name); 751 } 752 753 xfree(munged_name); 754 return undo_file_name; 755 } 756 757 /// Display an error for corrupted undo file 758 /// 759 /// @param[in] mesg Identifier of the corruption kind. 760 /// @param[in] file_name File in which error occurred. 761 static void corruption_error(const char *const mesg, const char *const file_name) 762 FUNC_ATTR_NONNULL_ALL 763 { 764 semsg(_("E825: Corrupted undo file (%s): %s"), mesg, file_name); 765 } 766 767 static void u_free_uhp(u_header_T *uhp) 768 { 769 u_entry_T *uep = uhp->uh_entry; 770 while (uep != NULL) { 771 u_entry_T *nuep = uep->ue_next; 772 u_freeentry(uep, uep->ue_size); 773 uep = nuep; 774 } 775 xfree(uhp); 776 } 777 778 /// Writes the undofile header. 779 /// 780 /// @param bi The buffer information 781 /// @param hash The hash of the buffer contents 782 // 783 /// @returns false in case of an error. 784 static bool serialize_header(bufinfo_T *bi, uint8_t *hash) 785 FUNC_ATTR_NONNULL_ALL 786 { 787 buf_T *buf = bi->bi_buf; 788 FILE *fp = bi->bi_fp; 789 790 // Start writing, first the magic marker and undo info version. 791 if (fwrite(UF_START_MAGIC, UF_START_MAGIC_LEN, 1, fp) != 1) { 792 return false; 793 } 794 795 undo_write_bytes(bi, UF_VERSION, 2); 796 797 // Write a hash of the buffer text, so that we can verify it is 798 // still the same when reading the buffer text. 799 if (!undo_write(bi, hash, UNDO_HASH_SIZE)) { 800 return false; 801 } 802 803 // Write buffer-specific data. 804 undo_write_bytes(bi, (uintmax_t)buf->b_ml.ml_line_count, 4); 805 size_t len = buf->b_u_line_ptr ? strlen(buf->b_u_line_ptr) : 0; 806 undo_write_bytes(bi, len, 4); 807 if (len > 0 && !undo_write(bi, (uint8_t *)buf->b_u_line_ptr, len)) { 808 return false; 809 } 810 undo_write_bytes(bi, (uintmax_t)buf->b_u_line_lnum, 4); 811 undo_write_bytes(bi, (uintmax_t)buf->b_u_line_colnr, 4); 812 813 // Write undo structures header data. 814 put_header_ptr(bi, buf->b_u_oldhead); 815 put_header_ptr(bi, buf->b_u_newhead); 816 put_header_ptr(bi, buf->b_u_curhead); 817 818 undo_write_bytes(bi, (uintmax_t)buf->b_u_numhead, 4); 819 undo_write_bytes(bi, (uintmax_t)buf->b_u_seq_last, 4); 820 undo_write_bytes(bi, (uintmax_t)buf->b_u_seq_cur, 4); 821 uint8_t time_buf[8]; 822 time_to_bytes(buf->b_u_time_cur, time_buf); 823 undo_write(bi, time_buf, sizeof(time_buf)); 824 825 // Write optional fields. 826 undo_write_bytes(bi, 4, 1); 827 undo_write_bytes(bi, UF_LAST_SAVE_NR, 1); 828 undo_write_bytes(bi, (uintmax_t)buf->b_u_save_nr_last, 4); 829 830 // Write end marker. 831 undo_write_bytes(bi, 0, 1); 832 833 return true; 834 } 835 836 /// Writes an undo header. 837 /// 838 /// @param bi The buffer information 839 /// @param uhp The undo header to write 840 // 841 /// @returns false in case of an error. 842 static bool serialize_uhp(bufinfo_T *bi, u_header_T *uhp) 843 { 844 if (!undo_write_bytes(bi, (uintmax_t)UF_HEADER_MAGIC, 2)) { 845 return false; 846 } 847 848 put_header_ptr(bi, uhp->uh_next.ptr); 849 put_header_ptr(bi, uhp->uh_prev.ptr); 850 put_header_ptr(bi, uhp->uh_alt_next.ptr); 851 put_header_ptr(bi, uhp->uh_alt_prev.ptr); 852 undo_write_bytes(bi, (uintmax_t)uhp->uh_seq, 4); 853 serialize_pos(bi, uhp->uh_cursor); 854 undo_write_bytes(bi, (uintmax_t)uhp->uh_cursor_vcol, 4); 855 undo_write_bytes(bi, (uintmax_t)uhp->uh_flags, 2); 856 // Assume NMARKS will stay the same. 857 for (size_t i = 0; i < (size_t)NMARKS; i++) { 858 serialize_pos(bi, uhp->uh_namedm[i].mark); 859 } 860 serialize_visualinfo(bi, &uhp->uh_visual); 861 uint8_t time_buf[8]; 862 time_to_bytes(uhp->uh_time, time_buf); 863 undo_write(bi, time_buf, sizeof(time_buf)); 864 865 // Write optional fields. 866 undo_write_bytes(bi, 4, 1); 867 undo_write_bytes(bi, UHP_SAVE_NR, 1); 868 undo_write_bytes(bi, (uintmax_t)uhp->uh_save_nr, 4); 869 870 // Write end marker. 871 undo_write_bytes(bi, 0, 1); 872 873 // Write all the entries. 874 for (u_entry_T *uep = uhp->uh_entry; uep; uep = uep->ue_next) { 875 undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2); 876 if (!serialize_uep(bi, uep)) { 877 return false; 878 } 879 } 880 undo_write_bytes(bi, (uintmax_t)UF_ENTRY_END_MAGIC, 2); 881 882 // Write all extmark undo objects 883 for (size_t i = 0; i < kv_size(uhp->uh_extmark); i++) { 884 if (!serialize_extmark(bi, kv_A(uhp->uh_extmark, i))) { 885 return false; 886 } 887 } 888 undo_write_bytes(bi, (uintmax_t)UF_ENTRY_END_MAGIC, 2); 889 890 return true; 891 } 892 893 static u_header_T *unserialize_uhp(bufinfo_T *bi, const char *file_name) 894 { 895 u_header_T *uhp = xmalloc(sizeof(u_header_T)); 896 CLEAR_POINTER(uhp); 897 #ifdef U_DEBUG 898 uhp->uh_magic = UH_MAGIC; 899 #endif 900 uhp->uh_next.seq = undo_read_4c(bi); 901 uhp->uh_prev.seq = undo_read_4c(bi); 902 uhp->uh_alt_next.seq = undo_read_4c(bi); 903 uhp->uh_alt_prev.seq = undo_read_4c(bi); 904 uhp->uh_seq = undo_read_4c(bi); 905 if (uhp->uh_seq <= 0) { 906 corruption_error("uh_seq", file_name); 907 xfree(uhp); 908 return NULL; 909 } 910 unserialize_pos(bi, &uhp->uh_cursor); 911 uhp->uh_cursor_vcol = undo_read_4c(bi); 912 uhp->uh_flags = undo_read_2c(bi); 913 const Timestamp cur_timestamp = os_time(); 914 for (size_t i = 0; i < (size_t)NMARKS; i++) { 915 unserialize_pos(bi, &uhp->uh_namedm[i].mark); 916 uhp->uh_namedm[i].timestamp = cur_timestamp; 917 uhp->uh_namedm[i].fnum = 0; 918 } 919 unserialize_visualinfo(bi, &uhp->uh_visual); 920 uhp->uh_time = undo_read_time(bi); 921 922 // Unserialize optional fields. 923 while (true) { 924 int len = undo_read_byte(bi); 925 926 if (len == EOF) { 927 corruption_error("truncated", file_name); 928 u_free_uhp(uhp); 929 return NULL; 930 } 931 if (len == 0) { 932 break; 933 } 934 int what = undo_read_byte(bi); 935 switch (what) { 936 case UHP_SAVE_NR: 937 uhp->uh_save_nr = undo_read_4c(bi); 938 break; 939 default: 940 // Field not supported, skip it. 941 while (--len >= 0) { 942 undo_read_byte(bi); 943 } 944 } 945 } 946 947 // Unserialize the uep list. 948 u_entry_T *last_uep = NULL; 949 int c; 950 while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC) { 951 bool error = false; 952 u_entry_T *uep = unserialize_uep(bi, &error, file_name); 953 if (last_uep == NULL) { 954 uhp->uh_entry = uep; 955 } else { 956 last_uep->ue_next = uep; 957 } 958 last_uep = uep; 959 if (uep == NULL || error) { 960 u_free_uhp(uhp); 961 return NULL; 962 } 963 } 964 if (c != UF_ENTRY_END_MAGIC) { 965 corruption_error("entry end", file_name); 966 u_free_uhp(uhp); 967 return NULL; 968 } 969 970 // Unserialize all extmark undo information 971 kv_init(uhp->uh_extmark); 972 973 while ((c = undo_read_2c(bi)) == UF_ENTRY_MAGIC) { 974 bool error = false; 975 ExtmarkUndoObject *extup = unserialize_extmark(bi, &error, file_name); 976 if (error) { 977 kv_destroy(uhp->uh_extmark); 978 xfree(extup); 979 return NULL; 980 } 981 kv_push(uhp->uh_extmark, *extup); 982 xfree(extup); 983 } 984 if (c != UF_ENTRY_END_MAGIC) { 985 corruption_error("entry end", file_name); 986 u_free_uhp(uhp); 987 return NULL; 988 } 989 990 return uhp; 991 } 992 993 static bool serialize_extmark(bufinfo_T *bi, ExtmarkUndoObject extup) 994 { 995 if (extup.type == kExtmarkSplice) { 996 undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2); 997 undo_write_bytes(bi, (uintmax_t)extup.type, 4); 998 if (!undo_write(bi, (uint8_t *)&(extup.data.splice), 999 sizeof(ExtmarkSplice))) { 1000 return false; 1001 } 1002 } else if (extup.type == kExtmarkMove) { 1003 undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2); 1004 undo_write_bytes(bi, (uintmax_t)extup.type, 4); 1005 if (!undo_write(bi, (uint8_t *)&(extup.data.move), sizeof(ExtmarkMove))) { 1006 return false; 1007 } 1008 } 1009 // Note: We do not serialize ExtmarkSavePos information, since 1010 // buffer marktrees are not retained when closing/reopening a file 1011 return true; 1012 } 1013 1014 static ExtmarkUndoObject *unserialize_extmark(bufinfo_T *bi, bool *error, const char *filename) 1015 { 1016 uint8_t *buf = NULL; 1017 1018 ExtmarkUndoObject *extup = xmalloc(sizeof(ExtmarkUndoObject)); 1019 1020 UndoObjectType type = (UndoObjectType)undo_read_4c(bi); 1021 extup->type = type; 1022 if (type == kExtmarkSplice) { 1023 size_t n_elems = (size_t)sizeof(ExtmarkSplice) / sizeof(uint8_t); 1024 buf = xcalloc(n_elems, sizeof(uint8_t)); 1025 if (!undo_read(bi, buf, n_elems)) { 1026 goto error; 1027 } 1028 extup->data.splice = *(ExtmarkSplice *)buf; 1029 } else if (type == kExtmarkMove) { 1030 size_t n_elems = (size_t)sizeof(ExtmarkMove) / sizeof(uint8_t); 1031 buf = xcalloc(n_elems, sizeof(uint8_t)); 1032 if (!undo_read(bi, buf, n_elems)) { 1033 goto error; 1034 } 1035 extup->data.move = *(ExtmarkMove *)buf; 1036 } else { 1037 goto error; 1038 } 1039 1040 xfree(buf); 1041 1042 return extup; 1043 1044 error: 1045 xfree(extup); 1046 if (buf) { 1047 xfree(buf); 1048 } 1049 *error = true; 1050 return NULL; 1051 } 1052 1053 /// Serializes "uep". 1054 /// 1055 /// @param bi The buffer information 1056 /// @param uep The undo entry to write 1057 // 1058 /// @returns false in case of an error. 1059 static bool serialize_uep(bufinfo_T *bi, u_entry_T *uep) 1060 { 1061 undo_write_bytes(bi, (uintmax_t)uep->ue_top, 4); 1062 undo_write_bytes(bi, (uintmax_t)uep->ue_bot, 4); 1063 undo_write_bytes(bi, (uintmax_t)uep->ue_lcount, 4); 1064 undo_write_bytes(bi, (uintmax_t)uep->ue_size, 4); 1065 1066 for (size_t i = 0; i < (size_t)uep->ue_size; i++) { 1067 size_t len = strlen(uep->ue_array[i]); 1068 if (!undo_write_bytes(bi, len, 4)) { 1069 return false; 1070 } 1071 if (len > 0 && !undo_write(bi, (uint8_t *)uep->ue_array[i], len)) { 1072 return false; 1073 } 1074 } 1075 return true; 1076 } 1077 1078 static u_entry_T *unserialize_uep(bufinfo_T *bi, bool *error, const char *file_name) 1079 { 1080 u_entry_T *uep = xmalloc(sizeof(u_entry_T)); 1081 CLEAR_POINTER(uep); 1082 #ifdef U_DEBUG 1083 uep->ue_magic = UE_MAGIC; 1084 #endif 1085 uep->ue_top = undo_read_4c(bi); 1086 uep->ue_bot = undo_read_4c(bi); 1087 uep->ue_lcount = undo_read_4c(bi); 1088 uep->ue_size = undo_read_4c(bi); 1089 1090 char **array = NULL; 1091 if (uep->ue_size > 0) { 1092 if ((size_t)uep->ue_size < SIZE_MAX / sizeof(char *)) { 1093 array = xmalloc(sizeof(char *) * (size_t)uep->ue_size); 1094 memset(array, 0, sizeof(char *) * (size_t)uep->ue_size); 1095 } 1096 } 1097 uep->ue_array = array; 1098 1099 for (size_t i = 0; i < (size_t)uep->ue_size; i++) { 1100 int line_len = undo_read_4c(bi); 1101 char *line; 1102 if (line_len >= 0) { 1103 line = undo_read_string(bi, (size_t)line_len); 1104 } else { 1105 line = NULL; 1106 corruption_error("line length", file_name); 1107 } 1108 if (line == NULL) { 1109 *error = true; 1110 return uep; 1111 } 1112 array[i] = line; 1113 } 1114 return uep; 1115 } 1116 1117 /// Serializes "pos". 1118 static void serialize_pos(bufinfo_T *bi, pos_T pos) 1119 { 1120 undo_write_bytes(bi, (uintmax_t)pos.lnum, 4); 1121 undo_write_bytes(bi, (uintmax_t)pos.col, 4); 1122 undo_write_bytes(bi, (uintmax_t)pos.coladd, 4); 1123 } 1124 1125 /// Unserializes the pos_T at the current position. 1126 static void unserialize_pos(bufinfo_T *bi, pos_T *pos) 1127 { 1128 pos->lnum = undo_read_4c(bi); 1129 pos->lnum = MAX(pos->lnum, 0); 1130 pos->col = undo_read_4c(bi); 1131 pos->col = MAX(pos->col, 0); 1132 pos->coladd = undo_read_4c(bi); 1133 pos->coladd = MAX(pos->coladd, 0); 1134 } 1135 1136 /// Serializes "info". 1137 static void serialize_visualinfo(bufinfo_T *bi, visualinfo_T *info) 1138 { 1139 serialize_pos(bi, info->vi_start); 1140 serialize_pos(bi, info->vi_end); 1141 undo_write_bytes(bi, (uintmax_t)info->vi_mode, 4); 1142 undo_write_bytes(bi, (uintmax_t)info->vi_curswant, 4); 1143 } 1144 1145 /// Unserializes the visualinfo_T at the current position. 1146 static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info) 1147 { 1148 unserialize_pos(bi, &info->vi_start); 1149 unserialize_pos(bi, &info->vi_end); 1150 info->vi_mode = undo_read_4c(bi); 1151 info->vi_curswant = undo_read_4c(bi); 1152 } 1153 1154 /// Write the undo tree in an undo file. 1155 /// 1156 /// @param[in] name Name of the undo file or NULL if this function needs to 1157 /// generate the undo file name based on buf->b_ffname. 1158 /// @param[in] forceit True for `:wundo!`, false otherwise. 1159 /// @param[in] buf Buffer for which undo file is written. 1160 /// @param[in] hash Hash value of the buffer text. Must have #UNDO_HASH_SIZE 1161 /// size. 1162 void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, uint8_t *const hash) 1163 FUNC_ATTR_NONNULL_ARG(3, 4) 1164 { 1165 char *file_name; 1166 #ifdef U_DEBUG 1167 int headers_written = 0; 1168 #endif 1169 FILE *fp = NULL; 1170 bool write_ok = false; 1171 1172 if (name == NULL) { 1173 file_name = u_get_undo_file_name(buf->b_ffname, false); 1174 if (file_name == NULL) { 1175 if (p_verbose > 0) { 1176 verbose_enter(); 1177 smsg(0, "%s", _("Cannot write undo file in any directory in 'undodir'")); 1178 verbose_leave(); 1179 } 1180 return; 1181 } 1182 } else { 1183 file_name = (char *)name; 1184 } 1185 1186 // Decide about the permission to use for the undo file. If the buffer 1187 // has a name use the permission of the original file. Otherwise only 1188 // allow the user to access the undo file. 1189 int perm = 0600; 1190 if (buf->b_ffname != NULL) { 1191 perm = os_getperm(buf->b_ffname); 1192 if (perm < 0) { 1193 perm = 0600; 1194 } 1195 } 1196 1197 // Strip any sticky and executable bits. 1198 perm = perm & 0666; 1199 1200 // If the undo file already exists, verify that it actually is an undo 1201 // file, and delete it. 1202 if (os_path_exists(file_name)) { 1203 if (name == NULL || !forceit) { 1204 // Check we can read it and it's an undo file. 1205 int fd = os_open(file_name, O_RDONLY, 0); 1206 if (fd < 0) { 1207 if (name != NULL || p_verbose > 0) { 1208 if (name == NULL) { 1209 verbose_enter(); 1210 } 1211 smsg(0, _("Will not overwrite with undo file, cannot read: %s"), 1212 file_name); 1213 if (name == NULL) { 1214 verbose_leave(); 1215 } 1216 } 1217 goto theend; 1218 } else { 1219 char mbuf[UF_START_MAGIC_LEN]; 1220 ssize_t len = read_eintr(fd, mbuf, UF_START_MAGIC_LEN); 1221 close(fd); 1222 if (len < UF_START_MAGIC_LEN 1223 || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0) { 1224 if (name != NULL || p_verbose > 0) { 1225 if (name == NULL) { 1226 verbose_enter(); 1227 } 1228 smsg(0, _("Will not overwrite, this is not an undo file: %s"), 1229 file_name); 1230 if (name == NULL) { 1231 verbose_leave(); 1232 } 1233 } 1234 goto theend; 1235 } 1236 } 1237 } 1238 os_remove(file_name); 1239 } 1240 1241 // If there is no undo information at all, quit here after deleting any 1242 // existing undo file. 1243 if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL) { 1244 if (p_verbose > 0) { 1245 verb_msg(_("Skipping undo file write, nothing to undo")); 1246 } 1247 goto theend; 1248 } 1249 1250 int fd = os_open(file_name, O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm); 1251 if (fd < 0) { 1252 semsg(_(e_not_open), file_name); 1253 goto theend; 1254 } 1255 os_setperm(file_name, perm); 1256 if (p_verbose > 0) { 1257 verbose_enter(); 1258 smsg(0, _("Writing undo file: %s"), file_name); 1259 verbose_leave(); 1260 } 1261 1262 #ifdef U_DEBUG 1263 // Check there is no problem in undo info before writing. 1264 u_check(false); 1265 #endif 1266 1267 #ifdef UNIX 1268 // Try to set the group of the undo file same as the original file. If 1269 // this fails, set the protection bits for the group same as the 1270 // protection bits for others. 1271 FileInfo file_info_old; 1272 FileInfo file_info_new; 1273 if (buf->b_ffname != NULL 1274 && os_fileinfo(buf->b_ffname, &file_info_old) 1275 && os_fileinfo(file_name, &file_info_new) 1276 && file_info_old.stat.st_gid != file_info_new.stat.st_gid 1277 && os_fchown(fd, (uv_uid_t)-1, (uv_gid_t)file_info_old.stat.st_gid)) { 1278 os_setperm(file_name, (perm & 0707) | ((perm & 07) << 3)); 1279 } 1280 #endif 1281 1282 fp = fdopen(fd, "w"); 1283 if (fp == NULL) { 1284 semsg(_(e_not_open), file_name); 1285 close(fd); 1286 os_remove(file_name); 1287 goto theend; 1288 } 1289 1290 // Undo must be synced. 1291 u_sync(true); 1292 1293 // Write the header. 1294 bufinfo_T bi = { 1295 .bi_buf = buf, 1296 .bi_fp = fp, 1297 }; 1298 if (!serialize_header(&bi, hash)) { 1299 goto write_error; 1300 } 1301 1302 // Iteratively serialize UHPs and their UEPs from the top down. 1303 int mark = ++lastmark; 1304 u_header_T *uhp = buf->b_u_oldhead; 1305 while (uhp != NULL) { 1306 // Serialize current UHP if we haven't seen it 1307 if (uhp->uh_walk != mark) { 1308 uhp->uh_walk = mark; 1309 #ifdef U_DEBUG 1310 headers_written++; 1311 #endif 1312 if (!serialize_uhp(&bi, uhp)) { 1313 goto write_error; 1314 } 1315 } 1316 1317 // Now walk through the tree - algorithm from undo_time(). 1318 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark) { 1319 uhp = uhp->uh_prev.ptr; 1320 } else if (uhp->uh_alt_next.ptr != NULL 1321 && uhp->uh_alt_next.ptr->uh_walk != mark) { 1322 uhp = uhp->uh_alt_next.ptr; 1323 } else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL 1324 && uhp->uh_next.ptr->uh_walk != mark) { 1325 uhp = uhp->uh_next.ptr; 1326 } else if (uhp->uh_alt_prev.ptr != NULL) { 1327 uhp = uhp->uh_alt_prev.ptr; 1328 } else { 1329 uhp = uhp->uh_next.ptr; 1330 } 1331 } 1332 1333 if (undo_write_bytes(&bi, (uintmax_t)UF_HEADER_END_MAGIC, 2)) { 1334 write_ok = true; 1335 } 1336 #ifdef U_DEBUG 1337 if (headers_written != buf->b_u_numhead) { 1338 semsg("Written %" PRId64 " headers, ...", (int64_t)headers_written); 1339 semsg("... but numhead is %" PRId64, (int64_t)buf->b_u_numhead); 1340 } 1341 #endif 1342 1343 if ((buf->b_p_fs >= 0 ? buf->b_p_fs : p_fs) && fflush(fp) == 0 1344 && os_fsync(fd) != 0) { 1345 write_ok = false; 1346 } 1347 1348 write_error: 1349 fclose(fp); 1350 if (!write_ok) { 1351 semsg(_(e_write_error_in_undo_file_str), file_name); 1352 } 1353 1354 if (buf->b_ffname != NULL) { 1355 // For systems that support ACL: get the ACL from the original file. 1356 vim_acl_T acl = os_get_acl(buf->b_ffname); 1357 os_set_acl(file_name, acl); 1358 os_free_acl(acl); 1359 } 1360 1361 theend: 1362 if (file_name != name) { 1363 xfree(file_name); 1364 } 1365 } 1366 1367 /// Loads the undo tree from an undo file. 1368 /// If "name" is not NULL use it as the undo file name. This also means being 1369 /// a bit more verbose. 1370 /// Otherwise use curbuf->b_ffname to generate the undo file name. 1371 /// "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text. 1372 void u_read_undo(char *name, const uint8_t *hash, const char *orig_name FUNC_ATTR_UNUSED) 1373 FUNC_ATTR_NONNULL_ARG(2) 1374 { 1375 u_header_T **uhp_table = NULL; 1376 char *line_ptr = NULL; 1377 1378 char *file_name; 1379 if (name == NULL) { 1380 file_name = u_get_undo_file_name(curbuf->b_ffname, true); 1381 if (file_name == NULL) { 1382 return; 1383 } 1384 1385 #ifdef UNIX 1386 // For safety we only read an undo file if the owner is equal to the 1387 // owner of the text file or equal to the current user. 1388 FileInfo file_info_orig; 1389 FileInfo file_info_undo; 1390 if (os_fileinfo(orig_name, &file_info_orig) 1391 && os_fileinfo(file_name, &file_info_undo) 1392 && file_info_orig.stat.st_uid != file_info_undo.stat.st_uid 1393 && file_info_undo.stat.st_uid != getuid()) { 1394 if (p_verbose > 0) { 1395 verbose_enter(); 1396 smsg(0, _("Not reading undo file, owner differs: %s"), 1397 file_name); 1398 verbose_leave(); 1399 } 1400 return; 1401 } 1402 #endif 1403 } else { 1404 file_name = name; 1405 } 1406 1407 if (p_verbose > 0) { 1408 verbose_enter(); 1409 smsg(0, _("Reading undo file: %s"), file_name); 1410 verbose_leave(); 1411 } 1412 1413 FILE *fp = os_fopen(file_name, "r"); 1414 if (fp == NULL) { 1415 if (name != NULL || p_verbose > 0) { 1416 semsg(_("E822: Cannot open undo file for reading: %s"), file_name); 1417 } 1418 goto error; 1419 } 1420 1421 bufinfo_T bi = { 1422 .bi_buf = curbuf, 1423 .bi_fp = fp, 1424 }; 1425 1426 // Read the undo file header. 1427 char magic_buf[UF_START_MAGIC_LEN]; 1428 if (fread(magic_buf, UF_START_MAGIC_LEN, 1, fp) != 1 1429 || memcmp(magic_buf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0) { 1430 semsg(_("E823: Not an undo file: %s"), file_name); 1431 goto error; 1432 } 1433 int version = get2c(fp); 1434 if (version != UF_VERSION) { 1435 semsg(_("E824: Incompatible undo file: %s"), file_name); 1436 goto error; 1437 } 1438 1439 uint8_t read_hash[UNDO_HASH_SIZE]; 1440 if (!undo_read(&bi, read_hash, UNDO_HASH_SIZE)) { 1441 corruption_error("hash", file_name); 1442 goto error; 1443 } 1444 linenr_T line_count = (linenr_T)undo_read_4c(&bi); 1445 if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0 1446 || line_count != curbuf->b_ml.ml_line_count) { 1447 if (p_verbose > 0 || name != NULL) { 1448 if (name == NULL) { 1449 verbose_enter(); 1450 } 1451 give_warning(_("File contents changed, cannot use undo info"), true, true); 1452 if (name == NULL) { 1453 verbose_leave(); 1454 } 1455 } 1456 goto error; 1457 } 1458 1459 // Read undo data for "U" command. 1460 int str_len = undo_read_4c(&bi); 1461 if (str_len < 0) { 1462 goto error; 1463 } 1464 1465 if (str_len > 0) { 1466 line_ptr = undo_read_string(&bi, (size_t)str_len); 1467 } 1468 linenr_T line_lnum = (linenr_T)undo_read_4c(&bi); 1469 colnr_T line_colnr = (colnr_T)undo_read_4c(&bi); 1470 if (line_lnum < 0 || line_colnr < 0) { 1471 corruption_error("line lnum/col", file_name); 1472 goto error; 1473 } 1474 1475 // Begin general undo data 1476 int old_header_seq = undo_read_4c(&bi); 1477 int new_header_seq = undo_read_4c(&bi); 1478 int cur_header_seq = undo_read_4c(&bi); 1479 int num_head = undo_read_4c(&bi); 1480 int seq_last = undo_read_4c(&bi); 1481 int seq_cur = undo_read_4c(&bi); 1482 time_t seq_time = undo_read_time(&bi); 1483 1484 // Optional header fields. 1485 int last_save_nr = 0; 1486 while (true) { 1487 int len = undo_read_byte(&bi); 1488 1489 if (len == 0 || len == EOF) { 1490 break; 1491 } 1492 int what = undo_read_byte(&bi); 1493 switch (what) { 1494 case UF_LAST_SAVE_NR: 1495 last_save_nr = undo_read_4c(&bi); 1496 break; 1497 1498 default: 1499 // field not supported, skip 1500 while (--len >= 0) { 1501 undo_read_byte(&bi); 1502 } 1503 } 1504 } 1505 1506 // uhp_table will store the freshly created undo headers we allocate 1507 // until we insert them into curbuf. The table remains sorted by the 1508 // sequence numbers of the headers. 1509 // When there are no headers uhp_table is NULL. 1510 if (num_head > 0) { 1511 if ((size_t)num_head < SIZE_MAX / sizeof(*uhp_table)) { 1512 uhp_table = xmalloc((size_t)num_head * sizeof(*uhp_table)); 1513 } 1514 } 1515 1516 int num_read_uhps = 0; 1517 1518 int c; 1519 while ((c = undo_read_2c(&bi)) == UF_HEADER_MAGIC) { 1520 if (num_read_uhps >= num_head) { 1521 corruption_error("num_head too small", file_name); 1522 goto error; 1523 } 1524 1525 u_header_T *uhp = unserialize_uhp(&bi, file_name); 1526 if (uhp == NULL) { 1527 goto error; 1528 } 1529 uhp_table[num_read_uhps++] = uhp; 1530 } 1531 1532 if (num_read_uhps != num_head) { 1533 corruption_error("num_head", file_name); 1534 goto error; 1535 } 1536 if (c != UF_HEADER_END_MAGIC) { 1537 corruption_error("end marker", file_name); 1538 goto error; 1539 } 1540 1541 #ifdef U_DEBUG 1542 size_t amount = num_head * sizeof(int) + 1; 1543 int *uhp_table_used = xmalloc(amount); 1544 memset(uhp_table_used, 0, amount); 1545 # define SET_FLAG(j) ++uhp_table_used[j] 1546 #else 1547 # define SET_FLAG(j) 1548 #endif 1549 1550 // We have put all of the headers into a table. Now we iterate through the 1551 // table and swizzle each sequence number we have stored in uh_*_seq into 1552 // a pointer corresponding to the header with that sequence number. 1553 int16_t old_idx = -1; 1554 int16_t new_idx = -1; 1555 int16_t cur_idx = -1; 1556 for (int i = 0; i < num_head; i++) { 1557 u_header_T *uhp = uhp_table[i]; 1558 if (uhp == NULL) { 1559 continue; 1560 } 1561 for (int j = 0; j < num_head; j++) { 1562 if (uhp_table[j] != NULL && i != j 1563 && uhp_table[i]->uh_seq == uhp_table[j]->uh_seq) { 1564 corruption_error("duplicate uh_seq", file_name); 1565 goto error; 1566 } 1567 } 1568 for (int j = 0; j < num_head; j++) { 1569 if (uhp_table[j] != NULL 1570 && uhp_table[j]->uh_seq == uhp->uh_next.seq) { 1571 uhp->uh_next.ptr = uhp_table[j]; 1572 SET_FLAG(j); 1573 break; 1574 } 1575 } 1576 for (int j = 0; j < num_head; j++) { 1577 if (uhp_table[j] != NULL 1578 && uhp_table[j]->uh_seq == uhp->uh_prev.seq) { 1579 uhp->uh_prev.ptr = uhp_table[j]; 1580 SET_FLAG(j); 1581 break; 1582 } 1583 } 1584 for (int j = 0; j < num_head; j++) { 1585 if (uhp_table[j] != NULL 1586 && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq) { 1587 uhp->uh_alt_next.ptr = uhp_table[j]; 1588 SET_FLAG(j); 1589 break; 1590 } 1591 } 1592 for (int j = 0; j < num_head; j++) { 1593 if (uhp_table[j] != NULL 1594 && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq) { 1595 uhp->uh_alt_prev.ptr = uhp_table[j]; 1596 SET_FLAG(j); 1597 break; 1598 } 1599 } 1600 if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq) { 1601 assert(i <= INT16_MAX); 1602 old_idx = (int16_t)i; 1603 SET_FLAG(i); 1604 } 1605 if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq) { 1606 assert(i <= INT16_MAX); 1607 new_idx = (int16_t)i; 1608 SET_FLAG(i); 1609 } 1610 if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq) { 1611 assert(i <= INT16_MAX); 1612 cur_idx = (int16_t)i; 1613 SET_FLAG(i); 1614 } 1615 } 1616 1617 // Now that we have read the undo info successfully, free the current undo 1618 // info and use the info from the file. 1619 u_blockfree(curbuf); 1620 curbuf->b_u_oldhead = old_idx < 0 ? NULL : uhp_table[old_idx]; 1621 curbuf->b_u_newhead = new_idx < 0 ? NULL : uhp_table[new_idx]; 1622 curbuf->b_u_curhead = cur_idx < 0 ? NULL : uhp_table[cur_idx]; 1623 curbuf->b_u_line_ptr = line_ptr; 1624 curbuf->b_u_line_lnum = line_lnum; 1625 curbuf->b_u_line_colnr = line_colnr; 1626 curbuf->b_u_numhead = num_head; 1627 curbuf->b_u_seq_last = seq_last; 1628 curbuf->b_u_seq_cur = seq_cur; 1629 curbuf->b_u_time_cur = seq_time; 1630 curbuf->b_u_save_nr_last = last_save_nr; 1631 curbuf->b_u_save_nr_cur = last_save_nr; 1632 1633 curbuf->b_u_synced = true; 1634 xfree(uhp_table); 1635 1636 #ifdef U_DEBUG 1637 for (int i = 0; i < num_head; i++) { 1638 if (uhp_table_used[i] == 0) { 1639 semsg("uhp_table entry %" PRId64 " not used, leaking memory", (int64_t)i); 1640 } 1641 } 1642 xfree(uhp_table_used); 1643 u_check(true); 1644 #endif 1645 1646 if (name != NULL) { 1647 smsg(0, _("Finished reading undo file %s"), file_name); 1648 } 1649 goto theend; 1650 1651 error: 1652 xfree(line_ptr); 1653 if (uhp_table != NULL) { 1654 for (int i = 0; i < num_read_uhps; i++) { 1655 if (uhp_table[i] != NULL) { 1656 u_free_uhp(uhp_table[i]); 1657 } 1658 } 1659 xfree(uhp_table); 1660 } 1661 1662 theend: 1663 if (fp != NULL) { 1664 fclose(fp); 1665 } 1666 if (file_name != name) { 1667 xfree(file_name); 1668 } 1669 } 1670 1671 /// Writes a sequence of bytes to the undo file. 1672 /// 1673 /// @param bi The buffer info 1674 /// @param ptr The byte buffer to write 1675 /// @param len The number of bytes to write 1676 /// 1677 /// @returns false in case of an error. 1678 static bool undo_write(bufinfo_T *bi, uint8_t *ptr, size_t len) 1679 FUNC_ATTR_NONNULL_ARG(1) 1680 { 1681 return fwrite(ptr, len, 1, bi->bi_fp) == 1; 1682 } 1683 1684 /// Writes a number, most significant bit first, in "len" bytes. 1685 /// 1686 /// Must match with undo_read_?c() functions. 1687 /// 1688 /// @param bi The buffer info 1689 /// @param nr The number to write 1690 /// @param len The number of bytes to use when writing the number. 1691 /// 1692 /// @returns false in case of an error. 1693 static bool undo_write_bytes(bufinfo_T *bi, uintmax_t nr, size_t len) 1694 { 1695 assert(len > 0); 1696 uint8_t buf[8]; 1697 for (size_t i = len - 1, bufi = 0; bufi < len; i--, bufi++) { 1698 buf[bufi] = (uint8_t)(nr >> (i * 8)); 1699 } 1700 return undo_write(bi, buf, len); 1701 } 1702 1703 /// Writes the pointer to an undo header. 1704 /// 1705 /// Instead of writing the pointer itself, we use the sequence 1706 /// number of the header. This is converted back to pointers 1707 /// when reading. 1708 static void put_header_ptr(bufinfo_T *bi, u_header_T *uhp) 1709 { 1710 assert(uhp == NULL || uhp->uh_seq >= 0); 1711 undo_write_bytes(bi, (uint64_t)(uhp != NULL ? uhp->uh_seq : 0), 4); 1712 } 1713 1714 static int undo_read_4c(bufinfo_T *bi) 1715 { 1716 return get4c(bi->bi_fp); 1717 } 1718 1719 static int undo_read_2c(bufinfo_T *bi) 1720 { 1721 return get2c(bi->bi_fp); 1722 } 1723 1724 static int undo_read_byte(bufinfo_T *bi) 1725 { 1726 return getc(bi->bi_fp); 1727 } 1728 1729 static time_t undo_read_time(bufinfo_T *bi) 1730 { 1731 return get8ctime(bi->bi_fp); 1732 } 1733 1734 /// Reads "buffer[size]" from the undo file. 1735 /// 1736 /// @param bi The buffer info 1737 /// @param buffer Character buffer to read data into 1738 /// @param size The size of the character buffer 1739 /// 1740 /// @returns false in case of an error. 1741 static bool undo_read(bufinfo_T *bi, uint8_t *buffer, size_t size) 1742 FUNC_ATTR_NONNULL_ARG(1) 1743 { 1744 const bool retval = fread(buffer, size, 1, bi->bi_fp) == 1; 1745 if (!retval) { 1746 // Error may be checked for only later. Fill with zeros, 1747 // so that the reader won't use garbage. 1748 memset(buffer, 0, size); 1749 } 1750 return retval; 1751 } 1752 1753 /// Reads a string of length "len" from "bi->bi_fd" and appends a zero to it. 1754 /// 1755 /// @param len can be zero to allocate an empty line. 1756 /// 1757 /// @returns a pointer to allocated memory or NULL in case of an error. 1758 static char *undo_read_string(bufinfo_T *bi, size_t len) 1759 { 1760 char *ptr = xmallocz(len); 1761 if (len > 0 && !undo_read(bi, (uint8_t *)ptr, len)) { 1762 xfree(ptr); 1763 return NULL; 1764 } 1765 return ptr; 1766 } 1767 1768 /// If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible). 1769 /// If 'cpoptions' does not contain 'u': Always undo. 1770 void u_undo(int count) 1771 { 1772 // If we get an undo command while executing a macro, we behave like the 1773 // original vi. If this happens twice in one macro the result will not 1774 // be compatible. 1775 if (curbuf->b_u_synced == false) { 1776 u_sync(true); 1777 count = 1; 1778 } 1779 1780 if (vim_strchr(p_cpo, CPO_UNDO) == NULL) { 1781 undo_undoes = true; 1782 } else { 1783 undo_undoes = !undo_undoes; 1784 } 1785 u_doit(count, false, true); 1786 } 1787 1788 /// If 'cpoptions' contains 'u': Repeat the previous undo or redo. 1789 /// If 'cpoptions' does not contain 'u': Always redo. 1790 void u_redo(int count) 1791 { 1792 if (vim_strchr(p_cpo, CPO_UNDO) == NULL) { 1793 undo_undoes = false; 1794 } 1795 1796 u_doit(count, false, true); 1797 } 1798 1799 /// Undo and remove the branch from the undo tree. 1800 /// Also moves the cursor (as a "normal" undo would). 1801 /// 1802 /// @param do_buf_event If `true`, send the changedtick with the buffer updates 1803 bool u_undo_and_forget(int count, bool do_buf_event) 1804 { 1805 if (curbuf->b_u_synced == false) { 1806 u_sync(true); 1807 count = 1; 1808 } 1809 undo_undoes = true; 1810 u_doit(count, true, do_buf_event); 1811 1812 if (curbuf->b_u_curhead == NULL) { 1813 // nothing was undone. 1814 return false; 1815 } 1816 1817 // Delete the current redo header 1818 // set the redo header to the next alternative branch (if any) 1819 // otherwise we will be in the leaf state 1820 u_header_T *to_forget = curbuf->b_u_curhead; 1821 curbuf->b_u_newhead = to_forget->uh_next.ptr; 1822 curbuf->b_u_curhead = to_forget->uh_alt_next.ptr; 1823 if (curbuf->b_u_curhead) { 1824 to_forget->uh_alt_next.ptr = NULL; 1825 curbuf->b_u_curhead->uh_alt_prev.ptr = to_forget->uh_alt_prev.ptr; 1826 curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_next.ptr 1827 ? curbuf->b_u_curhead->uh_next.ptr->uh_seq : 0; 1828 } else if (curbuf->b_u_newhead) { 1829 curbuf->b_u_seq_cur = curbuf->b_u_newhead->uh_seq; 1830 } 1831 if (to_forget->uh_alt_prev.ptr) { 1832 to_forget->uh_alt_prev.ptr->uh_alt_next.ptr = curbuf->b_u_curhead; 1833 } 1834 if (curbuf->b_u_newhead) { 1835 curbuf->b_u_newhead->uh_prev.ptr = curbuf->b_u_curhead; 1836 } 1837 if (curbuf->b_u_seq_last == to_forget->uh_seq) { 1838 curbuf->b_u_seq_last--; 1839 } 1840 u_freebranch(curbuf, to_forget, NULL); 1841 return true; 1842 } 1843 1844 /// Undo or redo, depending on `undo_undoes`, `count` times. 1845 /// 1846 /// @param startcount How often to undo or redo 1847 /// @param quiet If `true`, don't show messages 1848 /// @param do_buf_event If `true`, send the changedtick with the buffer updates 1849 static void u_doit(int startcount, bool quiet, bool do_buf_event) 1850 { 1851 if (!undo_allowed(curbuf)) { 1852 return; 1853 } 1854 1855 u_newcount = 0; 1856 u_oldcount = 0; 1857 if (curbuf->b_ml.ml_flags & ML_EMPTY) { 1858 u_oldcount = -1; 1859 } 1860 1861 msg_ext_set_kind("undo"); 1862 int count = startcount; 1863 while (count--) { 1864 // Do the change warning now, so that it triggers FileChangedRO when 1865 // needed. This may cause the file to be reloaded, that must happen 1866 // before we do anything, because it may change curbuf->b_u_curhead 1867 // and more. 1868 change_warning(curbuf, 0); 1869 1870 if (undo_undoes) { 1871 if (curbuf->b_u_curhead == NULL) { // first undo 1872 curbuf->b_u_curhead = curbuf->b_u_newhead; 1873 } else if (get_undolevel(curbuf) > 0) { // multi level undo 1874 // get next undo 1875 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next.ptr; 1876 } 1877 // nothing to undo 1878 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL) { 1879 // stick curbuf->b_u_curhead at end 1880 curbuf->b_u_curhead = curbuf->b_u_oldhead; 1881 beep_flush(); 1882 if (count == startcount - 1) { 1883 msg(_("Already at oldest change"), 0); 1884 return; 1885 } 1886 break; 1887 } 1888 1889 u_undoredo(true, do_buf_event); 1890 } else { 1891 if (curbuf->b_u_curhead == NULL || get_undolevel(curbuf) <= 0) { 1892 beep_flush(); // nothing to redo 1893 if (count == startcount - 1) { 1894 msg(_("Already at newest change"), 0); 1895 return; 1896 } 1897 break; 1898 } 1899 1900 u_undoredo(false, do_buf_event); 1901 1902 // Advance for next redo. Set "newhead" when at the end of the 1903 // redoable changes. 1904 if (curbuf->b_u_curhead->uh_prev.ptr == NULL) { 1905 curbuf->b_u_newhead = curbuf->b_u_curhead; 1906 } 1907 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr; 1908 } 1909 } 1910 u_undo_end(undo_undoes, false, quiet); 1911 } 1912 1913 // Undo or redo over the timeline. 1914 // When "step" is negative go back in time, otherwise goes forward in time. 1915 // When "sec" is false make "step" steps, when "sec" is true use "step" as 1916 // seconds. 1917 // When "file" is true use "step" as a number of file writes. 1918 // When "absolute" is true use "step" as the sequence number to jump to. 1919 // "sec" must be false then. 1920 void undo_time(int step, bool sec, bool file, bool absolute) 1921 { 1922 if (text_locked()) { 1923 text_locked_msg(); 1924 return; 1925 } 1926 1927 // First make sure the current undoable change is synced. 1928 if (curbuf->b_u_synced == false) { 1929 u_sync(true); 1930 } 1931 1932 u_newcount = 0; 1933 u_oldcount = 0; 1934 if (curbuf->b_ml.ml_flags & ML_EMPTY) { 1935 u_oldcount = -1; 1936 } 1937 1938 int target; 1939 int closest; 1940 u_header_T *uhp = NULL; 1941 bool dosec = sec; 1942 bool dofile = file; 1943 bool above = false; 1944 bool did_undo = true; 1945 1946 // "target" is the node below which we want to be. 1947 // Init "closest" to a value we can't reach. 1948 if (absolute) { 1949 target = step; 1950 closest = -1; 1951 } else { 1952 if (dosec) { 1953 target = (int)curbuf->b_u_time_cur + step; 1954 } else if (dofile) { 1955 if (step < 0) { 1956 // Going back to a previous write. If there were changes after 1957 // the last write, count that as moving one file-write, so 1958 // that ":earlier 1f" undoes all changes since the last save. 1959 uhp = curbuf->b_u_curhead; 1960 if (uhp != NULL) { 1961 uhp = uhp->uh_next.ptr; 1962 } else { 1963 uhp = curbuf->b_u_newhead; 1964 } 1965 if (uhp != NULL && uhp->uh_save_nr != 0) { 1966 // "uh_save_nr" was set in the last block, that means 1967 // there were no changes since the last write 1968 target = curbuf->b_u_save_nr_cur + step; 1969 } else { 1970 // count the changes since the last write as one step 1971 target = curbuf->b_u_save_nr_cur + step + 1; 1972 } 1973 if (target <= 0) { 1974 // Go to before first write: before the oldest change. Use 1975 // the sequence number for that. 1976 dofile = false; 1977 } 1978 } else { 1979 // Moving forward to a newer write. 1980 target = curbuf->b_u_save_nr_cur + step; 1981 if (target > curbuf->b_u_save_nr_last) { 1982 // Go to after last write: after the latest change. Use 1983 // the sequence number for that. 1984 target = curbuf->b_u_seq_last + 1; 1985 dofile = false; 1986 } 1987 } 1988 } else { 1989 target = curbuf->b_u_seq_cur + step; 1990 } 1991 if (step < 0) { 1992 target = MAX(target, 0); 1993 closest = -1; 1994 } else { 1995 if (dosec) { 1996 closest = (int)(os_time() + 1); 1997 } else if (dofile) { 1998 closest = curbuf->b_u_save_nr_last + 2; 1999 } else { 2000 closest = curbuf->b_u_seq_last + 2; 2001 } 2002 if (target >= closest) { 2003 target = closest - 1; 2004 } 2005 } 2006 } 2007 int closest_start = closest; 2008 int closest_seq = curbuf->b_u_seq_cur; 2009 int mark; 2010 int nomark = 0; // shut up compiler 2011 2012 // When "target" is 0; Back to origin. 2013 if (target == 0) { 2014 mark = lastmark; // avoid that GCC complains 2015 goto target_zero; 2016 } 2017 2018 // May do this twice: 2019 // 1. Search for "target", update "closest" to the best match found. 2020 // 2. If "target" not found search for "closest". 2021 // 2022 // When using the closest time we use the sequence number in the second 2023 // round, because there may be several entries with the same time. 2024 for (int round = 1; round <= 2; round++) { 2025 // Find the path from the current state to where we want to go. The 2026 // desired state can be anywhere in the undo tree, need to go all over 2027 // it. We put "nomark" in uh_walk where we have been without success, 2028 // "mark" where it could possibly be. 2029 mark = ++lastmark; 2030 nomark = ++lastmark; 2031 2032 if (curbuf->b_u_curhead == NULL) { // at leaf of the tree 2033 uhp = curbuf->b_u_newhead; 2034 } else { 2035 uhp = curbuf->b_u_curhead; 2036 } 2037 2038 while (uhp != NULL) { 2039 uhp->uh_walk = mark; 2040 int val = dosec ? (int)(uhp->uh_time) 2041 : dofile ? uhp->uh_save_nr 2042 : uhp->uh_seq; 2043 2044 if (round == 1 && !(dofile && val == 0)) { 2045 // Remember the header that is closest to the target. 2046 // It must be at least in the right direction (checked with 2047 // "b_u_seq_cur"). When the timestamp is equal find the 2048 // highest/lowest sequence number. 2049 if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur 2050 : uhp->uh_seq > curbuf->b_u_seq_cur) 2051 && ((dosec && val == closest) 2052 ? (step < 0 2053 ? uhp->uh_seq < closest_seq 2054 : uhp->uh_seq > closest_seq) 2055 : closest == closest_start 2056 || (val > target 2057 ? (closest > target 2058 ? val - target <= closest - target 2059 : val - target <= target - closest) 2060 : (closest > target 2061 ? target - val <= closest - target 2062 : target - val <= target - closest)))) { 2063 closest = val; 2064 closest_seq = uhp->uh_seq; 2065 } 2066 } 2067 2068 // Quit searching when we found a match. But when searching for a 2069 // time we need to continue looking for the best uh_seq. 2070 if (target == val && !dosec) { 2071 target = uhp->uh_seq; 2072 break; 2073 } 2074 2075 // go down in the tree if we haven't been there 2076 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark 2077 && uhp->uh_prev.ptr->uh_walk != mark) { 2078 uhp = uhp->uh_prev.ptr; 2079 } else if (uhp->uh_alt_next.ptr != NULL 2080 && uhp->uh_alt_next.ptr->uh_walk != nomark 2081 && uhp->uh_alt_next.ptr->uh_walk != mark) { 2082 // go to alternate branch if we haven't been there 2083 uhp = uhp->uh_alt_next.ptr; 2084 } else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL 2085 // go up in the tree if we haven't been there and we are at the 2086 // start of alternate branches 2087 && uhp->uh_next.ptr->uh_walk != nomark 2088 && uhp->uh_next.ptr->uh_walk != mark) { 2089 // If still at the start we don't go through this change. 2090 if (uhp == curbuf->b_u_curhead) { 2091 uhp->uh_walk = nomark; 2092 } 2093 uhp = uhp->uh_next.ptr; 2094 } else { 2095 // need to backtrack; mark this node as useless 2096 uhp->uh_walk = nomark; 2097 if (uhp->uh_alt_prev.ptr != NULL) { 2098 uhp = uhp->uh_alt_prev.ptr; 2099 } else { 2100 uhp = uhp->uh_next.ptr; 2101 } 2102 } 2103 } 2104 2105 if (uhp != NULL) { // found it 2106 break; 2107 } 2108 2109 if (absolute) { 2110 semsg(_("E830: Undo number %" PRId64 " not found"), (int64_t)step); 2111 return; 2112 } 2113 2114 if (closest == closest_start) { 2115 if (step < 0) { 2116 msg(_("Already at oldest change"), 0); 2117 } else { 2118 msg(_("Already at newest change"), 0); 2119 } 2120 return; 2121 } 2122 2123 target = closest_seq; 2124 dosec = false; 2125 dofile = false; 2126 if (step < 0) { 2127 above = true; // stop above the header 2128 } 2129 } 2130 2131 target_zero: 2132 // If we found it: Follow the path to go to where we want to be. 2133 if (uhp != NULL || target == 0) { 2134 // First go up the tree as much as needed. 2135 while (!got_int) { 2136 // Do the change warning now, for the same reason as above. 2137 change_warning(curbuf, 0); 2138 2139 uhp = curbuf->b_u_curhead; 2140 if (uhp == NULL) { 2141 uhp = curbuf->b_u_newhead; 2142 } else { 2143 uhp = uhp->uh_next.ptr; 2144 } 2145 if (uhp == NULL 2146 || (target > 0 && uhp->uh_walk != mark) 2147 || (uhp->uh_seq == target && !above)) { 2148 break; 2149 } 2150 curbuf->b_u_curhead = uhp; 2151 u_undoredo(true, true); 2152 if (target > 0) { 2153 uhp->uh_walk = nomark; // don't go back down here 2154 } 2155 } 2156 2157 // When back to origin, redo is not needed. 2158 if (target > 0) { 2159 // And now go down the tree (redo), branching off where needed. 2160 while (!got_int) { 2161 // Do the change warning now, for the same reason as above. 2162 change_warning(curbuf, 0); 2163 2164 uhp = curbuf->b_u_curhead; 2165 if (uhp == NULL) { 2166 break; 2167 } 2168 2169 // Go back to the first branch with a mark. 2170 while (uhp->uh_alt_prev.ptr != NULL 2171 && uhp->uh_alt_prev.ptr->uh_walk == mark) { 2172 uhp = uhp->uh_alt_prev.ptr; 2173 } 2174 2175 // Find the last branch with a mark, that's the one. 2176 u_header_T *last = uhp; 2177 while (last->uh_alt_next.ptr != NULL 2178 && last->uh_alt_next.ptr->uh_walk == mark) { 2179 last = last->uh_alt_next.ptr; 2180 } 2181 if (last != uhp) { 2182 // Make the used branch the first entry in the list of 2183 // alternatives to make "u" and CTRL-R take this branch. 2184 while (uhp->uh_alt_prev.ptr != NULL) { 2185 uhp = uhp->uh_alt_prev.ptr; 2186 } 2187 if (last->uh_alt_next.ptr != NULL) { 2188 last->uh_alt_next.ptr->uh_alt_prev.ptr = last->uh_alt_prev.ptr; 2189 } 2190 last->uh_alt_prev.ptr->uh_alt_next.ptr = last->uh_alt_next.ptr; 2191 last->uh_alt_prev.ptr = NULL; 2192 last->uh_alt_next.ptr = uhp; 2193 uhp->uh_alt_prev.ptr = last; 2194 2195 if (curbuf->b_u_oldhead == uhp) { 2196 curbuf->b_u_oldhead = last; 2197 } 2198 uhp = last; 2199 if (uhp->uh_next.ptr != NULL) { 2200 uhp->uh_next.ptr->uh_prev.ptr = uhp; 2201 } 2202 } 2203 curbuf->b_u_curhead = uhp; 2204 2205 if (uhp->uh_walk != mark) { 2206 break; // must have reached the target 2207 } 2208 2209 // Stop when going backwards in time and didn't find the exact 2210 // header we were looking for. 2211 if (uhp->uh_seq == target && above) { 2212 curbuf->b_u_seq_cur = target - 1; 2213 break; 2214 } 2215 2216 u_undoredo(false, true); 2217 2218 // Advance "curhead" to below the header we last used. If it 2219 // becomes NULL then we need to set "newhead" to this leaf. 2220 if (uhp->uh_prev.ptr == NULL) { 2221 curbuf->b_u_newhead = uhp; 2222 } 2223 curbuf->b_u_curhead = uhp->uh_prev.ptr; 2224 did_undo = false; 2225 2226 if (uhp->uh_seq == target) { // found it! 2227 break; 2228 } 2229 2230 uhp = uhp->uh_prev.ptr; 2231 if (uhp == NULL || uhp->uh_walk != mark) { 2232 // Need to redo more but can't find it... 2233 internal_error("undo_time()"); 2234 break; 2235 } 2236 } 2237 } 2238 } 2239 u_undo_end(did_undo, absolute, false); 2240 } 2241 2242 /// u_undoredo: common code for undo and redo 2243 /// 2244 /// The lines in the file are replaced by the lines in the entry list at 2245 /// curbuf->b_u_curhead. The replaced lines in the file are saved in the entry 2246 /// list for the next undo/redo. 2247 /// 2248 /// @param undo If `true`, go up the tree. Down if `false`. 2249 /// @param do_buf_event If `true`, send buffer updates. 2250 static void u_undoredo(bool undo, bool do_buf_event) 2251 { 2252 char **newarray = NULL; 2253 linenr_T newlnum = MAXLNUM; 2254 pos_T new_curpos = curwin->w_cursor; 2255 u_entry_T *nuep; 2256 u_entry_T *newlist = NULL; 2257 fmark_T namedm[NMARKS]; 2258 u_header_T *curhead = curbuf->b_u_curhead; 2259 2260 // Don't want autocommands using the undo structures here, they are 2261 // invalid till the end. 2262 block_autocmds(); 2263 2264 #ifdef U_DEBUG 2265 u_check(false); 2266 #endif 2267 int old_flags = curhead->uh_flags; 2268 int new_flags = (curbuf->b_changed ? UH_CHANGED : 0) 2269 | ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0) 2270 | (old_flags & UH_RELOAD); 2271 setpcmark(); 2272 2273 // save marks before undo/redo 2274 zero_fmark_additional_data(curbuf->b_namedm); 2275 memmove(namedm, curbuf->b_namedm, sizeof(curbuf->b_namedm[0]) * NMARKS); 2276 visualinfo_T visualinfo = curbuf->b_visual; 2277 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count; 2278 curbuf->b_op_start.col = 0; 2279 curbuf->b_op_end.lnum = 0; 2280 curbuf->b_op_end.col = 0; 2281 2282 for (u_entry_T *uep = curhead->uh_entry; uep != NULL; uep = nuep) { 2283 linenr_T top = uep->ue_top; 2284 linenr_T bot = uep->ue_bot; 2285 if (bot == 0) { 2286 bot = curbuf->b_ml.ml_line_count + 1; 2287 } 2288 if (top > curbuf->b_ml.ml_line_count || top >= bot 2289 || bot > curbuf->b_ml.ml_line_count + 1) { 2290 unblock_autocmds(); 2291 iemsg(_("E438: u_undo: line numbers wrong")); 2292 changed(curbuf); // don't want UNCHANGED now 2293 return; 2294 } 2295 2296 linenr_T oldsize = bot - top - 1; // number of lines before undo 2297 linenr_T newsize = uep->ue_size; // number of lines after undo 2298 2299 // Decide about the cursor position, depending on what text changed. 2300 // Don't set it yet, it may be invalid if lines are going to be added. 2301 { 2302 // If the saved cursor is somewhere in this undo block, move it to 2303 // the remembered position. Makes "gwap" put the cursor back 2304 // where it was. 2305 linenr_T lnum = curhead->uh_cursor.lnum; 2306 if (lnum >= top && lnum <= top + newsize + 1) { 2307 new_curpos = curhead->uh_cursor; 2308 // We don't want other entries to override saved cursor 2309 // position. 2310 newlnum = -1; 2311 } else if (top < newlnum) { 2312 // Use the first line that actually changed. Avoids that 2313 // undoing auto-formatting puts the cursor in the previous 2314 // line. 2315 int i; 2316 for (i = 0; i < newsize && i < oldsize; i++) { 2317 if (strcmp(uep->ue_array[i], ml_get(top + 1 + (linenr_T)i)) != 0) { 2318 break; 2319 } 2320 } 2321 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL) { 2322 newlnum = top; 2323 new_curpos.lnum = newlnum + 1; 2324 } else if (i < newsize) { 2325 newlnum = top + (linenr_T)i; 2326 new_curpos.lnum = newlnum + 1; 2327 } 2328 } 2329 } 2330 2331 bool empty_buffer = false; 2332 2333 // Delete the lines between top and bot and save them in newarray. 2334 if (oldsize > 0) { 2335 newarray = xmalloc(sizeof(char *) * (size_t)oldsize); 2336 // delete backwards, it goes faster in most cases 2337 int i; 2338 linenr_T lnum; 2339 for (lnum = bot - 1, i = oldsize; --i >= 0; lnum--) { 2340 // what can we do when we run out of memory? 2341 newarray[i] = u_save_line(lnum); 2342 // remember we deleted the last line in the buffer, and a 2343 // dummy empty line will be inserted 2344 if (curbuf->b_ml.ml_line_count == 1) { 2345 empty_buffer = true; 2346 } 2347 ml_delete(lnum); // ML_DEL_UNDO 2348 } 2349 } else { 2350 newarray = NULL; 2351 } 2352 2353 // make sure the cursor is on a valid line after the deletions 2354 check_cursor_lnum(curwin); 2355 2356 // Insert the lines in u_array between top and bot. 2357 if (newsize) { 2358 int i; 2359 linenr_T lnum; 2360 for (lnum = top, i = 0; i < newsize; i++, lnum++) { 2361 // If the file is empty, there is an empty line 1 that we 2362 // should get rid of, by replacing it with the new line 2363 if (empty_buffer && lnum == 0) { 2364 ml_replace(1, uep->ue_array[i], true); 2365 } else { 2366 ml_append_flags(lnum, uep->ue_array[i], 0, 0); // ML_APPEND_UNDO 2367 } 2368 xfree(uep->ue_array[i]); 2369 } 2370 xfree(uep->ue_array); 2371 } 2372 2373 // Adjust marks 2374 if (oldsize != newsize) { 2375 mark_adjust(top + 1, top + oldsize, MAXLNUM, newsize - oldsize, kExtmarkNOOP); 2376 if (curbuf->b_op_start.lnum > top + oldsize) { 2377 curbuf->b_op_start.lnum += newsize - oldsize; 2378 } 2379 if (curbuf->b_op_end.lnum > top + oldsize) { 2380 curbuf->b_op_end.lnum += newsize - oldsize; 2381 } 2382 } 2383 2384 if (oldsize > 0 || newsize > 0) { 2385 changed_lines(curbuf, top + 1, 0, bot, newsize - oldsize, do_buf_event); 2386 // When text has been changed, possibly the start of the next line 2387 // may have SpellCap that should be removed or it needs to be 2388 // displayed. Schedule the next line for redrawing just in case. 2389 if (spell_check_window(curwin) && bot <= curbuf->b_ml.ml_line_count) { 2390 redrawWinline(curwin, bot); 2391 } 2392 } 2393 2394 // Set the '[ mark. 2395 curbuf->b_op_start.lnum = MIN(curbuf->b_op_start.lnum, top + 1); 2396 // Set the '] mark. 2397 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum) { 2398 curbuf->b_op_end.lnum = top + 1; 2399 } else if (top + newsize > curbuf->b_op_end.lnum) { 2400 curbuf->b_op_end.lnum = top + newsize; 2401 } 2402 2403 u_newcount += newsize; 2404 u_oldcount += oldsize; 2405 uep->ue_size = oldsize; 2406 uep->ue_array = newarray; 2407 uep->ue_bot = top + newsize + 1; 2408 2409 // insert this entry in front of the new entry list 2410 nuep = uep->ue_next; 2411 uep->ue_next = newlist; 2412 newlist = uep; 2413 } 2414 2415 // Ensure the '[ and '] marks are within bounds. 2416 curbuf->b_op_start.lnum = MIN(curbuf->b_op_start.lnum, curbuf->b_ml.ml_line_count); 2417 curbuf->b_op_end.lnum = MIN(curbuf->b_op_end.lnum, curbuf->b_ml.ml_line_count); 2418 2419 // Adjust Extmarks 2420 if (undo) { 2421 for (int i = (int)kv_size(curhead->uh_extmark) - 1; i > -1; i--) { 2422 extmark_apply_undo(kv_A(curhead->uh_extmark, i), undo); 2423 } 2424 // redo 2425 } else { 2426 for (int i = 0; i < (int)kv_size(curhead->uh_extmark); i++) { 2427 extmark_apply_undo(kv_A(curhead->uh_extmark, i), undo); 2428 } 2429 } 2430 if (curhead->uh_flags & UH_RELOAD) { 2431 // TODO(bfredl): this is a bit crude. When 'undoreload' is used we 2432 // should have all info to send a buffer-reloaing on_lines/on_bytes event 2433 buf_updates_unload(curbuf, true); 2434 } 2435 // Finish adjusting extmarks 2436 2437 // Set the cursor to the desired position. Check that the line is valid. 2438 curwin->w_cursor = new_curpos; 2439 check_cursor_lnum(curwin); 2440 2441 curhead->uh_entry = newlist; 2442 curhead->uh_flags = new_flags; 2443 if ((old_flags & UH_EMPTYBUF) && buf_is_empty(curbuf)) { 2444 curbuf->b_ml.ml_flags |= ML_EMPTY; 2445 } 2446 if (old_flags & UH_CHANGED) { 2447 changed(curbuf); 2448 } else { 2449 unchanged(curbuf, false, true); 2450 } 2451 2452 // because the calls to changed()/unchanged() above will bump changedtick 2453 // again, we need to send a nvim_buf_lines_event with just the new value of 2454 // b:changedtick 2455 if (do_buf_event) { 2456 buf_updates_changedtick(curbuf); 2457 } 2458 2459 // restore marks from before undo/redo 2460 for (int i = 0; i < NMARKS; i++) { 2461 if (curhead->uh_namedm[i].mark.lnum != 0) { 2462 free_fmark(curbuf->b_namedm[i]); 2463 curbuf->b_namedm[i] = curhead->uh_namedm[i]; 2464 } 2465 if (namedm[i].mark.lnum != 0) { 2466 curhead->uh_namedm[i] = namedm[i]; 2467 } else { 2468 curhead->uh_namedm[i].mark.lnum = 0; 2469 } 2470 } 2471 if (curhead->uh_visual.vi_start.lnum != 0) { 2472 curbuf->b_visual = curhead->uh_visual; 2473 curhead->uh_visual = visualinfo; 2474 } 2475 2476 // If the cursor is only off by one line, put it at the same position as 2477 // before starting the change (for the "o" command). 2478 // Otherwise the cursor should go to the first undone line. 2479 if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum 2480 && curwin->w_cursor.lnum > 1) { 2481 curwin->w_cursor.lnum--; 2482 } 2483 if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count) { 2484 if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum) { 2485 curwin->w_cursor.col = curhead->uh_cursor.col; 2486 if (virtual_active(curwin) && curhead->uh_cursor_vcol >= 0) { 2487 coladvance(curwin, curhead->uh_cursor_vcol); 2488 } else { 2489 curwin->w_cursor.coladd = 0; 2490 } 2491 } else { 2492 beginline(BL_SOL | BL_FIX); 2493 } 2494 } else { 2495 // We get here with the current cursor line being past the end (eg 2496 // after adding lines at the end of the file, and then undoing it). 2497 // check_cursor() will move the cursor to the last line. Move it to 2498 // the first column here. 2499 curwin->w_cursor.col = 0; 2500 curwin->w_cursor.coladd = 0; 2501 } 2502 2503 // Make sure the cursor is on an existing line and column. 2504 check_cursor(curwin); 2505 2506 // Remember where we are for "g-" and ":earlier 10s". 2507 curbuf->b_u_seq_cur = curhead->uh_seq; 2508 if (undo) { 2509 // We are below the previous undo. However, to make ":earlier 1s" 2510 // work we compute this as being just above the just undone change. 2511 curbuf->b_u_seq_cur = curhead->uh_next.ptr 2512 ? curhead->uh_next.ptr->uh_seq : 0; 2513 } 2514 2515 // Remember where we are for ":earlier 1f" and ":later 1f". 2516 if (curhead->uh_save_nr != 0) { 2517 if (undo) { 2518 curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1; 2519 } else { 2520 curbuf->b_u_save_nr_cur = curhead->uh_save_nr; 2521 } 2522 } 2523 2524 // The timestamp can be the same for multiple changes, just use the one of 2525 // the undone/redone change. 2526 curbuf->b_u_time_cur = curhead->uh_time; 2527 2528 unblock_autocmds(); 2529 #ifdef U_DEBUG 2530 u_check(false); 2531 #endif 2532 } 2533 2534 /// If we deleted or added lines, report the number of less/more lines. 2535 /// Otherwise, report the number of changes (this may be incorrect 2536 /// in some cases, but it's better than nothing). 2537 /// 2538 /// @param did_undo just did an undo 2539 /// @param absolute used ":undo N" 2540 static void u_undo_end(bool did_undo, bool absolute, bool quiet) 2541 { 2542 if ((fdo_flags & kOptFdoFlagUndo) && KeyTyped) { 2543 foldOpenCursor(); 2544 } 2545 2546 if (quiet 2547 || global_busy // no messages until global is finished 2548 || !messaging()) { // 'lazyredraw' set, don't do messages now 2549 return; 2550 } 2551 2552 if (curbuf->b_ml.ml_flags & ML_EMPTY) { 2553 u_newcount--; 2554 } 2555 2556 u_oldcount -= u_newcount; 2557 char *msgstr; 2558 if (u_oldcount == -1) { 2559 msgstr = N_("more line"); 2560 } else if (u_oldcount < 0) { 2561 msgstr = N_("more lines"); 2562 } else if (u_oldcount == 1) { 2563 msgstr = N_("line less"); 2564 } else if (u_oldcount > 1) { 2565 msgstr = N_("fewer lines"); 2566 } else { 2567 u_oldcount = u_newcount; 2568 if (u_newcount == 1) { 2569 msgstr = N_("change"); 2570 } else { 2571 msgstr = N_("changes"); 2572 } 2573 } 2574 2575 u_header_T *uhp; 2576 if (curbuf->b_u_curhead != NULL) { 2577 // For ":undo N" we prefer a "after #N" message. 2578 if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL) { 2579 uhp = curbuf->b_u_curhead->uh_next.ptr; 2580 did_undo = false; 2581 } else if (did_undo) { 2582 uhp = curbuf->b_u_curhead; 2583 } else { 2584 uhp = curbuf->b_u_curhead->uh_next.ptr; 2585 } 2586 } else { 2587 uhp = curbuf->b_u_newhead; 2588 } 2589 2590 char msgbuf[80]; 2591 if (uhp == NULL) { 2592 *msgbuf = NUL; 2593 } else { 2594 undo_fmt_time(msgbuf, sizeof(msgbuf), uhp->uh_time); 2595 } 2596 2597 { 2598 FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { 2599 if (wp->w_buffer == curbuf && wp->w_p_cole > 0) { 2600 redraw_later(wp, UPD_NOT_VALID); 2601 } 2602 } 2603 } 2604 2605 if (VIsual_active) { 2606 check_pos(curbuf, &VIsual); 2607 } 2608 2609 smsg_keep(0, _("%" PRId64 " %s; %s #%" PRId64 " %s"), 2610 u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount, 2611 _(msgstr), 2612 did_undo ? _("before") : _("after"), 2613 uhp == NULL ? 0 : (int64_t)uhp->uh_seq, 2614 msgbuf); 2615 } 2616 2617 /// Put the timestamp of an undo header in "buf[buflen]" in a nice format. 2618 void undo_fmt_time(char *buf, size_t buflen, time_t tt) 2619 { 2620 if (time(NULL) - tt >= 100) { 2621 struct tm curtime; 2622 os_localtime_r(&tt, &curtime); 2623 size_t n; 2624 if (time(NULL) - tt < (60 * 60 * 12)) { 2625 // within 12 hours 2626 n = strftime(buf, buflen, "%H:%M:%S", &curtime); 2627 } else { 2628 // longer ago 2629 n = strftime(buf, buflen, "%Y/%m/%d %H:%M:%S", &curtime); 2630 } 2631 if (n == 0) { 2632 buf[0] = NUL; 2633 } 2634 } else { 2635 int64_t seconds = time(NULL) - tt; 2636 vim_snprintf(buf, buflen, 2637 NGETTEXT("%" PRId64 " second ago", 2638 "%" PRId64 " seconds ago", (uint32_t)seconds), 2639 seconds); 2640 } 2641 } 2642 2643 /// u_sync: stop adding to the current entry list 2644 /// 2645 /// @param force if true, also sync when no_u_sync is set. 2646 void u_sync(bool force) 2647 { 2648 // Skip it when already synced or syncing is disabled. 2649 if (curbuf->b_u_synced || (!force && no_u_sync > 0)) { 2650 return; 2651 } 2652 2653 if (get_undolevel(curbuf) < 0) { 2654 curbuf->b_u_synced = true; // no entries, nothing to do 2655 } else { 2656 u_getbot(curbuf); // compute ue_bot of previous u_save 2657 curbuf->b_u_curhead = NULL; 2658 } 2659 } 2660 2661 /// ":undolist": List the leafs of the undo tree 2662 void ex_undolist(exarg_T *eap) 2663 { 2664 int changes = 1; 2665 2666 // 1: walk the tree to find all leafs, put the info in "ga". 2667 // 2: sort the lines 2668 // 3: display the list 2669 int mark = ++lastmark; 2670 int nomark = ++lastmark; 2671 garray_T ga; 2672 ga_init(&ga, (int)sizeof(char *), 20); 2673 2674 u_header_T *uhp = curbuf->b_u_oldhead; 2675 while (uhp != NULL) { 2676 if (uhp->uh_prev.ptr == NULL && uhp->uh_walk != nomark 2677 && uhp->uh_walk != mark) { 2678 vim_snprintf(IObuff, IOSIZE, "%6d %7d ", uhp->uh_seq, changes); 2679 undo_fmt_time(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff), uhp->uh_time); 2680 if (uhp->uh_save_nr > 0) { 2681 while (strlen(IObuff) < 33) { 2682 xstrlcat(IObuff, " ", IOSIZE); 2683 } 2684 vim_snprintf_add(IObuff, IOSIZE, " %3d", uhp->uh_save_nr); 2685 } 2686 GA_APPEND(char *, &ga, xstrdup(IObuff)); 2687 } 2688 2689 uhp->uh_walk = mark; 2690 2691 // go down in the tree if we haven't been there 2692 if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark 2693 && uhp->uh_prev.ptr->uh_walk != mark) { 2694 uhp = uhp->uh_prev.ptr; 2695 changes++; 2696 } else if (uhp->uh_alt_next.ptr != NULL 2697 && uhp->uh_alt_next.ptr->uh_walk != nomark 2698 && uhp->uh_alt_next.ptr->uh_walk != mark) { 2699 // go to alternate branch if we haven't been there 2700 uhp = uhp->uh_alt_next.ptr; 2701 } else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL 2702 // go up in the tree if we haven't been there and we are at the 2703 // start of alternate branches 2704 && uhp->uh_next.ptr->uh_walk != nomark 2705 && uhp->uh_next.ptr->uh_walk != mark) { 2706 uhp = uhp->uh_next.ptr; 2707 changes--; 2708 } else { 2709 // need to backtrack; mark this node as done 2710 uhp->uh_walk = nomark; 2711 if (uhp->uh_alt_prev.ptr != NULL) { 2712 uhp = uhp->uh_alt_prev.ptr; 2713 } else { 2714 uhp = uhp->uh_next.ptr; 2715 changes--; 2716 } 2717 } 2718 } 2719 2720 msg_ext_set_kind("list_cmd"); 2721 if (GA_EMPTY(&ga)) { 2722 msg(_("Nothing to undo"), 0); 2723 } else { 2724 sort_strings(ga.ga_data, ga.ga_len); 2725 2726 msg_start(); 2727 msg_puts_hl(_("number changes when saved"), HLF_T, false); 2728 for (int i = 0; i < ga.ga_len && !got_int; i++) { 2729 msg_putchar('\n'); 2730 if (got_int) { 2731 break; 2732 } 2733 msg_puts(((const char **)ga.ga_data)[i]); 2734 } 2735 msg_end(); 2736 2737 ga_clear_strings(&ga); 2738 } 2739 } 2740 2741 /// ":undojoin": continue adding to the last entry list 2742 void ex_undojoin(exarg_T *eap) 2743 { 2744 if (curbuf->b_u_newhead == NULL) { 2745 return; // nothing changed before 2746 } 2747 if (curbuf->b_u_curhead != NULL) { 2748 emsg(_("E790: undojoin is not allowed after undo")); 2749 return; 2750 } 2751 if (!curbuf->b_u_synced) { 2752 return; // already unsynced 2753 } 2754 if (get_undolevel(curbuf) < 0) { 2755 return; // no entries, nothing to do 2756 } 2757 curbuf->b_u_synced = false; // Append next change to last entry 2758 } 2759 2760 /// Called after writing or reloading the file and setting b_changed to false. 2761 /// Now an undo means that the buffer is modified. 2762 void u_unchanged(buf_T *buf) 2763 { 2764 u_unch_branch(buf->b_u_oldhead); 2765 buf->b_did_warn = false; 2766 } 2767 2768 /// After reloading a buffer which was saved for 'undoreload': Find the first 2769 /// line that was changed and set the cursor there. 2770 void u_find_first_changed(void) 2771 { 2772 u_header_T *uhp = curbuf->b_u_newhead; 2773 2774 if (curbuf->b_u_curhead != NULL || uhp == NULL) { 2775 return; // undid something in an autocmd? 2776 } 2777 // Check that the last undo block was for the whole file. 2778 u_entry_T *uep = uhp->uh_entry; 2779 if (uep->ue_top != 0 || uep->ue_bot != 0) { 2780 return; 2781 } 2782 2783 linenr_T lnum; 2784 for (lnum = 1; lnum < curbuf->b_ml.ml_line_count 2785 && lnum <= uep->ue_size; lnum++) { 2786 if (strcmp(ml_get_buf(curbuf, lnum), uep->ue_array[lnum - 1]) != 0) { 2787 clearpos(&(uhp->uh_cursor)); 2788 uhp->uh_cursor.lnum = lnum; 2789 return; 2790 } 2791 } 2792 if (curbuf->b_ml.ml_line_count != uep->ue_size) { 2793 // lines added or deleted at the end, put the cursor there 2794 clearpos(&(uhp->uh_cursor)); 2795 uhp->uh_cursor.lnum = lnum; 2796 } 2797 } 2798 2799 /// Increase the write count, store it in the last undo header, what would be 2800 /// used for "u". 2801 void u_update_save_nr(buf_T *buf) 2802 { 2803 buf->b_u_save_nr_last++; 2804 buf->b_u_save_nr_cur = buf->b_u_save_nr_last; 2805 u_header_T *uhp = buf->b_u_curhead; 2806 if (uhp != NULL) { 2807 uhp = uhp->uh_next.ptr; 2808 } else { 2809 uhp = buf->b_u_newhead; 2810 } 2811 if (uhp != NULL) { 2812 uhp->uh_save_nr = buf->b_u_save_nr_last; 2813 } 2814 } 2815 2816 static void u_unch_branch(u_header_T *uhp) 2817 { 2818 for (u_header_T *uh = uhp; uh != NULL; uh = uh->uh_prev.ptr) { 2819 uh->uh_flags |= UH_CHANGED; 2820 if (uh->uh_alt_next.ptr != NULL) { 2821 u_unch_branch(uh->uh_alt_next.ptr); // recursive 2822 } 2823 } 2824 } 2825 2826 /// Get pointer to last added entry. 2827 /// If it's not valid, give an error message and return NULL. 2828 static u_entry_T *u_get_headentry(buf_T *buf) 2829 { 2830 if (buf->b_u_newhead == NULL || buf->b_u_newhead->uh_entry == NULL) { 2831 iemsg(_(e_undo_list_corrupt)); 2832 return NULL; 2833 } 2834 return buf->b_u_newhead->uh_entry; 2835 } 2836 2837 /// u_getbot(): compute the line number of the previous u_save 2838 /// It is called only when b_u_synced is false. 2839 static void u_getbot(buf_T *buf) 2840 { 2841 u_entry_T *uep = u_get_headentry(buf); // check for corrupt undo list 2842 if (uep == NULL) { 2843 return; 2844 } 2845 2846 uep = buf->b_u_newhead->uh_getbot_entry; 2847 if (uep != NULL) { 2848 // the new ue_bot is computed from the number of lines that has been 2849 // inserted (0 - deleted) since calling u_save. This is equal to the 2850 // old line count subtracted from the current line count. 2851 linenr_T extra = buf->b_ml.ml_line_count - uep->ue_lcount; 2852 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra; 2853 if (uep->ue_bot < 1 || uep->ue_bot > buf->b_ml.ml_line_count) { 2854 iemsg(_(e_undo_line_missing)); 2855 uep->ue_bot = uep->ue_top + 1; // assume all lines deleted, will 2856 // get all the old lines back 2857 // without deleting the current 2858 // ones 2859 } 2860 2861 buf->b_u_newhead->uh_getbot_entry = NULL; 2862 } 2863 2864 buf->b_u_synced = true; 2865 } 2866 2867 /// Free one header "uhp" and its entry list and adjust the pointers. 2868 /// 2869 /// @param uhpp if not NULL reset when freeing this header 2870 static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) 2871 { 2872 // When there is an alternate redo list free that branch completely, 2873 // because we can never go there. 2874 if (uhp->uh_alt_next.ptr != NULL) { 2875 u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp); 2876 } 2877 2878 if (uhp->uh_alt_prev.ptr != NULL) { 2879 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL; 2880 } 2881 2882 // Update the links in the list to remove the header. 2883 if (uhp->uh_next.ptr == NULL) { 2884 buf->b_u_oldhead = uhp->uh_prev.ptr; 2885 } else { 2886 uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr; 2887 } 2888 2889 if (uhp->uh_prev.ptr == NULL) { 2890 buf->b_u_newhead = uhp->uh_next.ptr; 2891 } else { 2892 for (u_header_T *uhap = uhp->uh_prev.ptr; uhap != NULL; 2893 uhap = uhap->uh_alt_next.ptr) { 2894 uhap->uh_next.ptr = uhp->uh_next.ptr; 2895 } 2896 } 2897 2898 u_freeentries(buf, uhp, uhpp); 2899 } 2900 2901 /// Free an alternate branch and any following alternate branches. 2902 /// 2903 /// @param uhpp if not NULL reset when freeing this header 2904 static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) 2905 { 2906 // If this is the top branch we may need to use u_freeheader() to update 2907 // all the pointers. 2908 if (uhp == buf->b_u_oldhead) { 2909 while (buf->b_u_oldhead != NULL) { 2910 u_freeheader(buf, buf->b_u_oldhead, uhpp); 2911 } 2912 return; 2913 } 2914 2915 if (uhp->uh_alt_prev.ptr != NULL) { 2916 uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL; 2917 } 2918 2919 u_header_T *next = uhp; 2920 while (next != NULL) { 2921 u_header_T *tofree = next; 2922 if (tofree->uh_alt_next.ptr != NULL) { 2923 u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); // recursive 2924 } 2925 next = tofree->uh_prev.ptr; 2926 u_freeentries(buf, tofree, uhpp); 2927 } 2928 } 2929 2930 /// Free all the undo entries for one header and the header itself. 2931 /// This means that "uhp" is invalid when returning. 2932 /// 2933 /// @param uhpp if not NULL reset when freeing this header 2934 static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) 2935 { 2936 // Check for pointers to the header that become invalid now. 2937 if (buf->b_u_curhead == uhp) { 2938 buf->b_u_curhead = NULL; 2939 } 2940 if (buf->b_u_newhead == uhp) { 2941 buf->b_u_newhead = NULL; // freeing the newest entry 2942 } 2943 if (uhpp != NULL && uhp == *uhpp) { 2944 *uhpp = NULL; 2945 } 2946 2947 u_entry_T *nuep; 2948 for (u_entry_T *uep = uhp->uh_entry; uep != NULL; uep = nuep) { 2949 nuep = uep->ue_next; 2950 u_freeentry(uep, uep->ue_size); 2951 } 2952 2953 kv_destroy(uhp->uh_extmark); 2954 2955 #ifdef U_DEBUG 2956 uhp->uh_magic = 0; 2957 #endif 2958 xfree(uhp); 2959 buf->b_u_numhead--; 2960 } 2961 2962 /// free entry 'uep' and 'n' lines in uep->ue_array[] 2963 static void u_freeentry(u_entry_T *uep, int n) 2964 { 2965 while (n > 0) { 2966 xfree(uep->ue_array[--n]); 2967 } 2968 xfree(uep->ue_array); 2969 #ifdef U_DEBUG 2970 uep->ue_magic = 0; 2971 #endif 2972 xfree(uep); 2973 } 2974 2975 /// invalidate the undo buffer; called when storage has already been released 2976 void u_clearall(buf_T *buf) 2977 { 2978 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL; 2979 buf->b_u_synced = true; 2980 buf->b_u_numhead = 0; 2981 buf->b_u_line_ptr = NULL; 2982 buf->b_u_line_lnum = 0; 2983 } 2984 2985 /// Free all allocated memory blocks for the buffer 'buf'. 2986 void u_blockfree(buf_T *buf) 2987 { 2988 while (buf->b_u_oldhead != NULL) { 2989 #ifndef NDEBUG 2990 u_header_T *previous_oldhead = buf->b_u_oldhead; 2991 #endif 2992 2993 u_freeheader(buf, buf->b_u_oldhead, NULL); 2994 assert(buf->b_u_oldhead != previous_oldhead); 2995 } 2996 xfree(buf->b_u_line_ptr); 2997 } 2998 2999 /// Free all allocated memory blocks for the buffer 'buf'. 3000 /// and invalidate the undo buffer 3001 void u_clearallandblockfree(buf_T *buf) 3002 { 3003 u_blockfree(buf); 3004 u_clearall(buf); 3005 } 3006 3007 /// Save the line "lnum" for the "U" command. 3008 static void u_saveline(buf_T *buf, linenr_T lnum) 3009 { 3010 if (lnum == buf->b_u_line_lnum) { // line is already saved 3011 return; 3012 } 3013 if (lnum < 1 || lnum > buf->b_ml.ml_line_count) { // should never happen 3014 return; 3015 } 3016 u_clearline(buf); 3017 buf->b_u_line_lnum = lnum; 3018 if (curwin->w_buffer == buf && curwin->w_cursor.lnum == lnum) { 3019 buf->b_u_line_colnr = curwin->w_cursor.col; 3020 } else { 3021 buf->b_u_line_colnr = 0; 3022 } 3023 buf->b_u_line_ptr = u_save_line_buf(buf, lnum); 3024 } 3025 3026 /// clear the line saved for the "U" command 3027 /// (this is used externally for crossing a line while in insert mode) 3028 void u_clearline(buf_T *buf) 3029 { 3030 if (buf->b_u_line_ptr == NULL) { 3031 return; 3032 } 3033 3034 XFREE_CLEAR(buf->b_u_line_ptr); 3035 buf->b_u_line_lnum = 0; 3036 } 3037 3038 /// Implementation of the "U" command. 3039 /// Differentiation from vi: "U" can be undone with the next "U". 3040 /// We also allow the cursor to be in another line. 3041 /// Careful: may trigger autocommands that reload the buffer. 3042 void u_undoline(void) 3043 { 3044 if (curbuf->b_u_line_ptr == NULL 3045 || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count) { 3046 beep_flush(); 3047 return; 3048 } 3049 3050 // first save the line for the 'u' command 3051 if (u_savecommon(curbuf, curbuf->b_u_line_lnum - 1, 3052 curbuf->b_u_line_lnum + 1, 0, false) == FAIL) { 3053 return; 3054 } 3055 3056 char *oldp = u_save_line(curbuf->b_u_line_lnum); 3057 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, true); 3058 extmark_splice_cols(curbuf, (int)curbuf->b_u_line_lnum - 1, 0, (colnr_T)strlen(oldp), 3059 (colnr_T)strlen(curbuf->b_u_line_ptr), kExtmarkUndo); 3060 changed_bytes(curbuf->b_u_line_lnum, 0); 3061 xfree(curbuf->b_u_line_ptr); 3062 curbuf->b_u_line_ptr = oldp; 3063 3064 colnr_T t = curbuf->b_u_line_colnr; 3065 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum) { 3066 curbuf->b_u_line_colnr = curwin->w_cursor.col; 3067 } 3068 curwin->w_cursor.col = t; 3069 curwin->w_cursor.lnum = curbuf->b_u_line_lnum; 3070 check_cursor_col(curwin); 3071 } 3072 3073 /// Allocate memory and copy curbuf line into it. 3074 /// 3075 /// @param lnum the line to copy 3076 static char *u_save_line(linenr_T lnum) 3077 { 3078 return u_save_line_buf(curbuf, lnum); 3079 } 3080 3081 /// Allocate memory and copy line into it 3082 /// 3083 /// @param lnum line to copy 3084 /// @param buf buffer to copy from 3085 static char *u_save_line_buf(buf_T *buf, linenr_T lnum) 3086 { 3087 return xstrdup(ml_get_buf(buf, lnum)); 3088 } 3089 3090 /// Check if the 'modified' flag is set, or 'ff' has changed (only need to 3091 /// check the first character, because it can only be "dos", "unix" or "mac"). 3092 /// "nofile" and "scratch" type buffers are considered to always be unchanged. 3093 /// 3094 /// @param buf The buffer to check 3095 /// 3096 /// @return true if the buffer has changed 3097 bool bufIsChanged(buf_T *buf) 3098 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT 3099 { 3100 // In a "prompt" buffer we do respect 'modified', so that we can control 3101 // closing the window by setting or resetting that option. 3102 return (!bt_dontwrite(buf) || bt_prompt(buf)) 3103 && (buf->b_changed || file_ff_differs(buf, true)); 3104 } 3105 3106 // Return true if any buffer has changes. Also buffers that are not written. 3107 bool anyBufIsChanged(void) 3108 FUNC_ATTR_WARN_UNUSED_RESULT 3109 { 3110 FOR_ALL_BUFFERS(buf) { 3111 if (bufIsChanged(buf)) { 3112 return true; 3113 } 3114 } 3115 return false; 3116 } 3117 3118 /// @see bufIsChanged 3119 /// @return true if the current buffer has changed 3120 bool curbufIsChanged(void) 3121 FUNC_ATTR_WARN_UNUSED_RESULT 3122 { 3123 return bufIsChanged(curbuf); 3124 } 3125 3126 /// Append the list of undo blocks to a newly allocated list 3127 /// 3128 /// For use in undotree(). Recursive. 3129 /// 3130 /// @param[in] first_uhp Undo blocks list to start with. 3131 /// 3132 /// @return [allocated] List with a representation of undo blocks. 3133 static list_T *u_eval_tree(buf_T *const buf, const u_header_T *const first_uhp) 3134 FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET 3135 { 3136 list_T *const list = tv_list_alloc(kListLenMayKnow); 3137 3138 for (const u_header_T *uhp = first_uhp; uhp != NULL; uhp = uhp->uh_prev.ptr) { 3139 dict_T *const dict = tv_dict_alloc(); 3140 tv_dict_add_nr(dict, S_LEN("seq"), (varnumber_T)uhp->uh_seq); 3141 tv_dict_add_nr(dict, S_LEN("time"), (varnumber_T)uhp->uh_time); 3142 if (uhp == buf->b_u_newhead) { 3143 tv_dict_add_nr(dict, S_LEN("newhead"), 1); 3144 } 3145 if (uhp == buf->b_u_curhead) { 3146 tv_dict_add_nr(dict, S_LEN("curhead"), 1); 3147 } 3148 if (uhp->uh_save_nr > 0) { 3149 tv_dict_add_nr(dict, S_LEN("save"), (varnumber_T)uhp->uh_save_nr); 3150 } 3151 3152 if (uhp->uh_alt_next.ptr != NULL) { 3153 // Recursive call to add alternate undo tree. 3154 tv_dict_add_list(dict, S_LEN("alt"), u_eval_tree(buf, uhp->uh_alt_next.ptr)); 3155 } 3156 3157 tv_list_append_dict(list, dict); 3158 } 3159 3160 return list; 3161 } 3162 3163 /// "undofile(name)" function 3164 void f_undofile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3165 { 3166 rettv->v_type = VAR_STRING; 3167 const char *const fname = tv_get_string(&argvars[0]); 3168 3169 if (*fname == NUL) { 3170 // If there is no file name there will be no undo file. 3171 rettv->vval.v_string = NULL; 3172 } else { 3173 char *ffname = FullName_save(fname, true); 3174 3175 if (ffname != NULL) { 3176 rettv->vval.v_string = u_get_undo_file_name(ffname, false); 3177 } 3178 xfree(ffname); 3179 } 3180 } 3181 3182 /// "undotree(expr)" function 3183 void f_undotree(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) 3184 { 3185 tv_dict_alloc_ret(rettv); 3186 3187 typval_T *const tv = &argvars[0]; 3188 buf_T *const buf = tv->v_type == VAR_UNKNOWN ? curbuf : get_buf_arg(tv); 3189 if (buf == NULL) { 3190 return; 3191 } 3192 3193 dict_T *dict = rettv->vval.v_dict; 3194 3195 tv_dict_add_nr(dict, S_LEN("synced"), (varnumber_T)buf->b_u_synced); 3196 tv_dict_add_nr(dict, S_LEN("seq_last"), (varnumber_T)buf->b_u_seq_last); 3197 tv_dict_add_nr(dict, S_LEN("save_last"), (varnumber_T)buf->b_u_save_nr_last); 3198 tv_dict_add_nr(dict, S_LEN("seq_cur"), (varnumber_T)buf->b_u_seq_cur); 3199 tv_dict_add_nr(dict, S_LEN("time_cur"), (varnumber_T)buf->b_u_time_cur); 3200 tv_dict_add_nr(dict, S_LEN("save_cur"), (varnumber_T)buf->b_u_save_nr_cur); 3201 3202 tv_dict_add_list(dict, S_LEN("entries"), u_eval_tree(buf, buf->b_u_oldhead)); 3203 } 3204 3205 // Given the buffer, Return the undo header. If none is set, set one first. 3206 // NULL will be returned if e.g undolevels = -1 (undo disabled) 3207 u_header_T *u_force_get_undo_header(buf_T *buf) 3208 { 3209 u_header_T *uhp = NULL; 3210 if (buf->b_u_curhead != NULL) { 3211 uhp = buf->b_u_curhead; 3212 } else if (buf->b_u_newhead) { 3213 uhp = buf->b_u_newhead; 3214 } 3215 // Create the first undo header for the buffer 3216 if (!uhp) { 3217 // Args are tricky: this means replace empty range by empty range.. 3218 u_savecommon(buf, 0, 1, 1, true); 3219 3220 uhp = buf->b_u_curhead; 3221 if (!uhp) { 3222 uhp = buf->b_u_newhead; 3223 if (get_undolevel(buf) > 0 && !uhp) { 3224 abort(); 3225 } 3226 } 3227 } 3228 return uhp; 3229 }