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 * mark.c: functions for setting marks and jumping to them 12 */ 13 14 #include "vim.h" 15 16 /* 17 * This file contains routines to maintain and manipulate marks. 18 */ 19 20 /* 21 * If a named file mark's lnum is non-zero, it is valid. 22 * If a named file mark's fnum is non-zero, it is for an existing buffer, 23 * otherwise it is from .viminfo and namedfm[n].fname is the file name. 24 * There are marks 'A - 'Z (set by user) and '0 to '9 (set when writing 25 * viminfo). 26 */ 27 #define EXTRA_MARKS 10 /* marks 0-9 */ 28 static xfmark_T namedfm[NMARKS + EXTRA_MARKS]; /* marks with file nr */ 29 30 static void fname2fnum __ARGS((xfmark_T *fm)); 31 static void fmarks_check_one __ARGS((xfmark_T *fm, char_u *name, buf_T *buf)); 32 static char_u *mark_line __ARGS((pos_T *mp, int lead_len)); 33 static void show_one_mark __ARGS((int, char_u *, pos_T *, char_u *, int current)); 34 #ifdef FEAT_JUMPLIST 35 static void cleanup_jumplist __ARGS((void)); 36 #endif 37 #ifdef FEAT_VIMINFO 38 static void write_one_filemark __ARGS((FILE *fp, xfmark_T *fm, int c1, int c2)); 39 #endif 40 41 /* 42 * Set named mark 'c' at current cursor position. 43 * Returns OK on success, FAIL if bad name given. 44 */ 45 int 46 setmark(c) 47 int c; 48 { 49 int i; 50 51 /* Check for a special key (may cause islower() to crash). */ 52 if (c < 0) 53 return FAIL; 54 55 if (c == '\'' || c == '`') 56 { 57 setpcmark(); 58 /* keep it even when the cursor doesn't move */ 59 curwin->w_prev_pcmark = curwin->w_pcmark; 60 return OK; 61 } 62 63 /* Allow setting '[ and '] for an autocommand that simulates reading a 64 * file. */ 65 if (c == '[') 66 { 67 curbuf->b_op_start = curwin->w_cursor; 68 return OK; 69 } 70 if (c == ']') 71 { 72 curbuf->b_op_end = curwin->w_cursor; 73 return OK; 74 } 75 76 #ifndef EBCDIC 77 if (c > 'z') /* some islower() and isupper() cannot handle 78 characters above 127 */ 79 return FAIL; 80 #endif 81 if (islower(c)) 82 { 83 i = c - 'a'; 84 curbuf->b_namedm[i] = curwin->w_cursor; 85 return OK; 86 } 87 if (isupper(c)) 88 { 89 i = c - 'A'; 90 namedfm[i].fmark.mark = curwin->w_cursor; 91 namedfm[i].fmark.fnum = curbuf->b_fnum; 92 vim_free(namedfm[i].fname); 93 namedfm[i].fname = NULL; 94 return OK; 95 } 96 return FAIL; 97 } 98 99 /* 100 * Set the previous context mark to the current position and add it to the 101 * jump list. 102 */ 103 void 104 setpcmark() 105 { 106 #ifdef FEAT_JUMPLIST 107 int i; 108 xfmark_T *fm; 109 #endif 110 #ifdef JUMPLIST_ROTATE 111 xfmark_T tempmark; 112 #endif 113 114 /* for :global the mark is set only once */ 115 if (global_busy || listcmd_busy || cmdmod.keepjumps) 116 return; 117 118 curwin->w_prev_pcmark = curwin->w_pcmark; 119 curwin->w_pcmark = curwin->w_cursor; 120 121 #ifdef FEAT_JUMPLIST 122 # ifdef JUMPLIST_ROTATE 123 /* 124 * If last used entry is not at the top, put it at the top by rotating 125 * the stack until it is (the newer entries will be at the bottom). 126 * Keep one entry (the last used one) at the top. 127 */ 128 if (curwin->w_jumplistidx < curwin->w_jumplistlen) 129 ++curwin->w_jumplistidx; 130 while (curwin->w_jumplistidx < curwin->w_jumplistlen) 131 { 132 tempmark = curwin->w_jumplist[curwin->w_jumplistlen - 1]; 133 for (i = curwin->w_jumplistlen - 1; i > 0; --i) 134 curwin->w_jumplist[i] = curwin->w_jumplist[i - 1]; 135 curwin->w_jumplist[0] = tempmark; 136 ++curwin->w_jumplistidx; 137 } 138 # endif 139 140 /* If jumplist is full: remove oldest entry */ 141 if (++curwin->w_jumplistlen > JUMPLISTSIZE) 142 { 143 curwin->w_jumplistlen = JUMPLISTSIZE; 144 vim_free(curwin->w_jumplist[0].fname); 145 for (i = 1; i < JUMPLISTSIZE; ++i) 146 curwin->w_jumplist[i - 1] = curwin->w_jumplist[i]; 147 } 148 curwin->w_jumplistidx = curwin->w_jumplistlen; 149 fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1]; 150 151 fm->fmark.mark = curwin->w_pcmark; 152 fm->fmark.fnum = curbuf->b_fnum; 153 fm->fname = NULL; 154 #endif 155 } 156 157 /* 158 * To change context, call setpcmark(), then move the current position to 159 * where ever, then call checkpcmark(). This ensures that the previous 160 * context will only be changed if the cursor moved to a different line. 161 * If pcmark was deleted (with "dG") the previous mark is restored. 162 */ 163 void 164 checkpcmark() 165 { 166 if (curwin->w_prev_pcmark.lnum != 0 167 && (equalpos(curwin->w_pcmark, curwin->w_cursor) 168 || curwin->w_pcmark.lnum == 0)) 169 { 170 curwin->w_pcmark = curwin->w_prev_pcmark; 171 curwin->w_prev_pcmark.lnum = 0; /* Show it has been checked */ 172 } 173 } 174 175 #if defined(FEAT_JUMPLIST) || defined(PROTO) 176 /* 177 * move "count" positions in the jump list (count may be negative) 178 */ 179 pos_T * 180 movemark(count) 181 int count; 182 { 183 pos_T *pos; 184 xfmark_T *jmp; 185 186 cleanup_jumplist(); 187 188 if (curwin->w_jumplistlen == 0) /* nothing to jump to */ 189 return (pos_T *)NULL; 190 191 for (;;) 192 { 193 if (curwin->w_jumplistidx + count < 0 194 || curwin->w_jumplistidx + count >= curwin->w_jumplistlen) 195 return (pos_T *)NULL; 196 197 /* 198 * if first CTRL-O or CTRL-I command after a jump, add cursor position 199 * to list. Careful: If there are duplicates (CTRL-O immidiately after 200 * starting Vim on a file), another entry may have been removed. 201 */ 202 if (curwin->w_jumplistidx == curwin->w_jumplistlen) 203 { 204 setpcmark(); 205 --curwin->w_jumplistidx; /* skip the new entry */ 206 if (curwin->w_jumplistidx + count < 0) 207 return (pos_T *)NULL; 208 } 209 210 curwin->w_jumplistidx += count; 211 212 jmp = curwin->w_jumplist + curwin->w_jumplistidx; 213 if (jmp->fmark.fnum == 0) 214 fname2fnum(jmp); 215 if (jmp->fmark.fnum != curbuf->b_fnum) 216 { 217 /* jump to other file */ 218 if (buflist_findnr(jmp->fmark.fnum) == NULL) 219 { /* Skip this one .. */ 220 count += count < 0 ? -1 : 1; 221 continue; 222 } 223 if (buflist_getfile(jmp->fmark.fnum, jmp->fmark.mark.lnum, 224 0, FALSE) == FAIL) 225 return (pos_T *)NULL; 226 /* Set lnum again, autocommands my have changed it */ 227 curwin->w_cursor = jmp->fmark.mark; 228 pos = (pos_T *)-1; 229 } 230 else 231 pos = &(jmp->fmark.mark); 232 return pos; 233 } 234 } 235 236 /* 237 * Move "count" positions in the changelist (count may be negative). 238 */ 239 pos_T * 240 movechangelist(count) 241 int count; 242 { 243 int n; 244 245 if (curbuf->b_changelistlen == 0) /* nothing to jump to */ 246 return (pos_T *)NULL; 247 248 n = curwin->w_changelistidx; 249 if (n + count < 0) 250 { 251 if (n == 0) 252 return (pos_T *)NULL; 253 n = 0; 254 } 255 else if (n + count >= curbuf->b_changelistlen) 256 { 257 if (n == curbuf->b_changelistlen - 1) 258 return (pos_T *)NULL; 259 n = curbuf->b_changelistlen - 1; 260 } 261 else 262 n += count; 263 curwin->w_changelistidx = n; 264 return curbuf->b_changelist + n; 265 } 266 #endif 267 268 /* 269 * Find mark "c". 270 * Returns: 271 * - pointer to pos_T if found. lnum is 0 when mark not set, -1 when mark is 272 * in another file which can't be gotten. (caller needs to check lnum!) 273 * - NULL if there is no mark called 'c'. 274 * - -1 if mark is in other file and jumped there (only if changefile is TRUE) 275 */ 276 pos_T * 277 getmark(c, changefile) 278 int c; 279 int changefile; /* allowed to edit another file */ 280 { 281 pos_T *posp; 282 #ifdef FEAT_VISUAL 283 pos_T *startp, *endp; 284 #endif 285 static pos_T pos_copy; 286 287 posp = NULL; 288 289 /* Check for special key, can't be a mark name and might cause islower() 290 * to crash. */ 291 if (c < 0) 292 return posp; 293 #ifndef EBCDIC 294 if (c > '~') /* check for islower()/isupper() */ 295 ; 296 else 297 #endif 298 if (c == '\'' || c == '`') /* previous context mark */ 299 { 300 pos_copy = curwin->w_pcmark; /* need to make a copy because */ 301 posp = &pos_copy; /* w_pcmark may be changed soon */ 302 } 303 else if (c == '"') /* to pos when leaving buffer */ 304 posp = &(curbuf->b_last_cursor); 305 else if (c == '^') /* to where Insert mode stopped */ 306 posp = &(curbuf->b_last_insert); 307 else if (c == '.') /* to where last change was made */ 308 posp = &(curbuf->b_last_change); 309 else if (c == '[') /* to start of previous operator */ 310 posp = &(curbuf->b_op_start); 311 else if (c == ']') /* to end of previous operator */ 312 posp = &(curbuf->b_op_end); 313 else if (c == '{' || c == '}') /* to previous/next paragraph */ 314 { 315 pos_T pos; 316 oparg_T oa; 317 int slcb = listcmd_busy; 318 319 pos = curwin->w_cursor; 320 listcmd_busy = TRUE; /* avoid that '' is changed */ 321 if (findpar(&oa, c == '}' ? FORWARD : BACKWARD, 1L, NUL, FALSE)) 322 { 323 pos_copy = curwin->w_cursor; 324 posp = &pos_copy; 325 } 326 curwin->w_cursor = pos; 327 listcmd_busy = slcb; 328 } 329 else if (c == '(' || c == ')') /* to previous/next sentence */ 330 { 331 pos_T pos; 332 int slcb = listcmd_busy; 333 334 pos = curwin->w_cursor; 335 listcmd_busy = TRUE; /* avoid that '' is changed */ 336 if (findsent(c == ')' ? FORWARD : BACKWARD, 1L)) 337 { 338 pos_copy = curwin->w_cursor; 339 posp = &pos_copy; 340 } 341 curwin->w_cursor = pos; 342 listcmd_busy = slcb; 343 } 344 #ifdef FEAT_VISUAL 345 else if (c == '<' || c == '>') /* start/end of visual area */ 346 { 347 startp = &curbuf->b_visual_start; 348 endp = &curbuf->b_visual_end; 349 if ((c == '<') == lt(*startp, *endp)) 350 posp = startp; 351 else 352 posp = endp; 353 /* 354 * For Visual line mode, set mark at begin or end of line 355 */ 356 if (curbuf->b_visual_mode == 'V') 357 { 358 pos_copy = *posp; 359 posp = &pos_copy; 360 if (c == '<') 361 pos_copy.col = 0; 362 else 363 pos_copy.col = MAXCOL; 364 #ifdef FEAT_VIRTUALEDIT 365 pos_copy.coladd = 0; 366 #endif 367 } 368 } 369 #endif 370 else if (ASCII_ISLOWER(c)) /* normal named mark */ 371 { 372 posp = &(curbuf->b_namedm[c - 'a']); 373 } 374 else if (ASCII_ISUPPER(c) || VIM_ISDIGIT(c)) /* named file mark */ 375 { 376 if (VIM_ISDIGIT(c)) 377 c = c - '0' + NMARKS; 378 else 379 c -= 'A'; 380 posp = &(namedfm[c].fmark.mark); 381 382 if (namedfm[c].fmark.fnum == 0) 383 fname2fnum(&namedfm[c]); 384 if (namedfm[c].fmark.fnum != curbuf->b_fnum) 385 { 386 posp = &pos_copy; 387 388 /* mark is in another file */ 389 if (namedfm[c].fmark.mark.lnum != 0 390 && changefile && namedfm[c].fmark.fnum) 391 { 392 if (buflist_getfile(namedfm[c].fmark.fnum, 393 (linenr_T)1, GETF_SETMARK, FALSE) == OK) 394 { 395 /* Set the lnum now, autocommands could have changed it */ 396 curwin->w_cursor = namedfm[c].fmark.mark; 397 return (pos_T *)-1; 398 } 399 pos_copy.lnum = -1; /* can't get file */ 400 } 401 else 402 pos_copy.lnum = 0; /* mark exists, but is not valid in 403 current buffer */ 404 } 405 } 406 407 return posp; 408 } 409 410 /* 411 * Search for the next named mark in the current file. 412 * 413 * Returns pointer to pos_T of the next mark or NULL if no mark is found. 414 */ 415 pos_T * 416 getnextmark(startpos, dir, begin_line) 417 pos_T *startpos; /* where to start */ 418 int dir; /* direction for search */ 419 int begin_line; 420 { 421 int i; 422 pos_T *result = NULL; 423 pos_T pos; 424 425 pos = *startpos; 426 427 /* When searching backward and leaving the cursor on the first non-blank, 428 * position must be in a previous line. 429 * When searching forward and leaving the cursor on the first non-blank, 430 * position must be in a next line. */ 431 if (dir == BACKWARD && begin_line) 432 pos.col = 0; 433 else if (dir == FORWARD && begin_line) 434 pos.col = MAXCOL; 435 436 for (i = 0; i < NMARKS; i++) 437 { 438 if (curbuf->b_namedm[i].lnum > 0) 439 { 440 if (dir == FORWARD) 441 { 442 if ((result == NULL || lt(curbuf->b_namedm[i], *result)) 443 && lt(pos, curbuf->b_namedm[i])) 444 result = &curbuf->b_namedm[i]; 445 } 446 else 447 { 448 if ((result == NULL || lt(*result, curbuf->b_namedm[i])) 449 && lt(curbuf->b_namedm[i], pos)) 450 result = &curbuf->b_namedm[i]; 451 } 452 } 453 } 454 455 return result; 456 } 457 458 /* 459 * For an xtended filemark: set the fnum from the fname. 460 * This is used for marks obtained from the .viminfo file. It's postponed 461 * until the mark is used to avoid a long startup delay. 462 */ 463 static void 464 fname2fnum(fm) 465 xfmark_T *fm; 466 { 467 char_u *p; 468 469 if (fm->fname != NULL) 470 { 471 /* 472 * First expand "~/" in the file name to the home directory. 473 * Try to shorten the file name. 474 */ 475 expand_env(fm->fname, NameBuff, MAXPATHL); 476 mch_dirname(IObuff, IOSIZE); 477 p = shorten_fname(NameBuff, IObuff); 478 479 /* buflist_new() will call fmarks_check_names() */ 480 (void)buflist_new(NameBuff, p, (linenr_T)1, 0); 481 } 482 } 483 484 /* 485 * Check all file marks for a name that matches the file name in buf. 486 * May replace the name with an fnum. 487 * Used for marks that come from the .viminfo file. 488 */ 489 void 490 fmarks_check_names(buf) 491 buf_T *buf; 492 { 493 char_u *name; 494 int i; 495 #ifdef FEAT_JUMPLIST 496 win_T *wp; 497 #endif 498 499 if (buf->b_ffname == NULL) 500 return; 501 502 name = home_replace_save(buf, buf->b_ffname); 503 if (name == NULL) 504 return; 505 506 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i) 507 fmarks_check_one(&namedfm[i], name, buf); 508 509 #ifdef FEAT_JUMPLIST 510 FOR_ALL_WINDOWS(wp) 511 { 512 for (i = 0; i < wp->w_jumplistlen; ++i) 513 fmarks_check_one(&wp->w_jumplist[i], name, buf); 514 } 515 #endif 516 517 vim_free(name); 518 } 519 520 static void 521 fmarks_check_one(fm, name, buf) 522 xfmark_T *fm; 523 char_u *name; 524 buf_T *buf; 525 { 526 if (fm->fmark.fnum == 0 527 && fm->fname != NULL 528 && fnamecmp(name, fm->fname) == 0) 529 { 530 fm->fmark.fnum = buf->b_fnum; 531 vim_free(fm->fname); 532 fm->fname = NULL; 533 } 534 } 535 536 /* 537 * Check a if a position from a mark is valid. 538 * Give and error message and return FAIL if not. 539 */ 540 int 541 check_mark(pos) 542 pos_T *pos; 543 { 544 if (pos == NULL) 545 { 546 EMSG(_(e_umark)); 547 return FAIL; 548 } 549 if (pos->lnum <= 0) 550 { 551 /* lnum is negative if mark is in another file can can't get that 552 * file, error message already give then. */ 553 if (pos->lnum == 0) 554 EMSG(_(e_marknotset)); 555 return FAIL; 556 } 557 if (pos->lnum > curbuf->b_ml.ml_line_count) 558 { 559 EMSG(_(e_markinval)); 560 return FAIL; 561 } 562 return OK; 563 } 564 565 /* 566 * clrallmarks() - clear all marks in the buffer 'buf' 567 * 568 * Used mainly when trashing the entire buffer during ":e" type commands 569 */ 570 void 571 clrallmarks(buf) 572 buf_T *buf; 573 { 574 static int i = -1; 575 576 if (i == -1) /* first call ever: initialize */ 577 for (i = 0; i < NMARKS + 1; i++) 578 { 579 namedfm[i].fmark.mark.lnum = 0; 580 namedfm[i].fname = NULL; 581 } 582 583 for (i = 0; i < NMARKS; i++) 584 buf->b_namedm[i].lnum = 0; 585 buf->b_op_start.lnum = 0; /* start/end op mark cleared */ 586 buf->b_op_end.lnum = 0; 587 buf->b_last_cursor.lnum = 1; /* '" mark cleared */ 588 buf->b_last_cursor.col = 0; 589 #ifdef FEAT_VIRTUALEDIT 590 buf->b_last_cursor.coladd = 0; 591 #endif 592 buf->b_last_insert.lnum = 0; /* '^ mark cleared */ 593 buf->b_last_change.lnum = 0; /* '. mark cleared */ 594 #ifdef FEAT_JUMPLIST 595 buf->b_changelistlen = 0; 596 #endif 597 } 598 599 /* 600 * Get name of file from a filemark. 601 * When it's in the current buffer, return the text at the mark. 602 * Returns an allocated string. 603 */ 604 char_u * 605 fm_getname(fmark, lead_len) 606 fmark_T *fmark; 607 int lead_len; 608 { 609 if (fmark->fnum == curbuf->b_fnum) /* current buffer */ 610 return mark_line(&(fmark->mark), lead_len); 611 return buflist_nr2name(fmark->fnum, FALSE, TRUE); 612 } 613 614 /* 615 * Return the line at mark "mp". Truncate to fit in window. 616 * The returned string has been allocated. 617 */ 618 static char_u * 619 mark_line(mp, lead_len) 620 pos_T *mp; 621 int lead_len; 622 { 623 char_u *s, *p; 624 int len; 625 626 if (mp->lnum == 0 || mp->lnum > curbuf->b_ml.ml_line_count) 627 return vim_strsave((char_u *)"-invalid-"); 628 s = vim_strnsave(skipwhite(ml_get(mp->lnum)), (int)Columns); 629 if (s == NULL) 630 return NULL; 631 /* Truncate the line to fit it in the window */ 632 len = 0; 633 for (p = s; *p != NUL; mb_ptr_adv(p)) 634 { 635 len += ptr2cells(p); 636 if (len >= Columns - lead_len) 637 break; 638 } 639 *p = NUL; 640 return s; 641 } 642 643 /* 644 * print the marks 645 */ 646 void 647 do_marks(eap) 648 exarg_T *eap; 649 { 650 char_u *arg = eap->arg; 651 int i; 652 char_u *name; 653 654 if (arg != NULL && *arg == NUL) 655 arg = NULL; 656 657 show_one_mark('\'', arg, &curwin->w_pcmark, NULL, TRUE); 658 for (i = 0; i < NMARKS; ++i) 659 show_one_mark(i + 'a', arg, &curbuf->b_namedm[i], NULL, TRUE); 660 for (i = 0; i < NMARKS + EXTRA_MARKS; ++i) 661 { 662 if (namedfm[i].fmark.fnum != 0) 663 name = fm_getname(&namedfm[i].fmark, 15); 664 else 665 name = namedfm[i].fname; 666 if (name != NULL) 667 { 668 show_one_mark(i >= NMARKS ? i - NMARKS + '0' : i + 'A', 669 arg, &namedfm[i].fmark.mark, name, 670 namedfm[i].fmark.fnum == curbuf->b_fnum); 671 if (namedfm[i].fmark.fnum != 0) 672 vim_free(name); 673 } 674 } 675 show_one_mark('"', arg, &curbuf->b_last_cursor, NULL, TRUE); 676 show_one_mark('[', arg, &curbuf->b_op_start, NULL, TRUE); 677 show_one_mark(']', arg, &curbuf->b_op_end, NULL, TRUE); 678 show_one_mark('^', arg, &curbuf->b_last_insert, NULL, TRUE); 679 show_one_mark('.', arg, &curbuf->b_last_change, NULL, TRUE); 680 #ifdef FEAT_VISUAL 681 show_one_mark('<', arg, &curbuf->b_visual_start, NULL, TRUE); 682 show_one_mark('>', arg, &curbuf->b_visual_end, NULL, TRUE); 683 #endif 684 show_one_mark(-1, arg, NULL, NULL, FALSE); 685 } 686 687 static void 688 show_one_mark(c, arg, p, name, current) 689 int c; 690 char_u *arg; 691 pos_T *p; 692 char_u *name; 693 int current; /* in current file */ 694 { 695 static int did_title = FALSE; 696 int mustfree = FALSE; 697 698 if (c == -1) /* finish up */ 699 { 700 if (did_title) 701 did_title = FALSE; 702 else 703 { 704 if (arg == NULL) 705 MSG(_("No marks set")); 706 else 707 EMSG2(_("E283: No marks matching \"%s\""), arg); 708 } 709 } 710 /* don't output anything if 'q' typed at --more-- prompt */ 711 else if (!got_int 712 && (arg == NULL || vim_strchr(arg, c) != NULL) 713 && p->lnum != 0) 714 { 715 if (!did_title) 716 { 717 /* Highlight title */ 718 MSG_PUTS_TITLE(_("\nmark line col file/text")); 719 did_title = TRUE; 720 } 721 msg_putchar('\n'); 722 if (!got_int) 723 { 724 sprintf((char *)IObuff, " %c %6ld %4d ", c, p->lnum, p->col); 725 msg_outtrans(IObuff); 726 if (name == NULL && current) 727 { 728 name = mark_line(p, 15); 729 mustfree = TRUE; 730 } 731 if (name != NULL) 732 { 733 msg_outtrans_attr(name, current ? hl_attr(HLF_D) : 0); 734 if (mustfree) 735 vim_free(name); 736 } 737 } 738 out_flush(); /* show one line at a time */ 739 } 740 } 741 742 /* 743 * ":delmarks[!] [marks]" 744 */ 745 void 746 ex_delmarks(eap) 747 exarg_T *eap; 748 { 749 char_u *p; 750 int from, to; 751 int i; 752 int lower; 753 int digit; 754 int n; 755 756 if (*eap->arg == NUL && eap->forceit) 757 /* clear all marks */ 758 clrallmarks(curbuf); 759 else if (eap->forceit) 760 EMSG(_(e_invarg)); 761 else if (*eap->arg == NUL) 762 EMSG(_(e_argreq)); 763 else 764 { 765 /* clear specified marks only */ 766 for (p = eap->arg; *p != NUL; ++p) 767 { 768 lower = ASCII_ISLOWER(*p); 769 digit = VIM_ISDIGIT(*p); 770 if (lower || digit || ASCII_ISUPPER(*p)) 771 { 772 if (p[1] == '-') 773 { 774 /* clear range of marks */ 775 from = *p; 776 to = p[2]; 777 if (!(lower ? ASCII_ISLOWER(p[2]) 778 : (digit ? VIM_ISDIGIT(p[2]) 779 : ASCII_ISUPPER(p[2]))) 780 || to < from) 781 { 782 EMSG2(_(e_invarg2), p); 783 return; 784 } 785 p += 2; 786 } 787 else 788 /* clear one lower case mark */ 789 from = to = *p; 790 791 for (i = from; i <= to; ++i) 792 { 793 if (lower) 794 curbuf->b_namedm[i - 'a'].lnum = 0; 795 else 796 { 797 if (digit) 798 n = i - '0' + NMARKS; 799 else 800 n = i - 'A'; 801 namedfm[n].fmark.mark.lnum = 0; 802 vim_free(namedfm[n].fname); 803 namedfm[n].fname = NULL; 804 } 805 } 806 } 807 else 808 switch (*p) 809 { 810 case '"': curbuf->b_last_cursor.lnum = 0; break; 811 case '^': curbuf->b_last_insert.lnum = 0; break; 812 case '.': curbuf->b_last_change.lnum = 0; break; 813 case '[': curbuf->b_op_start.lnum = 0; break; 814 case ']': curbuf->b_op_end.lnum = 0; break; 815 #ifdef FEAT_VISUAL 816 case '<': curbuf->b_visual_start.lnum = 0; break; 817 case '>': curbuf->b_visual_end.lnum = 0; break; 818 #endif 819 case ' ': break; 820 default: EMSG2(_(e_invarg2), p); 821 return; 822 } 823 } 824 } 825 } 826 827 #if defined(FEAT_JUMPLIST) || defined(PROTO) 828 /* 829 * print the jumplist 830 */ 831 /*ARGSUSED*/ 832 void 833 ex_jumps(eap) 834 exarg_T *eap; 835 { 836 int i; 837 char_u *name; 838 839 cleanup_jumplist(); 840 /* Highlight title */ 841 MSG_PUTS_TITLE(_("\n jump line col file/text")); 842 for (i = 0; i < curwin->w_jumplistlen && !got_int; ++i) 843 { 844 if (curwin->w_jumplist[i].fmark.mark.lnum != 0) 845 { 846 if (curwin->w_jumplist[i].fmark.fnum == 0) 847 fname2fnum(&curwin->w_jumplist[i]); 848 name = fm_getname(&curwin->w_jumplist[i].fmark, 16); 849 if (name == NULL) /* file name not available */ 850 continue; 851 852 msg_putchar('\n'); 853 if (got_int) 854 break; 855 sprintf((char *)IObuff, "%c %2d %5ld %4d ", 856 i == curwin->w_jumplistidx ? '>' : ' ', 857 i > curwin->w_jumplistidx ? i - curwin->w_jumplistidx 858 : curwin->w_jumplistidx - i, 859 curwin->w_jumplist[i].fmark.mark.lnum, 860 curwin->w_jumplist[i].fmark.mark.col); 861 msg_outtrans(IObuff); 862 msg_outtrans_attr(name, 863 curwin->w_jumplist[i].fmark.fnum == curbuf->b_fnum 864 ? hl_attr(HLF_D) : 0); 865 vim_free(name); 866 ui_breakcheck(); 867 } 868 out_flush(); 869 } 870 if (curwin->w_jumplistidx == curwin->w_jumplistlen) 871 MSG_PUTS("\n>"); 872 } 873 874 /* 875 * print the changelist 876 */ 877 /*ARGSUSED*/ 878 void 879 ex_changes(eap) 880 exarg_T *eap; 881 { 882 int i; 883 char_u *name; 884 885 /* Highlight title */ 886 MSG_PUTS_TITLE(_("\nchange line col text")); 887 888 for (i = 0; i < curbuf->b_changelistlen && !got_int; ++i) 889 { 890 if (curbuf->b_changelist[i].lnum != 0) 891 { 892 msg_putchar('\n'); 893 if (got_int) 894 break; 895 sprintf((char *)IObuff, "%c %3d %5ld %4d ", 896 i == curwin->w_changelistidx ? '>' : ' ', 897 i > curwin->w_changelistidx ? i - curwin->w_changelistidx 898 : curwin->w_changelistidx - i, 899 (long)curbuf->b_changelist[i].lnum, 900 curbuf->b_changelist[i].col); 901 msg_outtrans(IObuff); 902 name = mark_line(&curbuf->b_changelist[i], 17); 903 if (name == NULL) 904 break; 905 msg_outtrans_attr(name, hl_attr(HLF_D)); 906 vim_free(name); 907 ui_breakcheck(); 908 } 909 out_flush(); 910 } 911 if (curwin->w_changelistidx == curbuf->b_changelistlen) 912 MSG_PUTS("\n>"); 913 } 914 #endif 915 916 #define one_adjust(add) \ 917 { \ 918 lp = add; \ 919 if (*lp >= line1 && *lp <= line2) \ 920 { \ 921 if (amount == MAXLNUM) \ 922 *lp = 0; \ 923 else \ 924 *lp += amount; \ 925 } \ 926 else if (amount_after && *lp > line2) \ 927 *lp += amount_after; \ 928 } 929 930 /* don't delete the line, just put at first deleted line */ 931 #define one_adjust_nodel(add) \ 932 { \ 933 lp = add; \ 934 if (*lp >= line1 && *lp <= line2) \ 935 { \ 936 if (amount == MAXLNUM) \ 937 *lp = line1; \ 938 else \ 939 *lp += amount; \ 940 } \ 941 else if (amount_after && *lp > line2) \ 942 *lp += amount_after; \ 943 } 944 945 /* 946 * Adjust marks between line1 and line2 (inclusive) to move 'amount' lines. 947 * Must be called before changed_*(), appended_lines() or deleted_lines(). 948 * May be called before or after changing the text. 949 * When deleting lines line1 to line2, use an 'amount' of MAXLNUM: The marks 950 * within this range are made invalid. 951 * If 'amount_after' is non-zero adjust marks after line2. 952 * Example: Delete lines 34 and 35: mark_adjust(34, 35, MAXLNUM, -2); 953 * Example: Insert two lines below 55: mark_adjust(56, MAXLNUM, 2, 0); 954 * or: mark_adjust(56, 55, MAXLNUM, 2); 955 */ 956 void 957 mark_adjust(line1, line2, amount, amount_after) 958 linenr_T line1; 959 linenr_T line2; 960 long amount; 961 long amount_after; 962 { 963 int i; 964 int fnum = curbuf->b_fnum; 965 linenr_T *lp; 966 win_T *win; 967 968 if (line2 < line1 && amount_after == 0L) /* nothing to do */ 969 return; 970 971 if (!cmdmod.lockmarks) 972 { 973 /* named marks, lower case and upper case */ 974 for (i = 0; i < NMARKS; i++) 975 { 976 one_adjust(&(curbuf->b_namedm[i].lnum)); 977 if (namedfm[i].fmark.fnum == fnum) 978 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum)); 979 } 980 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++) 981 { 982 if (namedfm[i].fmark.fnum == fnum) 983 one_adjust_nodel(&(namedfm[i].fmark.mark.lnum)); 984 } 985 986 /* last Insert position */ 987 one_adjust(&(curbuf->b_last_insert.lnum)); 988 989 /* last change position */ 990 one_adjust(&(curbuf->b_last_change.lnum)); 991 992 #ifdef FEAT_JUMPLIST 993 /* list of change positions */ 994 for (i = 0; i < curbuf->b_changelistlen; ++i) 995 one_adjust_nodel(&(curbuf->b_changelist[i].lnum)); 996 #endif 997 998 #ifdef FEAT_VISUAL 999 /* Visual area */ 1000 one_adjust_nodel(&(curbuf->b_visual_start.lnum)); 1001 one_adjust_nodel(&(curbuf->b_visual_end.lnum)); 1002 #endif 1003 1004 #ifdef FEAT_QUICKFIX 1005 /* quickfix marks */ 1006 qf_mark_adjust(line1, line2, amount, amount_after); 1007 #endif 1008 1009 #ifdef FEAT_SIGNS 1010 sign_mark_adjust(line1, line2, amount, amount_after); 1011 #endif 1012 } 1013 1014 /* previous context mark */ 1015 one_adjust(&(curwin->w_pcmark.lnum)); 1016 1017 /* previous pcmark */ 1018 one_adjust(&(curwin->w_prev_pcmark.lnum)); 1019 1020 /* saved cursor for formatting */ 1021 if (saved_cursor.lnum != 0) 1022 one_adjust_nodel(&(saved_cursor.lnum)); 1023 1024 /* 1025 * Adjust items in all windows related to the current buffer. 1026 */ 1027 FOR_ALL_WINDOWS(win) 1028 { 1029 #ifdef FEAT_JUMPLIST 1030 if (!cmdmod.lockmarks) 1031 /* Marks in the jumplist. When deleting lines, this may create 1032 * duplicate marks in the jumplist, they will be removed later. */ 1033 for (i = 0; i < win->w_jumplistlen; ++i) 1034 if (win->w_jumplist[i].fmark.fnum == fnum) 1035 one_adjust_nodel(&(win->w_jumplist[i].fmark.mark.lnum)); 1036 #endif 1037 1038 if (win->w_buffer == curbuf) 1039 { 1040 if (!cmdmod.lockmarks) 1041 /* marks in the tag stack */ 1042 for (i = 0; i < win->w_tagstacklen; i++) 1043 if (win->w_tagstack[i].fmark.fnum == fnum) 1044 one_adjust_nodel(&(win->w_tagstack[i].fmark.mark.lnum)); 1045 1046 #ifdef FEAT_VISUAL 1047 /* the displayed Visual area */ 1048 if (win->w_old_cursor_lnum != 0) 1049 { 1050 one_adjust_nodel(&(win->w_old_cursor_lnum)); 1051 one_adjust_nodel(&(win->w_old_visual_lnum)); 1052 } 1053 #endif 1054 1055 /* topline and cursor position for windows with the same buffer 1056 * other than the current window */ 1057 if (win != curwin) 1058 { 1059 if (win->w_topline >= line1 && win->w_topline <= line2) 1060 { 1061 if (amount == MAXLNUM) /* topline is deleted */ 1062 { 1063 if (line1 <= 1) 1064 win->w_topline = 1; 1065 else 1066 win->w_topline = line1 - 1; 1067 } 1068 else /* keep topline on the same line */ 1069 win->w_topline += amount; 1070 #ifdef FEAT_DIFF 1071 win->w_topfill = 0; 1072 #endif 1073 } 1074 else if (amount_after && win->w_topline > line2) 1075 { 1076 win->w_topline += amount_after; 1077 #ifdef FEAT_DIFF 1078 win->w_topfill = 0; 1079 #endif 1080 } 1081 if (win->w_cursor.lnum >= line1 && win->w_cursor.lnum <= line2) 1082 { 1083 if (amount == MAXLNUM) /* line with cursor is deleted */ 1084 { 1085 if (line1 <= 1) 1086 win->w_cursor.lnum = 1; 1087 else 1088 win->w_cursor.lnum = line1 - 1; 1089 win->w_cursor.col = 0; 1090 } 1091 else /* keep cursor on the same line */ 1092 win->w_cursor.lnum += amount; 1093 } 1094 else if (amount_after && win->w_cursor.lnum > line2) 1095 win->w_cursor.lnum += amount_after; 1096 } 1097 1098 #ifdef FEAT_FOLDING 1099 /* adjust folds */ 1100 foldMarkAdjust(win, line1, line2, amount, amount_after); 1101 #endif 1102 } 1103 } 1104 1105 #ifdef FEAT_DIFF 1106 /* adjust diffs */ 1107 diff_mark_adjust(line1, line2, amount, amount_after); 1108 #endif 1109 } 1110 1111 /* This code is used often, needs to be fast. */ 1112 #define col_adjust(pp) \ 1113 { \ 1114 posp = pp; \ 1115 if (posp->lnum == lnum && posp->col >= mincol) \ 1116 { \ 1117 posp->lnum += lnum_amount; \ 1118 if (col_amount < 0 && posp->col <= (colnr_T)-col_amount) \ 1119 posp->col = 0; \ 1120 else \ 1121 posp->col += col_amount; \ 1122 } \ 1123 } 1124 1125 /* 1126 * Adjust marks in line "lnum" at column "mincol" and further: add 1127 * "lnum_amount" to the line number and add "col_amount" to the column 1128 * position. 1129 */ 1130 void 1131 mark_col_adjust(lnum, mincol, lnum_amount, col_amount) 1132 linenr_T lnum; 1133 colnr_T mincol; 1134 long lnum_amount; 1135 long col_amount; 1136 { 1137 int i; 1138 int fnum = curbuf->b_fnum; 1139 win_T *win; 1140 pos_T *posp; 1141 1142 if ((col_amount == 0L && lnum_amount == 0L) || cmdmod.lockmarks) 1143 return; /* nothing to do */ 1144 1145 /* named marks, lower case and upper case */ 1146 for (i = 0; i < NMARKS; i++) 1147 { 1148 col_adjust(&(curbuf->b_namedm[i])); 1149 if (namedfm[i].fmark.fnum == fnum) 1150 col_adjust(&(namedfm[i].fmark.mark)); 1151 } 1152 for (i = NMARKS; i < NMARKS + EXTRA_MARKS; i++) 1153 { 1154 if (namedfm[i].fmark.fnum == fnum) 1155 col_adjust(&(namedfm[i].fmark.mark)); 1156 } 1157 1158 /* last Insert position */ 1159 col_adjust(&(curbuf->b_last_insert)); 1160 1161 /* last change position */ 1162 col_adjust(&(curbuf->b_last_change)); 1163 1164 #ifdef FEAT_JUMPLIST 1165 /* list of change positions */ 1166 for (i = 0; i < curbuf->b_changelistlen; ++i) 1167 col_adjust(&(curbuf->b_changelist[i])); 1168 #endif 1169 1170 #ifdef FEAT_VISUAL 1171 /* Visual area */ 1172 col_adjust(&(curbuf->b_visual_start)); 1173 col_adjust(&(curbuf->b_visual_end)); 1174 #endif 1175 1176 /* previous context mark */ 1177 col_adjust(&(curwin->w_pcmark)); 1178 1179 /* previous pcmark */ 1180 col_adjust(&(curwin->w_prev_pcmark)); 1181 1182 /* saved cursor for formatting */ 1183 col_adjust(&saved_cursor); 1184 1185 /* 1186 * Adjust items in all windows related to the current buffer. 1187 */ 1188 FOR_ALL_WINDOWS(win) 1189 { 1190 #ifdef FEAT_JUMPLIST 1191 /* marks in the jumplist */ 1192 for (i = 0; i < win->w_jumplistlen; ++i) 1193 if (win->w_jumplist[i].fmark.fnum == fnum) 1194 col_adjust(&(win->w_jumplist[i].fmark.mark)); 1195 #endif 1196 1197 if (win->w_buffer == curbuf) 1198 { 1199 /* marks in the tag stack */ 1200 for (i = 0; i < win->w_tagstacklen; i++) 1201 if (win->w_tagstack[i].fmark.fnum == fnum) 1202 col_adjust(&(win->w_tagstack[i].fmark.mark)); 1203 1204 /* cursor position for other windows with the same buffer */ 1205 if (win != curwin) 1206 col_adjust(&win->w_cursor); 1207 } 1208 } 1209 } 1210 1211 #ifdef FEAT_JUMPLIST 1212 /* 1213 * When deleting lines, this may create duplicate marks in the 1214 * jumplist. They will be removed here for the current window. 1215 */ 1216 static void 1217 cleanup_jumplist() 1218 { 1219 int i; 1220 int from, to; 1221 1222 to = 0; 1223 for (from = 0; from < curwin->w_jumplistlen; ++from) 1224 { 1225 if (curwin->w_jumplistidx == from) 1226 curwin->w_jumplistidx = to; 1227 for (i = from + 1; i < curwin->w_jumplistlen; ++i) 1228 if (curwin->w_jumplist[i].fmark.fnum 1229 == curwin->w_jumplist[from].fmark.fnum 1230 && curwin->w_jumplist[from].fmark.fnum != 0 1231 && curwin->w_jumplist[i].fmark.mark.lnum 1232 == curwin->w_jumplist[from].fmark.mark.lnum) 1233 break; 1234 if (i >= curwin->w_jumplistlen) /* no duplicate */ 1235 curwin->w_jumplist[to++] = curwin->w_jumplist[from]; 1236 else 1237 vim_free(curwin->w_jumplist[from].fname); 1238 } 1239 if (curwin->w_jumplistidx == curwin->w_jumplistlen) 1240 curwin->w_jumplistidx = to; 1241 curwin->w_jumplistlen = to; 1242 } 1243 1244 # if defined(FEAT_WINDOWS) || defined(PROTO) 1245 /* 1246 * Copy the jumplist from window "from" to window "to". 1247 */ 1248 void 1249 copy_jumplist(from, to) 1250 win_T *from; 1251 win_T *to; 1252 { 1253 int i; 1254 1255 for (i = 0; i < from->w_jumplistlen; ++i) 1256 { 1257 to->w_jumplist[i] = from->w_jumplist[i]; 1258 if (from->w_jumplist[i].fname != NULL) 1259 to->w_jumplist[i].fname = vim_strsave(from->w_jumplist[i].fname); 1260 } 1261 to->w_jumplistlen = from->w_jumplistlen; 1262 to->w_jumplistidx = from->w_jumplistidx; 1263 } 1264 1265 /* 1266 * Free items in the jumplist of window "wp". 1267 */ 1268 void 1269 free_jumplist(wp) 1270 win_T *wp; 1271 { 1272 int i; 1273 1274 for (i = 0; i < wp->w_jumplistlen; ++i) 1275 vim_free(wp->w_jumplist[i].fname); 1276 } 1277 # endif 1278 #endif /* FEAT_JUMPLIST */ 1279 1280 void 1281 set_last_cursor(win) 1282 win_T *win; 1283 { 1284 win->w_buffer->b_last_cursor = win->w_cursor; 1285 } 1286 1287 #if defined(EXITFREE) || defined(PROTO) 1288 void 1289 free_all_marks() 1290 { 1291 int i; 1292 1293 for (i = 0; i < NMARKS + EXTRA_MARKS; i++) 1294 if (namedfm[i].fmark.mark.lnum != 0) 1295 vim_free(namedfm[i].fname); 1296 } 1297 #endif 1298 1299 #if defined(FEAT_VIMINFO) || defined(PROTO) 1300 int 1301 read_viminfo_filemark(virp, force) 1302 vir_T *virp; 1303 int force; 1304 { 1305 char_u *str; 1306 xfmark_T *fm; 1307 int i; 1308 1309 /* We only get here if line[0] == '\'' or '-'. 1310 * Illegal mark names are ignored (for future expansion). */ 1311 str = virp->vir_line + 1; 1312 if ( 1313 #ifndef EBCDIC 1314 *str <= 127 && 1315 #endif 1316 ((*virp->vir_line == '\'' && (VIM_ISDIGIT(*str) || isupper(*str))) 1317 || (*virp->vir_line == '-' && *str == '\''))) 1318 { 1319 if (*str == '\'') 1320 { 1321 #ifdef FEAT_JUMPLIST 1322 /* If the jumplist isn't full insert fmark as oldest entry */ 1323 if (curwin->w_jumplistlen == JUMPLISTSIZE) 1324 fm = NULL; 1325 else 1326 { 1327 for (i = curwin->w_jumplistlen; i > 0; --i) 1328 curwin->w_jumplist[i] = curwin->w_jumplist[i - 1]; 1329 ++curwin->w_jumplistidx; 1330 ++curwin->w_jumplistlen; 1331 fm = &curwin->w_jumplist[0]; 1332 fm->fmark.mark.lnum = 0; 1333 fm->fname = NULL; 1334 } 1335 #else 1336 fm = NULL; 1337 #endif 1338 } 1339 else if (VIM_ISDIGIT(*str)) 1340 fm = &namedfm[*str - '0' + NMARKS]; 1341 else 1342 fm = &namedfm[*str - 'A']; 1343 if (fm != NULL && (fm->fmark.mark.lnum == 0 || force)) 1344 { 1345 str = skipwhite(str + 1); 1346 fm->fmark.mark.lnum = getdigits(&str); 1347 str = skipwhite(str); 1348 fm->fmark.mark.col = getdigits(&str); 1349 #ifdef FEAT_VIRTUALEDIT 1350 fm->fmark.mark.coladd = 0; 1351 #endif 1352 fm->fmark.fnum = 0; 1353 str = skipwhite(str); 1354 vim_free(fm->fname); 1355 fm->fname = viminfo_readstring(virp, (int)(str - virp->vir_line), 1356 FALSE); 1357 } 1358 } 1359 return vim_fgets(virp->vir_line, LSIZE, virp->vir_fd); 1360 } 1361 1362 void 1363 write_viminfo_filemarks(fp) 1364 FILE *fp; 1365 { 1366 int i; 1367 char_u *name; 1368 buf_T *buf; 1369 xfmark_T *fm; 1370 1371 if (get_viminfo_parameter('f') == 0) 1372 return; 1373 1374 fprintf(fp, _("\n# File marks:\n")); 1375 1376 /* 1377 * Find a mark that is the same file and position as the cursor. 1378 * That one, or else the last one is deleted. 1379 * Move '0 to '1, '1 to '2, etc. until the matching one or '9 1380 * Set '0 mark to current cursor position. 1381 */ 1382 if (curbuf->b_ffname != NULL && !removable(curbuf->b_ffname)) 1383 { 1384 name = buflist_nr2name(curbuf->b_fnum, TRUE, FALSE); 1385 for (i = NMARKS; i < NMARKS + EXTRA_MARKS - 1; ++i) 1386 if (namedfm[i].fmark.mark.lnum == curwin->w_cursor.lnum 1387 && (namedfm[i].fname == NULL 1388 ? namedfm[i].fmark.fnum == curbuf->b_fnum 1389 : (name != NULL 1390 && STRCMP(name, namedfm[i].fname) == 0))) 1391 break; 1392 vim_free(name); 1393 1394 vim_free(namedfm[i].fname); 1395 for ( ; i > NMARKS; --i) 1396 namedfm[i] = namedfm[i - 1]; 1397 namedfm[NMARKS].fmark.mark = curwin->w_cursor; 1398 namedfm[NMARKS].fmark.fnum = curbuf->b_fnum; 1399 namedfm[NMARKS].fname = NULL; 1400 } 1401 1402 /* Write the filemarks '0 - '9 and 'A - 'Z */ 1403 for (i = 0; i < NMARKS + EXTRA_MARKS; i++) 1404 write_one_filemark(fp, &namedfm[i], '\'', 1405 i < NMARKS ? i + 'A' : i - NMARKS + '0'); 1406 1407 #ifdef FEAT_JUMPLIST 1408 /* Write the jumplist with -' */ 1409 fprintf(fp, _("\n# Jumplist (newest first):\n")); 1410 setpcmark(); /* add current cursor position */ 1411 cleanup_jumplist(); 1412 for (fm = &curwin->w_jumplist[curwin->w_jumplistlen - 1]; 1413 fm >= &curwin->w_jumplist[0]; --fm) 1414 { 1415 if (fm->fmark.fnum == 0 1416 || ((buf = buflist_findnr(fm->fmark.fnum)) != NULL 1417 && !removable(buf->b_ffname))) 1418 write_one_filemark(fp, fm, '-', '\''); 1419 } 1420 #endif 1421 } 1422 1423 static void 1424 write_one_filemark(fp, fm, c1, c2) 1425 FILE *fp; 1426 xfmark_T *fm; 1427 int c1; 1428 int c2; 1429 { 1430 char_u *name; 1431 1432 if (fm->fmark.mark.lnum == 0) /* not set */ 1433 return; 1434 1435 if (fm->fmark.fnum != 0) /* there is a buffer */ 1436 name = buflist_nr2name(fm->fmark.fnum, TRUE, FALSE); 1437 else 1438 name = fm->fname; /* use name from .viminfo */ 1439 if (name != NULL && *name != NUL) 1440 { 1441 fprintf(fp, "%c%c %ld %ld ", c1, c2, (long)fm->fmark.mark.lnum, 1442 (long)fm->fmark.mark.col); 1443 viminfo_writestring(fp, name); 1444 } 1445 1446 if (fm->fmark.fnum != 0) 1447 vim_free(name); 1448 } 1449 1450 /* 1451 * Return TRUE if "name" is on removable media (depending on 'viminfo'). 1452 */ 1453 int 1454 removable(name) 1455 char_u *name; 1456 { 1457 char_u *p; 1458 char_u part[51]; 1459 int retval = FALSE; 1460 size_t n; 1461 1462 name = home_replace_save(NULL, name); 1463 if (name != NULL) 1464 { 1465 for (p = p_viminfo; *p; ) 1466 { 1467 copy_option_part(&p, part, 51, ", "); 1468 if (part[0] == 'r') 1469 { 1470 n = STRLEN(part + 1); 1471 if (MB_STRNICMP(part + 1, name, n) == 0) 1472 { 1473 retval = TRUE; 1474 break; 1475 } 1476 } 1477 } 1478 vim_free(name); 1479 } 1480 return retval; 1481 } 1482 1483 static void write_one_mark __ARGS((FILE *fp_out, int c, pos_T *pos)); 1484 1485 /* 1486 * Write all the named marks for all buffers. 1487 * Return the number of buffers for which marks have been written. 1488 */ 1489 int 1490 write_viminfo_marks(fp_out) 1491 FILE *fp_out; 1492 { 1493 int count; 1494 buf_T *buf; 1495 int is_mark_set; 1496 int i; 1497 #ifdef FEAT_WINDOWS 1498 win_T *win; 1499 1500 /* 1501 * Set b_last_cursor for the all buffers that have a window. 1502 */ 1503 for (win = firstwin; win != NULL; win = win->w_next) 1504 set_last_cursor(win); 1505 #else 1506 set_last_cursor(curwin); 1507 #endif 1508 1509 fprintf(fp_out, _("\n# History of marks within files (newest to oldest):\n")); 1510 count = 0; 1511 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 1512 { 1513 /* 1514 * Only write something if buffer has been loaded and at least one 1515 * mark is set. 1516 */ 1517 if (buf->b_marks_read) 1518 { 1519 if (buf->b_last_cursor.lnum != 0) 1520 is_mark_set = TRUE; 1521 else 1522 { 1523 is_mark_set = FALSE; 1524 for (i = 0; i < NMARKS; i++) 1525 if (buf->b_namedm[i].lnum != 0) 1526 { 1527 is_mark_set = TRUE; 1528 break; 1529 } 1530 } 1531 if (is_mark_set && buf->b_ffname != NULL 1532 && buf->b_ffname[0] != NUL && !removable(buf->b_ffname)) 1533 { 1534 home_replace(NULL, buf->b_ffname, IObuff, IOSIZE, TRUE); 1535 fprintf(fp_out, "\n> "); 1536 viminfo_writestring(fp_out, IObuff); 1537 write_one_mark(fp_out, '"', &buf->b_last_cursor); 1538 write_one_mark(fp_out, '^', &buf->b_last_insert); 1539 write_one_mark(fp_out, '.', &buf->b_last_change); 1540 #ifdef FEAT_JUMPLIST 1541 /* changelist positions are stored oldest first */ 1542 for (i = 0; i < buf->b_changelistlen; ++i) 1543 write_one_mark(fp_out, '+', &buf->b_changelist[i]); 1544 #endif 1545 for (i = 0; i < NMARKS; i++) 1546 write_one_mark(fp_out, 'a' + i, &buf->b_namedm[i]); 1547 count++; 1548 } 1549 } 1550 } 1551 1552 return count; 1553 } 1554 1555 static void 1556 write_one_mark(fp_out, c, pos) 1557 FILE *fp_out; 1558 int c; 1559 pos_T *pos; 1560 { 1561 if (pos->lnum != 0) 1562 fprintf(fp_out, "\t%c\t%ld\t%d\n", c, (long)pos->lnum, (int)pos->col); 1563 } 1564 1565 /* 1566 * Handle marks in the viminfo file: 1567 * fp_out == NULL read marks for current buffer only 1568 * fp_out != NULL copy marks for buffers not in buffer list 1569 */ 1570 void 1571 copy_viminfo_marks(virp, fp_out, count, eof) 1572 vir_T *virp; 1573 FILE *fp_out; 1574 int count; 1575 int eof; 1576 { 1577 char_u *line = virp->vir_line; 1578 buf_T *buf; 1579 int num_marked_files; 1580 int load_marks; 1581 int copy_marks_out; 1582 char_u *str; 1583 int i; 1584 char_u *p; 1585 char_u *name_buf; 1586 pos_T pos; 1587 1588 if ((name_buf = alloc(LSIZE)) == NULL) 1589 return; 1590 *name_buf = NUL; 1591 num_marked_files = get_viminfo_parameter('\''); 1592 while (!eof && (count < num_marked_files || fp_out == NULL)) 1593 { 1594 if (line[0] != '>') 1595 { 1596 if (line[0] != '\n' && line[0] != '\r' && line[0] != '#') 1597 { 1598 if (viminfo_error("E576: ", _("Missing '>'"), line)) 1599 break; /* too many errors, return now */ 1600 } 1601 eof = vim_fgets(line, LSIZE, virp->vir_fd); 1602 continue; /* Skip this dud line */ 1603 } 1604 1605 /* 1606 * Handle long line and translate escaped characters. 1607 * Find file name, set str to start. 1608 * Ignore leading and trailing white space. 1609 */ 1610 str = skipwhite(line + 1); 1611 str = viminfo_readstring(virp, (int)(str - virp->vir_line), FALSE); 1612 if (str == NULL) 1613 continue; 1614 p = str + STRLEN(str); 1615 while (p != str && (*p == NUL || vim_isspace(*p))) 1616 p--; 1617 if (*p) 1618 p++; 1619 *p = NUL; 1620 1621 /* 1622 * If fp_out == NULL, load marks for current buffer. 1623 * If fp_out != NULL, copy marks for buffers not in buflist. 1624 */ 1625 load_marks = copy_marks_out = FALSE; 1626 if (fp_out == NULL) 1627 { 1628 if (curbuf->b_ffname != NULL) 1629 { 1630 if (*name_buf == NUL) /* only need to do this once */ 1631 home_replace(NULL, curbuf->b_ffname, name_buf, LSIZE, TRUE); 1632 if (fnamecmp(str, name_buf) == 0) 1633 load_marks = TRUE; 1634 } 1635 } 1636 else /* fp_out != NULL */ 1637 { 1638 /* This is slow if there are many buffers!! */ 1639 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 1640 if (buf->b_ffname != NULL) 1641 { 1642 home_replace(NULL, buf->b_ffname, name_buf, LSIZE, TRUE); 1643 if (fnamecmp(str, name_buf) == 0) 1644 break; 1645 } 1646 1647 /* 1648 * copy marks if the buffer has not been loaded 1649 */ 1650 if (buf == NULL || !buf->b_marks_read) 1651 { 1652 copy_marks_out = TRUE; 1653 fputs("\n> ", fp_out); 1654 viminfo_writestring(fp_out, str); 1655 count++; 1656 } 1657 } 1658 vim_free(str); 1659 1660 #ifdef FEAT_VIRTUALEDIT 1661 pos.coladd = 0; 1662 #endif 1663 while (!(eof = viminfo_readline(virp)) && line[0] == TAB) 1664 { 1665 if (load_marks) 1666 { 1667 if (line[1] != NUL) 1668 { 1669 sscanf((char *)line + 2, "%ld %u", &pos.lnum, &pos.col); 1670 switch (line[1]) 1671 { 1672 case '"': curbuf->b_last_cursor = pos; break; 1673 case '^': curbuf->b_last_insert = pos; break; 1674 case '.': curbuf->b_last_change = pos; break; 1675 case '+': 1676 #ifdef FEAT_JUMPLIST 1677 /* changelist positions are stored oldest 1678 * first */ 1679 if (curbuf->b_changelistlen == JUMPLISTSIZE) 1680 /* list is full, remove oldest entry */ 1681 mch_memmove(curbuf->b_changelist, 1682 curbuf->b_changelist + 1, 1683 sizeof(pos_T) * (JUMPLISTSIZE - 1)); 1684 else 1685 ++curbuf->b_changelistlen; 1686 curbuf->b_changelist[ 1687 curbuf->b_changelistlen - 1] = pos; 1688 #endif 1689 break; 1690 default: if ((i = line[1] - 'a') >= 0 && i < NMARKS) 1691 curbuf->b_namedm[i] = pos; 1692 } 1693 } 1694 } 1695 else if (copy_marks_out) 1696 fputs((char *)line, fp_out); 1697 } 1698 if (load_marks) 1699 { 1700 #ifdef FEAT_JUMPLIST 1701 win_T *wp; 1702 1703 FOR_ALL_WINDOWS(wp) 1704 { 1705 if (wp->w_buffer == curbuf) 1706 wp->w_changelistidx = curbuf->b_changelistlen; 1707 } 1708 #endif 1709 break; 1710 } 1711 } 1712 vim_free(name_buf); 1713 } 1714 #endif /* FEAT_VIMINFO */ 1715