1 /* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 * See README.txt for an overview of the Vim source code. 8 */ 9 10 /* 11 * undo.c: multi level undo facility 12 * 13 * The saved lines are stored in a list of lists (one for each buffer): 14 * 15 * b_u_oldhead------------------------------------------------+ 16 * | 17 * V 18 * +--------------+ +--------------+ +--------------+ 19 * b_u_newhead--->| u_header | | u_header | | u_header | 20 * | uh_next------>| uh_next------>| uh_next---->NULL 21 * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev | 22 * | uh_entry | | uh_entry | | uh_entry | 23 * +--------|-----+ +--------|-----+ +--------|-----+ 24 * | | | 25 * V V V 26 * +--------------+ +--------------+ +--------------+ 27 * | u_entry | | u_entry | | u_entry | 28 * | ue_next | | ue_next | | ue_next | 29 * +--------|-----+ +--------|-----+ +--------|-----+ 30 * | | | 31 * V V V 32 * +--------------+ NULL NULL 33 * | u_entry | 34 * | ue_next | 35 * +--------|-----+ 36 * | 37 * V 38 * etc. 39 * 40 * Each u_entry list contains the information for one undo or redo. 41 * curbuf->b_u_curhead points to the header of the last undo (the next redo), 42 * or is NULL if nothing has been undone. 43 * 44 * All data is allocated with U_ALLOC_LINE(), it will be freed as soon as the 45 * buffer is unloaded. 46 */ 47 48 #include "vim.h" 49 50 /* See below: use malloc()/free() for memory management. */ 51 #define U_USE_MALLOC 1 52 53 static u_entry_T *u_get_headentry __ARGS((void)); 54 static void u_getbot __ARGS((void)); 55 static int undo_allowed __ARGS((void)); 56 static int u_savecommon __ARGS((linenr_T, linenr_T, linenr_T)); 57 static void u_doit __ARGS((int count)); 58 static void u_undoredo __ARGS((void)); 59 static void u_undo_end __ARGS((void)); 60 static void u_freelist __ARGS((buf_T *buf, struct u_header *)); 61 static void u_freeentry __ARGS((u_entry_T *, long)); 62 63 #ifdef U_USE_MALLOC 64 # define U_FREE_LINE(ptr) vim_free(ptr) 65 # define U_ALLOC_LINE(size) lalloc((long_u)((size) + 1), FALSE) 66 #else 67 static void u_free_line __ARGS((char_u *ptr, int keep)); 68 static char_u *u_alloc_line __ARGS((unsigned size)); 69 # define U_FREE_LINE(ptr) u_free_line((ptr), FALSE) 70 # define U_ALLOC_LINE(size) u_alloc_line(size) 71 #endif 72 static char_u *u_save_line __ARGS((linenr_T)); 73 74 static long u_newcount, u_oldcount; 75 76 /* 77 * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember 78 * the action that "u" should do. 79 */ 80 static int undo_undoes = FALSE; 81 82 /* 83 * Save the current line for both the "u" and "U" command. 84 * Returns OK or FAIL. 85 */ 86 int 87 u_save_cursor() 88 { 89 return (u_save((linenr_T)(curwin->w_cursor.lnum - 1), 90 (linenr_T)(curwin->w_cursor.lnum + 1))); 91 } 92 93 /* 94 * Save the lines between "top" and "bot" for both the "u" and "U" command. 95 * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1. 96 * Returns FAIL when lines could not be saved, OK otherwise. 97 */ 98 int 99 u_save(top, bot) 100 linenr_T top, bot; 101 { 102 if (undo_off) 103 return OK; 104 105 if (top > curbuf->b_ml.ml_line_count || 106 top >= bot || bot > curbuf->b_ml.ml_line_count + 1) 107 return FALSE; /* rely on caller to do error messages */ 108 109 if (top + 2 == bot) 110 u_saveline((linenr_T)(top + 1)); 111 112 return (u_savecommon(top, bot, (linenr_T)0)); 113 } 114 115 /* 116 * save the line "lnum" (used by ":s" and "~" command) 117 * The line is replaced, so the new bottom line is lnum + 1. 118 */ 119 int 120 u_savesub(lnum) 121 linenr_T lnum; 122 { 123 if (undo_off) 124 return OK; 125 126 return (u_savecommon(lnum - 1, lnum + 1, lnum + 1)); 127 } 128 129 /* 130 * a new line is inserted before line "lnum" (used by :s command) 131 * The line is inserted, so the new bottom line is lnum + 1. 132 */ 133 int 134 u_inssub(lnum) 135 linenr_T lnum; 136 { 137 if (undo_off) 138 return OK; 139 140 return (u_savecommon(lnum - 1, lnum, lnum + 1)); 141 } 142 143 /* 144 * save the lines "lnum" - "lnum" + nlines (used by delete command) 145 * The lines are deleted, so the new bottom line is lnum, unless the buffer 146 * becomes empty. 147 */ 148 int 149 u_savedel(lnum, nlines) 150 linenr_T lnum; 151 long nlines; 152 { 153 if (undo_off) 154 return OK; 155 156 return (u_savecommon(lnum - 1, lnum + nlines, 157 nlines == curbuf->b_ml.ml_line_count ? 2 : lnum)); 158 } 159 160 /* 161 * Return TRUE when undo is allowed. Otherwise give an error message and 162 * return FALSE. 163 */ 164 static int 165 undo_allowed() 166 { 167 /* Don't allow changes when 'modifiable' is off. */ 168 if (!curbuf->b_p_ma) 169 { 170 EMSG(_(e_modifiable)); 171 return FALSE; 172 } 173 174 #ifdef HAVE_SANDBOX 175 /* In the sandbox it's not allowed to change the text. */ 176 if (sandbox != 0) 177 { 178 EMSG(_(e_sandbox)); 179 return FALSE; 180 } 181 #endif 182 183 /* Don't allow changes in the buffer while editing the cmdline. The 184 * caller of getcmdline() may get confused. */ 185 if (textlock != 0) 186 { 187 EMSG(_(e_secure)); 188 return FALSE; 189 } 190 191 return TRUE; 192 } 193 194 static int 195 u_savecommon(top, bot, newbot) 196 linenr_T top, bot; 197 linenr_T newbot; 198 { 199 linenr_T lnum; 200 long i; 201 struct u_header *uhp; 202 u_entry_T *uep; 203 u_entry_T *prev_uep; 204 long size; 205 206 /* When making changes is not allowed return FAIL. It's a crude way to 207 * make all change commands fail. */ 208 if (!undo_allowed()) 209 return FAIL; 210 211 #ifdef FEAT_NETBEANS_INTG 212 /* 213 * Netbeans defines areas that cannot be modified. Bail out here when 214 * trying to change text in a guarded area. 215 */ 216 if (usingNetbeans) 217 { 218 if (netbeans_is_guarded(top, bot)) 219 { 220 EMSG(_(e_guarded)); 221 return FAIL; 222 } 223 if (curbuf->b_p_ro) 224 { 225 EMSG(_(e_nbreadonly)); 226 return FAIL; 227 } 228 } 229 #endif 230 231 #ifdef FEAT_AUTOCMD 232 /* 233 * Saving text for undo means we are going to make a change. Give a 234 * warning for a read-only file before making the change, so that the 235 * FileChangedRO event can replace the buffer with a read-write version 236 * (e.g., obtained from a source control system). 237 */ 238 change_warning(0); 239 #endif 240 241 size = bot - top - 1; 242 243 /* 244 * if curbuf->b_u_synced == TRUE make a new header 245 */ 246 if (curbuf->b_u_synced) 247 { 248 #ifdef FEAT_JUMPLIST 249 /* Need to create new entry in b_changelist. */ 250 curbuf->b_new_change = TRUE; 251 #endif 252 253 /* 254 * if we undid more than we redid, free the entry lists before and 255 * including curbuf->b_u_curhead 256 */ 257 while (curbuf->b_u_curhead != NULL) 258 u_freelist(curbuf, curbuf->b_u_newhead); 259 260 /* 261 * free headers to keep the size right 262 */ 263 while (curbuf->b_u_numhead > p_ul && curbuf->b_u_oldhead != NULL) 264 u_freelist(curbuf, curbuf->b_u_oldhead); 265 266 if (p_ul < 0) /* no undo at all */ 267 { 268 curbuf->b_u_synced = FALSE; 269 return OK; 270 } 271 272 /* 273 * make a new header entry 274 */ 275 uhp = (struct u_header *)U_ALLOC_LINE((unsigned) 276 sizeof(struct u_header)); 277 if (uhp == NULL) 278 goto nomem; 279 uhp->uh_prev = NULL; 280 uhp->uh_next = curbuf->b_u_newhead; 281 if (curbuf->b_u_newhead != NULL) 282 curbuf->b_u_newhead->uh_prev = uhp; 283 uhp->uh_entry = NULL; 284 uhp->uh_getbot_entry = NULL; 285 uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */ 286 #ifdef FEAT_VIRTUALEDIT 287 if (virtual_active() && curwin->w_cursor.coladd > 0) 288 uhp->uh_cursor_vcol = getviscol(); 289 else 290 uhp->uh_cursor_vcol = -1; 291 #endif 292 293 /* save changed and buffer empty flag for undo */ 294 uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) + 295 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); 296 297 /* save named marks for undo */ 298 mch_memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS); 299 curbuf->b_u_newhead = uhp; 300 if (curbuf->b_u_oldhead == NULL) 301 curbuf->b_u_oldhead = uhp; 302 ++curbuf->b_u_numhead; 303 } 304 else 305 { 306 if (p_ul < 0) /* no undo at all */ 307 return OK; 308 309 /* 310 * When saving a single line, and it has been saved just before, it 311 * doesn't make sense saving it again. Saves a lot of memory when 312 * making lots of changes inside the same line. 313 * This is only possible if the previous change didn't increase or 314 * decrease the number of lines. 315 * Check the ten last changes. More doesn't make sense and takes too 316 * long. 317 */ 318 if (size == 1) 319 { 320 uep = u_get_headentry(); 321 prev_uep = NULL; 322 for (i = 0; i < 10; ++i) 323 { 324 if (uep == NULL) 325 break; 326 327 /* If lines have been inserted/deleted we give up. 328 * Also when the line was included in a multi-line save. */ 329 if ((curbuf->b_u_newhead->uh_getbot_entry != uep 330 ? (uep->ue_top + uep->ue_size + 1 331 != (uep->ue_bot == 0 332 ? curbuf->b_ml.ml_line_count + 1 333 : uep->ue_bot)) 334 : uep->ue_lcount != curbuf->b_ml.ml_line_count) 335 || (uep->ue_size > 1 336 && top >= uep->ue_top 337 && top + 2 <= uep->ue_top + uep->ue_size + 1)) 338 break; 339 340 /* If it's the same line we can skip saving it again. */ 341 if (uep->ue_size == 1 && uep->ue_top == top) 342 { 343 if (i > 0) 344 { 345 /* It's not the last entry: get ue_bot for the last 346 * entry now. Following deleted/inserted lines go to 347 * the re-used entry. */ 348 u_getbot(); 349 curbuf->b_u_synced = FALSE; 350 351 /* Move the found entry to become the last entry. The 352 * order of undo/redo doesn't matter for the entries 353 * we move it over, since they don't change the line 354 * count and don't include this line. It does matter 355 * for the found entry if the line count is changed by 356 * the executed command. */ 357 prev_uep->ue_next = uep->ue_next; 358 uep->ue_next = curbuf->b_u_newhead->uh_entry; 359 curbuf->b_u_newhead->uh_entry = uep; 360 } 361 362 /* The executed command may change the line count. */ 363 if (newbot != 0) 364 uep->ue_bot = newbot; 365 else if (bot > curbuf->b_ml.ml_line_count) 366 uep->ue_bot = 0; 367 else 368 { 369 uep->ue_lcount = curbuf->b_ml.ml_line_count; 370 curbuf->b_u_newhead->uh_getbot_entry = uep; 371 } 372 return OK; 373 } 374 prev_uep = uep; 375 uep = uep->ue_next; 376 } 377 } 378 379 /* find line number for ue_bot for previous u_save() */ 380 u_getbot(); 381 } 382 383 #if !defined(UNIX) && !defined(DJGPP) && !defined(WIN32) && !defined(__EMX__) 384 /* 385 * With Amiga and MSDOS 16 bit we can't handle big undo's, because 386 * then u_alloc_line would have to allocate a block larger than 32K 387 */ 388 if (size >= 8000) 389 goto nomem; 390 #endif 391 392 /* 393 * add lines in front of entry list 394 */ 395 uep = (u_entry_T *)U_ALLOC_LINE((unsigned)sizeof(u_entry_T)); 396 if (uep == NULL) 397 goto nomem; 398 399 uep->ue_size = size; 400 uep->ue_top = top; 401 if (newbot != 0) 402 uep->ue_bot = newbot; 403 /* 404 * Use 0 for ue_bot if bot is below last line. 405 * Otherwise we have to compute ue_bot later. 406 */ 407 else if (bot > curbuf->b_ml.ml_line_count) 408 uep->ue_bot = 0; 409 else 410 { 411 uep->ue_lcount = curbuf->b_ml.ml_line_count; 412 curbuf->b_u_newhead->uh_getbot_entry = uep; 413 } 414 415 if (size > 0) 416 { 417 if ((uep->ue_array = (char_u **)U_ALLOC_LINE( 418 (unsigned)(sizeof(char_u *) * size))) == NULL) 419 { 420 u_freeentry(uep, 0L); 421 goto nomem; 422 } 423 for (i = 0, lnum = top + 1; i < size; ++i) 424 { 425 fast_breakcheck(); 426 if (got_int) 427 { 428 u_freeentry(uep, i); 429 return FAIL; 430 } 431 if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL) 432 { 433 u_freeentry(uep, i); 434 goto nomem; 435 } 436 } 437 } 438 else 439 uep->ue_array = NULL; 440 uep->ue_next = curbuf->b_u_newhead->uh_entry; 441 curbuf->b_u_newhead->uh_entry = uep; 442 curbuf->b_u_synced = FALSE; 443 undo_undoes = FALSE; 444 445 return OK; 446 447 nomem: 448 msg_silent = 0; /* must display the prompt */ 449 if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE) 450 == 'y') 451 { 452 undo_off = TRUE; /* will be reset when character typed */ 453 return OK; 454 } 455 do_outofmem_msg((long_u)0); 456 return FAIL; 457 } 458 459 /* 460 * If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible). 461 * If 'cpoptions' does not contain 'u': Always undo. 462 */ 463 void 464 u_undo(count) 465 int count; 466 { 467 /* 468 * If we get an undo command while executing a macro, we behave like the 469 * original vi. If this happens twice in one macro the result will not 470 * be compatible. 471 */ 472 if (curbuf->b_u_synced == FALSE) 473 { 474 u_sync(); 475 count = 1; 476 } 477 478 if (vim_strchr(p_cpo, CPO_UNDO) == NULL) 479 undo_undoes = TRUE; 480 else 481 undo_undoes = !undo_undoes; 482 u_doit(count); 483 } 484 485 /* 486 * If 'cpoptions' contains 'u': Repeat the previous undo or redo. 487 * If 'cpoptions' does not contain 'u': Always redo. 488 */ 489 void 490 u_redo(count) 491 int count; 492 { 493 if (vim_strchr(p_cpo, CPO_UNDO) == NULL) 494 undo_undoes = FALSE; 495 u_doit(count); 496 } 497 498 /* 499 * Undo or redo, depending on 'undo_undoes', 'count' times. 500 */ 501 static void 502 u_doit(count) 503 int count; 504 { 505 if (!undo_allowed()) 506 return; 507 508 u_newcount = 0; 509 u_oldcount = 0; 510 if (curbuf->b_ml.ml_flags & ML_EMPTY) 511 u_oldcount = -1; 512 while (count--) 513 { 514 if (undo_undoes) 515 { 516 if (curbuf->b_u_curhead == NULL) /* first undo */ 517 curbuf->b_u_curhead = curbuf->b_u_newhead; 518 else if (p_ul > 0) /* multi level undo */ 519 /* get next undo */ 520 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_next; 521 /* nothing to undo */ 522 if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL) 523 { 524 /* stick curbuf->b_u_curhead at end */ 525 curbuf->b_u_curhead = curbuf->b_u_oldhead; 526 beep_flush(); 527 break; 528 } 529 530 u_undoredo(); 531 } 532 else 533 { 534 if (curbuf->b_u_curhead == NULL || p_ul <= 0) 535 { 536 beep_flush(); /* nothing to redo */ 537 break; 538 } 539 540 u_undoredo(); 541 /* advance for next redo */ 542 curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev; 543 } 544 } 545 if (curbuf->b_ml.ml_flags & ML_EMPTY) 546 --u_newcount; 547 u_undo_end(); 548 } 549 550 /* 551 * u_undoredo: common code for undo and redo 552 * 553 * The lines in the file are replaced by the lines in the entry list at 554 * curbuf->b_u_curhead. The replaced lines in the file are saved in the entry 555 * list for the next undo/redo. 556 */ 557 static void 558 u_undoredo() 559 { 560 char_u **newarray = NULL; 561 linenr_T oldsize; 562 linenr_T newsize; 563 linenr_T top, bot; 564 linenr_T lnum; 565 linenr_T newlnum = MAXLNUM; 566 long i; 567 u_entry_T *uep, *nuep; 568 u_entry_T *newlist = NULL; 569 int old_flags; 570 int new_flags; 571 pos_T namedm[NMARKS]; 572 int empty_buffer; /* buffer became empty */ 573 574 old_flags = curbuf->b_u_curhead->uh_flags; 575 new_flags = (curbuf->b_changed ? UH_CHANGED : 0) + 576 ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); 577 setpcmark(); 578 579 /* 580 * save marks before undo/redo 581 */ 582 mch_memmove(namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS); 583 curbuf->b_op_start.lnum = curbuf->b_ml.ml_line_count; 584 curbuf->b_op_start.col = 0; 585 curbuf->b_op_end.lnum = 0; 586 curbuf->b_op_end.col = 0; 587 588 for (uep = curbuf->b_u_curhead->uh_entry; uep != NULL; uep = nuep) 589 { 590 top = uep->ue_top; 591 bot = uep->ue_bot; 592 if (bot == 0) 593 bot = curbuf->b_ml.ml_line_count + 1; 594 if (top > curbuf->b_ml.ml_line_count || top >= bot 595 || bot > curbuf->b_ml.ml_line_count + 1) 596 { 597 EMSG(_("E438: u_undo: line numbers wrong")); 598 changed(); /* don't want UNCHANGED now */ 599 return; 600 } 601 602 oldsize = bot - top - 1; /* number of lines before undo */ 603 newsize = uep->ue_size; /* number of lines after undo */ 604 605 if (top < newlnum) 606 { 607 /* If the saved cursor is somewhere in this undo block, move it to 608 * the remembered position. Makes "gwap" put the cursor back 609 * where it was. */ 610 lnum = curbuf->b_u_curhead->uh_cursor.lnum; 611 if (lnum >= top && lnum <= top + newsize + 1) 612 { 613 curwin->w_cursor = curbuf->b_u_curhead->uh_cursor; 614 newlnum = curwin->w_cursor.lnum - 1; 615 } 616 else 617 { 618 /* Use the first line that actually changed. Avoids that 619 * undoing auto-formatting puts the cursor in the previous 620 * line. */ 621 for (i = 0; i < newsize && i < oldsize; ++i) 622 if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0) 623 break; 624 if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL) 625 { 626 newlnum = top; 627 curwin->w_cursor.lnum = newlnum + 1; 628 } 629 else if (i < newsize) 630 { 631 newlnum = top + i; 632 curwin->w_cursor.lnum = newlnum + 1; 633 } 634 } 635 } 636 637 empty_buffer = FALSE; 638 639 /* delete the lines between top and bot and save them in newarray */ 640 if (oldsize > 0) 641 { 642 if ((newarray = (char_u **)U_ALLOC_LINE( 643 (unsigned)(sizeof(char_u *) * oldsize))) == NULL) 644 { 645 do_outofmem_msg((long_u)(sizeof(char_u *) * oldsize)); 646 /* 647 * We have messed up the entry list, repair is impossible. 648 * we have to free the rest of the list. 649 */ 650 while (uep != NULL) 651 { 652 nuep = uep->ue_next; 653 u_freeentry(uep, uep->ue_size); 654 uep = nuep; 655 } 656 break; 657 } 658 /* delete backwards, it goes faster in most cases */ 659 for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum) 660 { 661 /* what can we do when we run out of memory? */ 662 if ((newarray[i] = u_save_line(lnum)) == NULL) 663 do_outofmem_msg((long_u)0); 664 /* remember we deleted the last line in the buffer, and a 665 * dummy empty line will be inserted */ 666 if (curbuf->b_ml.ml_line_count == 1) 667 empty_buffer = TRUE; 668 ml_delete(lnum, FALSE); 669 } 670 } 671 else 672 newarray = NULL; 673 674 /* insert the lines in u_array between top and bot */ 675 if (newsize) 676 { 677 for (lnum = top, i = 0; i < newsize; ++i, ++lnum) 678 { 679 /* 680 * If the file is empty, there is an empty line 1 that we 681 * should get rid of, by replacing it with the new line 682 */ 683 if (empty_buffer && lnum == 0) 684 ml_replace((linenr_T)1, uep->ue_array[i], TRUE); 685 else 686 ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE); 687 U_FREE_LINE(uep->ue_array[i]); 688 } 689 U_FREE_LINE((char_u *)uep->ue_array); 690 } 691 692 /* adjust marks */ 693 if (oldsize != newsize) 694 { 695 mark_adjust(top + 1, top + oldsize, (long)MAXLNUM, 696 (long)newsize - (long)oldsize); 697 if (curbuf->b_op_start.lnum > top + oldsize) 698 curbuf->b_op_start.lnum += newsize - oldsize; 699 if (curbuf->b_op_end.lnum > top + oldsize) 700 curbuf->b_op_end.lnum += newsize - oldsize; 701 } 702 703 changed_lines(top + 1, 0, bot, newsize - oldsize); 704 705 /* set '[ and '] mark */ 706 if (top + 1 < curbuf->b_op_start.lnum) 707 curbuf->b_op_start.lnum = top + 1; 708 if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum) 709 curbuf->b_op_end.lnum = top + 1; 710 else if (top + newsize > curbuf->b_op_end.lnum) 711 curbuf->b_op_end.lnum = top + newsize; 712 713 u_newcount += newsize; 714 u_oldcount += oldsize; 715 uep->ue_size = oldsize; 716 uep->ue_array = newarray; 717 uep->ue_bot = top + newsize + 1; 718 719 /* 720 * insert this entry in front of the new entry list 721 */ 722 nuep = uep->ue_next; 723 uep->ue_next = newlist; 724 newlist = uep; 725 } 726 727 curbuf->b_u_curhead->uh_entry = newlist; 728 curbuf->b_u_curhead->uh_flags = new_flags; 729 if ((old_flags & UH_EMPTYBUF) && bufempty()) 730 curbuf->b_ml.ml_flags |= ML_EMPTY; 731 if (old_flags & UH_CHANGED) 732 changed(); 733 else 734 #ifdef FEAT_NETBEANS_INTG 735 /* per netbeans undo rules, keep it as modified */ 736 if (!isNetbeansModified(curbuf)) 737 #endif 738 unchanged(curbuf, FALSE); 739 740 /* 741 * restore marks from before undo/redo 742 */ 743 for (i = 0; i < NMARKS; ++i) 744 if (curbuf->b_u_curhead->uh_namedm[i].lnum) 745 { 746 curbuf->b_namedm[i] = curbuf->b_u_curhead->uh_namedm[i]; 747 curbuf->b_u_curhead->uh_namedm[i] = namedm[i]; 748 } 749 750 /* 751 * If the cursor is only off by one line, put it at the same position as 752 * before starting the change (for the "o" command). 753 * Otherwise the cursor should go to the first undone line. 754 */ 755 if (curbuf->b_u_curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum 756 && curwin->w_cursor.lnum > 1) 757 --curwin->w_cursor.lnum; 758 if (curbuf->b_u_curhead->uh_cursor.lnum == curwin->w_cursor.lnum) 759 { 760 curwin->w_cursor.col = curbuf->b_u_curhead->uh_cursor.col; 761 #ifdef FEAT_VIRTUALEDIT 762 if (virtual_active() && curbuf->b_u_curhead->uh_cursor_vcol >= 0) 763 coladvance((colnr_T)curbuf->b_u_curhead->uh_cursor_vcol); 764 else 765 curwin->w_cursor.coladd = 0; 766 #endif 767 } 768 else if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count) 769 beginline(BL_SOL | BL_FIX); 770 else 771 { 772 /* We get here with the current cursor line being past the end (eg 773 * after adding lines at the end of the file, and then undoing it). 774 * check_cursor() will move the cursor to the last line. Move it to 775 * the first column here. */ 776 curwin->w_cursor.col = 0; 777 #ifdef FEAT_VIRTUALEDIT 778 curwin->w_cursor.coladd = 0; 779 #endif 780 } 781 782 /* Make sure the cursor is on an existing line and column. */ 783 check_cursor(); 784 } 785 786 /* 787 * If we deleted or added lines, report the number of less/more lines. 788 * Otherwise, report the number of changes (this may be incorrect 789 * in some cases, but it's better than nothing). 790 */ 791 static void 792 u_undo_end() 793 { 794 if ((u_oldcount -= u_newcount) != 0) 795 msgmore(-u_oldcount); 796 else if (u_newcount > p_report) 797 { 798 if (u_newcount == 1) 799 MSG(_("1 change")); 800 else 801 smsg((char_u *)_("%ld changes"), u_newcount); 802 } 803 #ifdef FEAT_FOLDING 804 if ((fdo_flags & FDO_UNDO) && KeyTyped) 805 foldOpenCursor(); 806 #endif 807 } 808 809 /* 810 * u_sync: stop adding to the current entry list 811 */ 812 void 813 u_sync() 814 { 815 if (curbuf->b_u_synced) 816 return; /* already synced */ 817 #if defined(FEAT_XIM) && defined(FEAT_GUI_GTK) 818 if (im_is_preediting()) 819 return; /* XIM is busy, don't break an undo sequence */ 820 #endif 821 if (p_ul < 0) 822 curbuf->b_u_synced = TRUE; /* no entries, nothing to do */ 823 else 824 { 825 u_getbot(); /* compute ue_bot of previous u_save */ 826 curbuf->b_u_curhead = NULL; 827 } 828 } 829 830 /* 831 * Called after writing the file and setting b_changed to FALSE. 832 * Now an undo means that the buffer is modified. 833 */ 834 void 835 u_unchanged(buf) 836 buf_T *buf; 837 { 838 struct u_header *uh; 839 840 for (uh = buf->b_u_newhead; uh; uh = uh->uh_next) 841 uh->uh_flags |= UH_CHANGED; 842 buf->b_did_warn = FALSE; 843 } 844 845 /* 846 * Get pointer to last added entry. 847 * If it's not valid, give an error message and return NULL. 848 */ 849 static u_entry_T * 850 u_get_headentry() 851 { 852 if (curbuf->b_u_newhead == NULL || curbuf->b_u_newhead->uh_entry == NULL) 853 { 854 EMSG(_("E439: undo list corrupt")); 855 return NULL; 856 } 857 return curbuf->b_u_newhead->uh_entry; 858 } 859 860 /* 861 * u_getbot(): compute the line number of the previous u_save 862 * It is called only when b_u_synced is FALSE. 863 */ 864 static void 865 u_getbot() 866 { 867 u_entry_T *uep; 868 linenr_T extra; 869 870 uep = u_get_headentry(); /* check for corrupt undo list */ 871 if (uep == NULL) 872 return; 873 874 uep = curbuf->b_u_newhead->uh_getbot_entry; 875 if (uep != NULL) 876 { 877 /* 878 * the new ue_bot is computed from the number of lines that has been 879 * inserted (0 - deleted) since calling u_save. This is equal to the 880 * old line count subtracted from the current line count. 881 */ 882 extra = curbuf->b_ml.ml_line_count - uep->ue_lcount; 883 uep->ue_bot = uep->ue_top + uep->ue_size + 1 + extra; 884 if (uep->ue_bot < 1 || uep->ue_bot > curbuf->b_ml.ml_line_count) 885 { 886 EMSG(_("E440: undo line missing")); 887 uep->ue_bot = uep->ue_top + 1; /* assume all lines deleted, will 888 * get all the old lines back 889 * without deleting the current 890 * ones */ 891 } 892 893 curbuf->b_u_newhead->uh_getbot_entry = NULL; 894 } 895 896 curbuf->b_u_synced = TRUE; 897 } 898 899 /* 900 * u_freelist: free one entry list and adjust the pointers 901 */ 902 static void 903 u_freelist(buf, uhp) 904 buf_T *buf; 905 struct u_header *uhp; 906 { 907 u_entry_T *uep, *nuep; 908 909 for (uep = uhp->uh_entry; uep != NULL; uep = nuep) 910 { 911 nuep = uep->ue_next; 912 u_freeentry(uep, uep->ue_size); 913 } 914 915 if (buf->b_u_curhead == uhp) 916 buf->b_u_curhead = NULL; 917 918 if (uhp->uh_next == NULL) 919 buf->b_u_oldhead = uhp->uh_prev; 920 else 921 uhp->uh_next->uh_prev = uhp->uh_prev; 922 923 if (uhp->uh_prev == NULL) 924 buf->b_u_newhead = uhp->uh_next; 925 else 926 uhp->uh_prev->uh_next = uhp->uh_next; 927 928 U_FREE_LINE((char_u *)uhp); 929 --buf->b_u_numhead; 930 } 931 932 /* 933 * free entry 'uep' and 'n' lines in uep->ue_array[] 934 */ 935 static void 936 u_freeentry(uep, n) 937 u_entry_T *uep; 938 long n; 939 { 940 while (n > 0) 941 U_FREE_LINE(uep->ue_array[--n]); 942 U_FREE_LINE((char_u *)uep->ue_array); 943 U_FREE_LINE((char_u *)uep); 944 } 945 946 /* 947 * invalidate the undo buffer; called when storage has already been released 948 */ 949 void 950 u_clearall(buf) 951 buf_T *buf; 952 { 953 buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL; 954 buf->b_u_synced = TRUE; 955 buf->b_u_numhead = 0; 956 buf->b_u_line_ptr = NULL; 957 buf->b_u_line_lnum = 0; 958 } 959 960 /* 961 * save the line "lnum" for the "U" command 962 */ 963 void 964 u_saveline(lnum) 965 linenr_T lnum; 966 { 967 if (lnum == curbuf->b_u_line_lnum) /* line is already saved */ 968 return; 969 if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */ 970 return; 971 u_clearline(); 972 curbuf->b_u_line_lnum = lnum; 973 if (curwin->w_cursor.lnum == lnum) 974 curbuf->b_u_line_colnr = curwin->w_cursor.col; 975 else 976 curbuf->b_u_line_colnr = 0; 977 if ((curbuf->b_u_line_ptr = u_save_line(lnum)) == NULL) 978 do_outofmem_msg((long_u)0); 979 } 980 981 /* 982 * clear the line saved for the "U" command 983 * (this is used externally for crossing a line while in insert mode) 984 */ 985 void 986 u_clearline() 987 { 988 if (curbuf->b_u_line_ptr != NULL) 989 { 990 U_FREE_LINE(curbuf->b_u_line_ptr); 991 curbuf->b_u_line_ptr = NULL; 992 curbuf->b_u_line_lnum = 0; 993 } 994 } 995 996 /* 997 * Implementation of the "U" command. 998 * Differentiation from vi: "U" can be undone with the next "U". 999 * We also allow the cursor to be in another line. 1000 */ 1001 void 1002 u_undoline() 1003 { 1004 colnr_T t; 1005 char_u *oldp; 1006 1007 if (undo_off) 1008 return; 1009 1010 if (curbuf->b_u_line_ptr == NULL || 1011 curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count) 1012 { 1013 beep_flush(); 1014 return; 1015 } 1016 /* first save the line for the 'u' command */ 1017 if (u_savecommon(curbuf->b_u_line_lnum - 1, 1018 curbuf->b_u_line_lnum + 1, (linenr_T)0) == FAIL) 1019 return; 1020 oldp = u_save_line(curbuf->b_u_line_lnum); 1021 if (oldp == NULL) 1022 { 1023 do_outofmem_msg((long_u)0); 1024 return; 1025 } 1026 ml_replace(curbuf->b_u_line_lnum, curbuf->b_u_line_ptr, TRUE); 1027 changed_bytes(curbuf->b_u_line_lnum, 0); 1028 U_FREE_LINE(curbuf->b_u_line_ptr); 1029 curbuf->b_u_line_ptr = oldp; 1030 1031 t = curbuf->b_u_line_colnr; 1032 if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum) 1033 curbuf->b_u_line_colnr = curwin->w_cursor.col; 1034 curwin->w_cursor.col = t; 1035 curwin->w_cursor.lnum = curbuf->b_u_line_lnum; 1036 } 1037 1038 /* 1039 * There are two implementations of the memory management for undo: 1040 * 1. Use the standard malloc()/free() functions. 1041 * This should be fast for allocating memory, but when a buffer is 1042 * abandoned every single allocated chunk must be freed, which may be slow. 1043 * 2. Allocate larger blocks of memory and keep track of chunks ourselves. 1044 * This is fast for abandoning, but the use of linked lists is slow for 1045 * finding a free chunk. Esp. when a lot of lines are changed or deleted. 1046 * A bit of profiling showed that the first method is faster, especially when 1047 * making a large number of changes, under the condition that malloc()/free() 1048 * is implemented efficiently. 1049 */ 1050 #ifdef U_USE_MALLOC 1051 /* 1052 * Version of undo memory allocation using malloc()/free() 1053 * 1054 * U_FREE_LINE() and U_ALLOC_LINE() are macros that invoke vim_free() and 1055 * lalloc() directly. 1056 */ 1057 1058 /* 1059 * Free all allocated memory blocks for the buffer 'buf'. 1060 */ 1061 void 1062 u_blockfree(buf) 1063 buf_T *buf; 1064 { 1065 while (buf->b_u_newhead != NULL) 1066 u_freelist(buf, buf->b_u_newhead); 1067 U_FREE_LINE(buf->b_u_line_ptr); 1068 } 1069 1070 #else 1071 /* 1072 * Storage allocation for the undo lines and blocks of the current file. 1073 * Version where Vim keeps track of the available memory. 1074 */ 1075 1076 /* 1077 * Memory is allocated in relatively large blocks. These blocks are linked 1078 * in the allocated block list, headed by curbuf->b_block_head. They are all 1079 * freed when abandoning a file, so we don't have to free every single line. 1080 * The list is kept sorted on memory address. 1081 * block_alloc() allocates a block. 1082 * m_blockfree() frees all blocks. 1083 * 1084 * The available chunks of memory are kept in free chunk lists. There is 1085 * one free list for each block of allocated memory. The list is kept sorted 1086 * on memory address. 1087 * u_alloc_line() gets a chunk from the free lists. 1088 * u_free_line() returns a chunk to the free lists. 1089 * curbuf->b_m_search points to the chunk before the chunk that was 1090 * freed/allocated the last time. 1091 * curbuf->b_mb_current points to the b_head where curbuf->b_m_search 1092 * points into the free list. 1093 * 1094 * 1095 * b_block_head /---> block #1 /---> block #2 1096 * mb_next ---/ mb_next ---/ mb_next ---> NULL 1097 * mb_info mb_info mb_info 1098 * | | | 1099 * V V V 1100 * NULL free chunk #1.1 free chunk #2.1 1101 * | | 1102 * V V 1103 * free chunk #1.2 NULL 1104 * | 1105 * V 1106 * NULL 1107 * 1108 * When a single free chunk list would have been used, it could take a lot 1109 * of time in u_free_line() to find the correct place to insert a chunk in the 1110 * free list. The single free list would become very long when many lines are 1111 * changed (e.g. with :%s/^M$//). 1112 */ 1113 1114 /* 1115 * this blocksize is used when allocating new lines 1116 */ 1117 #define MEMBLOCKSIZE 2044 1118 1119 /* 1120 * The size field contains the size of the chunk, including the size field 1121 * itself. 1122 * 1123 * When the chunk is not in-use it is preceded with the m_info structure. 1124 * The m_next field links it in one of the free chunk lists. 1125 * 1126 * On most unix systems structures have to be longword (32 or 64 bit) aligned. 1127 * On most other systems they are short (16 bit) aligned. 1128 */ 1129 1130 /* the structure definitions are now in structs.h */ 1131 1132 #ifdef ALIGN_LONG 1133 /* size of m_size */ 1134 # define M_OFFSET (sizeof(long_u)) 1135 #else 1136 /* size of m_size */ 1137 # define M_OFFSET (sizeof(short_u)) 1138 #endif 1139 1140 static char_u *u_blockalloc __ARGS((long_u)); 1141 1142 /* 1143 * Allocate a block of memory and link it in the allocated block list. 1144 */ 1145 static char_u * 1146 u_blockalloc(size) 1147 long_u size; 1148 { 1149 mblock_T *p; 1150 mblock_T *mp, *next; 1151 1152 p = (mblock_T *)lalloc(size + sizeof(mblock_T), FALSE); 1153 if (p != NULL) 1154 { 1155 /* Insert the block into the allocated block list, keeping it 1156 sorted on address. */ 1157 for (mp = &curbuf->b_block_head; 1158 (next = mp->mb_next) != NULL && next < p; 1159 mp = next) 1160 ; 1161 p->mb_next = next; /* link in block list */ 1162 p->mb_size = size; 1163 p->mb_maxsize = 0; /* nothing free yet */ 1164 mp->mb_next = p; 1165 p->mb_info.m_next = NULL; /* clear free list */ 1166 p->mb_info.m_size = 0; 1167 curbuf->b_mb_current = p; /* remember current block */ 1168 curbuf->b_m_search = NULL; 1169 p++; /* return usable memory */ 1170 } 1171 return (char_u *)p; 1172 } 1173 1174 /* 1175 * free all allocated memory blocks for the buffer 'buf' 1176 */ 1177 void 1178 u_blockfree(buf) 1179 buf_T *buf; 1180 { 1181 mblock_T *p, *np; 1182 1183 for (p = buf->b_block_head.mb_next; p != NULL; p = np) 1184 { 1185 np = p->mb_next; 1186 vim_free(p); 1187 } 1188 buf->b_block_head.mb_next = NULL; 1189 buf->b_m_search = NULL; 1190 buf->b_mb_current = NULL; 1191 } 1192 1193 /* 1194 * Free a chunk of memory for the current buffer. 1195 * Insert the chunk into the correct free list, keeping it sorted on address. 1196 */ 1197 static void 1198 u_free_line(ptr, keep) 1199 char_u *ptr; 1200 int keep; /* don't free the block when it's empty */ 1201 { 1202 minfo_T *next; 1203 minfo_T *prev, *curr; 1204 minfo_T *mp; 1205 mblock_T *nextb; 1206 mblock_T *prevb; 1207 long_u maxsize; 1208 1209 if (ptr == NULL || ptr == IObuff) 1210 return; /* illegal address can happen in out-of-memory situations */ 1211 1212 mp = (minfo_T *)(ptr - M_OFFSET); 1213 1214 /* find block where chunk could be a part off */ 1215 /* if we change curbuf->b_mb_current, curbuf->b_m_search is set to NULL */ 1216 if (curbuf->b_mb_current == NULL || mp < (minfo_T *)curbuf->b_mb_current) 1217 { 1218 curbuf->b_mb_current = curbuf->b_block_head.mb_next; 1219 curbuf->b_m_search = NULL; 1220 } 1221 if ((nextb = curbuf->b_mb_current->mb_next) != NULL 1222 && (minfo_T *)nextb < mp) 1223 { 1224 curbuf->b_mb_current = nextb; 1225 curbuf->b_m_search = NULL; 1226 } 1227 while ((nextb = curbuf->b_mb_current->mb_next) != NULL 1228 && (minfo_T *)nextb < mp) 1229 curbuf->b_mb_current = nextb; 1230 1231 curr = NULL; 1232 /* 1233 * If mp is smaller than curbuf->b_m_search->m_next go to the start of 1234 * the free list 1235 */ 1236 if (curbuf->b_m_search == NULL || mp < (curbuf->b_m_search->m_next)) 1237 next = &(curbuf->b_mb_current->mb_info); 1238 else 1239 next = curbuf->b_m_search; 1240 /* 1241 * The following loop is executed very often. 1242 * Therefore it has been optimized at the cost of readability. 1243 * Keep it fast! 1244 */ 1245 #ifdef SLOW_BUT_EASY_TO_READ 1246 do 1247 { 1248 prev = curr; 1249 curr = next; 1250 next = next->m_next; 1251 } 1252 while (mp > next && next != NULL); 1253 #else 1254 do /* first, middle, last */ 1255 { 1256 prev = next->m_next; /* curr, next, prev */ 1257 if (prev == NULL || mp <= prev) 1258 { 1259 prev = curr; 1260 curr = next; 1261 next = next->m_next; 1262 break; 1263 } 1264 curr = prev->m_next; /* next, prev, curr */ 1265 if (curr == NULL || mp <= curr) 1266 { 1267 prev = next; 1268 curr = prev->m_next; 1269 next = curr->m_next; 1270 break; 1271 } 1272 next = curr->m_next; /* prev, curr, next */ 1273 } 1274 while (mp > next && next != NULL); 1275 #endif 1276 1277 /* if *mp and *next are concatenated, join them into one chunk */ 1278 if ((char_u *)mp + mp->m_size == (char_u *)next) 1279 { 1280 mp->m_size += next->m_size; 1281 mp->m_next = next->m_next; 1282 } 1283 else 1284 mp->m_next = next; 1285 maxsize = mp->m_size; 1286 1287 /* if *curr and *mp are concatenated, join them */ 1288 if (prev != NULL && (char_u *)curr + curr->m_size == (char_u *)mp) 1289 { 1290 curr->m_size += mp->m_size; 1291 maxsize = curr->m_size; 1292 curr->m_next = mp->m_next; 1293 curbuf->b_m_search = prev; 1294 } 1295 else 1296 { 1297 curr->m_next = mp; 1298 curbuf->b_m_search = curr; /* put curbuf->b_m_search before freed 1299 chunk */ 1300 } 1301 1302 /* 1303 * If the block only containes free memory now, release it. 1304 */ 1305 if (!keep && curbuf->b_mb_current->mb_size 1306 == curbuf->b_mb_current->mb_info.m_next->m_size) 1307 { 1308 /* Find the block before the current one to be able to unlink it from 1309 * the list of blocks. */ 1310 prevb = &curbuf->b_block_head; 1311 for (nextb = prevb->mb_next; nextb != curbuf->b_mb_current; 1312 nextb = nextb->mb_next) 1313 prevb = nextb; 1314 prevb->mb_next = nextb->mb_next; 1315 vim_free(nextb); 1316 curbuf->b_mb_current = NULL; 1317 curbuf->b_m_search = NULL; 1318 } 1319 else if (curbuf->b_mb_current->mb_maxsize < maxsize) 1320 curbuf->b_mb_current->mb_maxsize = maxsize; 1321 } 1322 1323 /* 1324 * Allocate and initialize a new line structure with room for at least 1325 * 'size' characters plus a terminating NUL. 1326 */ 1327 static char_u * 1328 u_alloc_line(size) 1329 unsigned size; 1330 { 1331 minfo_T *mp, *mprev, *mp2; 1332 mblock_T *mbp; 1333 int size_align; 1334 1335 /* 1336 * Add room for size field and trailing NUL byte. 1337 * Adjust for minimal size (must be able to store minfo_T 1338 * plus a trailing NUL, so the chunk can be released again) 1339 */ 1340 size += M_OFFSET + 1; 1341 if (size < sizeof(minfo_T) + 1) 1342 size = sizeof(minfo_T) + 1; 1343 1344 /* 1345 * round size up for alignment 1346 */ 1347 size_align = (size + ALIGN_MASK) & ~ALIGN_MASK; 1348 1349 /* 1350 * If curbuf->b_m_search is NULL (uninitialized free list) start at 1351 * curbuf->b_block_head 1352 */ 1353 if (curbuf->b_mb_current == NULL || curbuf->b_m_search == NULL) 1354 { 1355 curbuf->b_mb_current = &curbuf->b_block_head; 1356 curbuf->b_m_search = &(curbuf->b_block_head.mb_info); 1357 } 1358 1359 /* Search for a block with enough space. */ 1360 mbp = curbuf->b_mb_current; 1361 while (mbp->mb_maxsize < size_align) 1362 { 1363 if (mbp->mb_next != NULL) 1364 mbp = mbp->mb_next; 1365 else 1366 mbp = &curbuf->b_block_head; 1367 if (mbp == curbuf->b_mb_current) 1368 { 1369 int n = (size_align > (MEMBLOCKSIZE / 4) 1370 ? size_align : MEMBLOCKSIZE); 1371 1372 /* Back where we started in block list: need to add a new block 1373 * with enough space. */ 1374 mp = (minfo_T *)u_blockalloc((long_u)n); 1375 if (mp == NULL) 1376 return (NULL); 1377 mp->m_size = n; 1378 u_free_line((char_u *)mp + M_OFFSET, TRUE); 1379 mbp = curbuf->b_mb_current; 1380 break; 1381 } 1382 } 1383 if (mbp != curbuf->b_mb_current) 1384 curbuf->b_m_search = &(mbp->mb_info); 1385 1386 /* In this block find a chunk with enough space. */ 1387 mprev = curbuf->b_m_search; 1388 mp = curbuf->b_m_search->m_next; 1389 for (;;) 1390 { 1391 if (mp == NULL) /* at end of the list */ 1392 mp = &(mbp->mb_info); /* wrap around to begin */ 1393 if (mp->m_size >= size) 1394 break; 1395 if (mp == curbuf->b_m_search) 1396 { 1397 /* back where we started in free chunk list: "cannot happen" */ 1398 EMSG2(_(e_intern2), "u_alloc_line()"); 1399 return NULL; 1400 } 1401 mprev = mp; 1402 mp = mp->m_next; 1403 } 1404 1405 /* when using the largest chunk adjust mb_maxsize */ 1406 if (mp->m_size >= mbp->mb_maxsize) 1407 mbp->mb_maxsize = 0; 1408 1409 /* if the chunk we found is large enough, split it up in two */ 1410 if ((long)mp->m_size - size_align >= (long)(sizeof(minfo_T) + 1)) 1411 { 1412 mp2 = (minfo_T *)((char_u *)mp + size_align); 1413 mp2->m_size = mp->m_size - size_align; 1414 mp2->m_next = mp->m_next; 1415 mprev->m_next = mp2; 1416 mp->m_size = size_align; 1417 } 1418 else /* remove *mp from the free list */ 1419 { 1420 mprev->m_next = mp->m_next; 1421 } 1422 curbuf->b_m_search = mprev; 1423 curbuf->b_mb_current = mbp; 1424 1425 /* If using the largest chunk need to find the new largest chunk */ 1426 if (mbp->mb_maxsize == 0) 1427 for (mp2 = &(mbp->mb_info); mp2 != NULL; mp2 = mp2->m_next) 1428 if (mbp->mb_maxsize < mp2->m_size) 1429 mbp->mb_maxsize = mp2->m_size; 1430 1431 mp = (minfo_T *)((char_u *)mp + M_OFFSET); 1432 *(char_u *)mp = NUL; /* set the first byte to NUL */ 1433 1434 return ((char_u *)mp); 1435 } 1436 #endif 1437 1438 /* 1439 * u_save_line(): allocate memory with u_alloc_line() and copy line 'lnum' 1440 * into it. 1441 */ 1442 static char_u * 1443 u_save_line(lnum) 1444 linenr_T lnum; 1445 { 1446 char_u *src; 1447 char_u *dst; 1448 unsigned len; 1449 1450 src = ml_get(lnum); 1451 len = (unsigned)STRLEN(src); 1452 if ((dst = U_ALLOC_LINE(len)) != NULL) 1453 mch_memmove(dst, src, (size_t)(len + 1)); 1454 return (dst); 1455 } 1456 1457 /* 1458 * Check if the 'modified' flag is set, or 'ff' has changed (only need to 1459 * check the first character, because it can only be "dos", "unix" or "mac"). 1460 * "nofile" and "scratch" type buffers are considered to always be unchanged. 1461 */ 1462 int 1463 bufIsChanged(buf) 1464 buf_T *buf; 1465 { 1466 return 1467 #ifdef FEAT_QUICKFIX 1468 !bt_dontwrite(buf) && 1469 #endif 1470 (buf->b_changed || file_ff_differs(buf)); 1471 } 1472 1473 int 1474 curbufIsChanged() 1475 { 1476 return 1477 #ifdef FEAT_QUICKFIX 1478 !bt_dontwrite(curbuf) && 1479 #endif 1480 (curbuf->b_changed || file_ff_differs(curbuf)); 1481 } 1482