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 * buffer.c: functions for dealing with the buffer structure 12 */ 13 14 /* 15 * The buffer list is a double linked list of all buffers. 16 * Each buffer can be in one of these states: 17 * never loaded: BF_NEVERLOADED is set, only the file name is valid 18 * not loaded: b_ml.ml_mfp == NULL, no memfile allocated 19 * hidden: b_nwindows == 0, loaded but not displayed in a window 20 * normal: loaded and displayed in a window 21 * 22 * Instead of storing file names all over the place, each file name is 23 * stored in the buffer list. It can be referenced by a number. 24 * 25 * The current implementation remembers all file names ever used. 26 */ 27 28 #include "vim.h" 29 30 #if defined(FEAT_CMDL_COMPL) || defined(FEAT_LISTCMDS) || defined(FEAT_EVAL) || defined(FEAT_PERL) 31 static char_u *buflist_match __ARGS((regprog_T *prog, buf_T *buf)); 32 # define HAVE_BUFLIST_MATCH 33 static char_u *fname_match __ARGS((regprog_T *prog, char_u *name)); 34 #endif 35 static void buflist_setfpos __ARGS((buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options)); 36 static wininfo_T *find_wininfo __ARGS((buf_T *buf, int skip_diff_buffer)); 37 #ifdef UNIX 38 static buf_T *buflist_findname_stat __ARGS((char_u *ffname, struct stat *st)); 39 static int otherfile_buf __ARGS((buf_T *buf, char_u *ffname, struct stat *stp)); 40 static int buf_same_ino __ARGS((buf_T *buf, struct stat *stp)); 41 #else 42 static int otherfile_buf __ARGS((buf_T *buf, char_u *ffname)); 43 #endif 44 #ifdef FEAT_TITLE 45 static int ti_change __ARGS((char_u *str, char_u **last)); 46 #endif 47 static int append_arg_number __ARGS((win_T *wp, char_u *buf, int buflen, int add_file)); 48 static void free_buffer __ARGS((buf_T *)); 49 static void free_buffer_stuff __ARGS((buf_T *buf, int free_options)); 50 static void clear_wininfo __ARGS((buf_T *buf)); 51 52 #ifdef UNIX 53 # define dev_T dev_t 54 #else 55 # define dev_T unsigned 56 #endif 57 58 #if defined(FEAT_SIGNS) 59 static void insert_sign __ARGS((buf_T *buf, signlist_T *prev, signlist_T *next, int id, linenr_T lnum, int typenr)); 60 #endif 61 62 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 63 static char *msg_loclist = N_("[Location List]"); 64 static char *msg_qflist = N_("[Quickfix List]"); 65 #endif 66 #ifdef FEAT_AUTOCMD 67 static char *e_auabort = N_("E855: Autocommands caused command to abort"); 68 #endif 69 70 /* 71 * Open current buffer, that is: open the memfile and read the file into 72 * memory. 73 * Return FAIL for failure, OK otherwise. 74 */ 75 int 76 open_buffer(read_stdin, eap, flags) 77 int read_stdin; /* read file from stdin */ 78 exarg_T *eap; /* for forced 'ff' and 'fenc' or NULL */ 79 int flags; /* extra flags for readfile() */ 80 { 81 int retval = OK; 82 #ifdef FEAT_AUTOCMD 83 buf_T *old_curbuf; 84 #endif 85 #ifdef FEAT_SYN_HL 86 long old_tw = curbuf->b_p_tw; 87 #endif 88 89 /* 90 * The 'readonly' flag is only set when BF_NEVERLOADED is being reset. 91 * When re-entering the same buffer, it should not change, because the 92 * user may have reset the flag by hand. 93 */ 94 if (readonlymode && curbuf->b_ffname != NULL 95 && (curbuf->b_flags & BF_NEVERLOADED)) 96 curbuf->b_p_ro = TRUE; 97 98 if (ml_open(curbuf) == FAIL) 99 { 100 /* 101 * There MUST be a memfile, otherwise we can't do anything 102 * If we can't create one for the current buffer, take another buffer 103 */ 104 close_buffer(NULL, curbuf, 0, FALSE); 105 for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next) 106 if (curbuf->b_ml.ml_mfp != NULL) 107 break; 108 /* 109 * if there is no memfile at all, exit 110 * This is OK, since there are no changes to lose. 111 */ 112 if (curbuf == NULL) 113 { 114 EMSG(_("E82: Cannot allocate any buffer, exiting...")); 115 getout(2); 116 } 117 EMSG(_("E83: Cannot allocate buffer, using other one...")); 118 enter_buffer(curbuf); 119 #ifdef FEAT_SYN_HL 120 if (old_tw != curbuf->b_p_tw) 121 check_colorcolumn(curwin); 122 #endif 123 return FAIL; 124 } 125 126 #ifdef FEAT_AUTOCMD 127 /* The autocommands in readfile() may change the buffer, but only AFTER 128 * reading the file. */ 129 old_curbuf = curbuf; 130 modified_was_set = FALSE; 131 #endif 132 133 /* mark cursor position as being invalid */ 134 curwin->w_valid = 0; 135 136 if (curbuf->b_ffname != NULL 137 #ifdef FEAT_NETBEANS_INTG 138 && netbeansReadFile 139 #endif 140 ) 141 { 142 #ifdef FEAT_NETBEANS_INTG 143 int oldFire = netbeansFireChanges; 144 145 netbeansFireChanges = 0; 146 #endif 147 retval = readfile(curbuf->b_ffname, curbuf->b_fname, 148 (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, 149 flags | READ_NEW); 150 #ifdef FEAT_NETBEANS_INTG 151 netbeansFireChanges = oldFire; 152 #endif 153 /* Help buffer is filtered. */ 154 if (curbuf->b_help) 155 fix_help_buffer(); 156 } 157 else if (read_stdin) 158 { 159 int save_bin = curbuf->b_p_bin; 160 linenr_T line_count; 161 162 /* 163 * First read the text in binary mode into the buffer. 164 * Then read from that same buffer and append at the end. This makes 165 * it possible to retry when 'fileformat' or 'fileencoding' was 166 * guessed wrong. 167 */ 168 curbuf->b_p_bin = TRUE; 169 retval = readfile(NULL, NULL, (linenr_T)0, 170 (linenr_T)0, (linenr_T)MAXLNUM, NULL, 171 flags | (READ_NEW + READ_STDIN)); 172 curbuf->b_p_bin = save_bin; 173 if (retval == OK) 174 { 175 line_count = curbuf->b_ml.ml_line_count; 176 retval = readfile(NULL, NULL, (linenr_T)line_count, 177 (linenr_T)0, (linenr_T)MAXLNUM, eap, 178 flags | READ_BUFFER); 179 if (retval == OK) 180 { 181 /* Delete the binary lines. */ 182 while (--line_count >= 0) 183 ml_delete((linenr_T)1, FALSE); 184 } 185 else 186 { 187 /* Delete the converted lines. */ 188 while (curbuf->b_ml.ml_line_count > line_count) 189 ml_delete(line_count, FALSE); 190 } 191 /* Put the cursor on the first line. */ 192 curwin->w_cursor.lnum = 1; 193 curwin->w_cursor.col = 0; 194 195 /* Set or reset 'modified' before executing autocommands, so that 196 * it can be changed there. */ 197 if (!readonlymode && !bufempty()) 198 changed(); 199 else if (retval != FAIL) 200 unchanged(curbuf, FALSE); 201 #ifdef FEAT_AUTOCMD 202 # ifdef FEAT_EVAL 203 apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE, 204 curbuf, &retval); 205 # else 206 apply_autocmds(EVENT_STDINREADPOST, NULL, NULL, FALSE, curbuf); 207 # endif 208 #endif 209 } 210 } 211 212 /* if first time loading this buffer, init b_chartab[] */ 213 if (curbuf->b_flags & BF_NEVERLOADED) 214 { 215 (void)buf_init_chartab(curbuf, FALSE); 216 #ifdef FEAT_CINDENT 217 parse_cino(curbuf); 218 #endif 219 } 220 221 /* 222 * Set/reset the Changed flag first, autocmds may change the buffer. 223 * Apply the automatic commands, before processing the modelines. 224 * So the modelines have priority over auto commands. 225 */ 226 /* When reading stdin, the buffer contents always needs writing, so set 227 * the changed flag. Unless in readonly mode: "ls | gview -". 228 * When interrupted and 'cpoptions' contains 'i' set changed flag. */ 229 if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL) 230 #ifdef FEAT_AUTOCMD 231 || modified_was_set /* ":set modified" used in autocmd */ 232 # ifdef FEAT_EVAL 233 || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL) 234 # endif 235 #endif 236 ) 237 changed(); 238 else if (retval != FAIL && !read_stdin) 239 unchanged(curbuf, FALSE); 240 save_file_ff(curbuf); /* keep this fileformat */ 241 242 /* require "!" to overwrite the file, because it wasn't read completely */ 243 #ifdef FEAT_EVAL 244 if (aborting()) 245 #else 246 if (got_int) 247 #endif 248 curbuf->b_flags |= BF_READERR; 249 250 #ifdef FEAT_FOLDING 251 /* Need to update automatic folding. Do this before the autocommands, 252 * they may use the fold info. */ 253 foldUpdateAll(curwin); 254 #endif 255 256 #ifdef FEAT_AUTOCMD 257 /* need to set w_topline, unless some autocommand already did that. */ 258 if (!(curwin->w_valid & VALID_TOPLINE)) 259 { 260 curwin->w_topline = 1; 261 # ifdef FEAT_DIFF 262 curwin->w_topfill = 0; 263 # endif 264 } 265 # ifdef FEAT_EVAL 266 apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval); 267 # else 268 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 269 # endif 270 #endif 271 272 if (retval != FAIL) 273 { 274 #ifdef FEAT_AUTOCMD 275 /* 276 * The autocommands may have changed the current buffer. Apply the 277 * modelines to the correct buffer, if it still exists and is loaded. 278 */ 279 if (buf_valid(old_curbuf) && old_curbuf->b_ml.ml_mfp != NULL) 280 { 281 aco_save_T aco; 282 283 /* Go to the buffer that was opened. */ 284 aucmd_prepbuf(&aco, old_curbuf); 285 #endif 286 do_modelines(0); 287 curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED); 288 289 #ifdef FEAT_AUTOCMD 290 # ifdef FEAT_EVAL 291 apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf, 292 &retval); 293 # else 294 apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); 295 # endif 296 297 /* restore curwin/curbuf and a few other things */ 298 aucmd_restbuf(&aco); 299 } 300 #endif 301 } 302 303 return retval; 304 } 305 306 /* 307 * Return TRUE if "buf" points to a valid buffer (in the buffer list). 308 */ 309 int 310 buf_valid(buf) 311 buf_T *buf; 312 { 313 buf_T *bp; 314 315 for (bp = firstbuf; bp != NULL; bp = bp->b_next) 316 if (bp == buf) 317 return TRUE; 318 return FALSE; 319 } 320 321 /* 322 * Close the link to a buffer. 323 * "action" is used when there is no longer a window for the buffer. 324 * It can be: 325 * 0 buffer becomes hidden 326 * DOBUF_UNLOAD buffer is unloaded 327 * DOBUF_DELETE buffer is unloaded and removed from buffer list 328 * DOBUF_WIPE buffer is unloaded and really deleted 329 * When doing all but the first one on the current buffer, the caller should 330 * get a new buffer very soon! 331 * 332 * The 'bufhidden' option can force freeing and deleting. 333 * 334 * When "abort_if_last" is TRUE then do not close the buffer if autocommands 335 * cause there to be only one window with this buffer. e.g. when ":quit" is 336 * supposed to close the window but autocommands close all other windows. 337 */ 338 void 339 close_buffer(win, buf, action, abort_if_last) 340 win_T *win; /* if not NULL, set b_last_cursor */ 341 buf_T *buf; 342 int action; 343 int abort_if_last UNUSED; 344 { 345 #ifdef FEAT_AUTOCMD 346 int is_curbuf; 347 int nwindows; 348 #endif 349 int unload_buf = (action != 0); 350 int del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); 351 int wipe_buf = (action == DOBUF_WIPE); 352 353 #ifdef FEAT_QUICKFIX 354 /* 355 * Force unloading or deleting when 'bufhidden' says so. 356 * The caller must take care of NOT deleting/freeing when 'bufhidden' is 357 * "hide" (otherwise we could never free or delete a buffer). 358 */ 359 if (buf->b_p_bh[0] == 'd') /* 'bufhidden' == "delete" */ 360 { 361 del_buf = TRUE; 362 unload_buf = TRUE; 363 } 364 else if (buf->b_p_bh[0] == 'w') /* 'bufhidden' == "wipe" */ 365 { 366 del_buf = TRUE; 367 unload_buf = TRUE; 368 wipe_buf = TRUE; 369 } 370 else if (buf->b_p_bh[0] == 'u') /* 'bufhidden' == "unload" */ 371 unload_buf = TRUE; 372 #endif 373 374 if (win != NULL) 375 { 376 /* Set b_last_cursor when closing the last window for the buffer. 377 * Remember the last cursor position and window options of the buffer. 378 * This used to be only for the current window, but then options like 379 * 'foldmethod' may be lost with a ":only" command. */ 380 if (buf->b_nwindows == 1) 381 set_last_cursor(win); 382 buflist_setfpos(buf, win, 383 win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum, 384 win->w_cursor.col, TRUE); 385 } 386 387 #ifdef FEAT_AUTOCMD 388 /* When the buffer is no longer in a window, trigger BufWinLeave */ 389 if (buf->b_nwindows == 1) 390 { 391 buf->b_closing = TRUE; 392 apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, 393 FALSE, buf); 394 if (!buf_valid(buf)) 395 { 396 /* Autocommands deleted the buffer. */ 397 aucmd_abort: 398 EMSG(_(e_auabort)); 399 return; 400 } 401 buf->b_closing = FALSE; 402 if (abort_if_last && one_window()) 403 /* Autocommands made this the only window. */ 404 goto aucmd_abort; 405 406 /* When the buffer becomes hidden, but is not unloaded, trigger 407 * BufHidden */ 408 if (!unload_buf) 409 { 410 buf->b_closing = TRUE; 411 apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, 412 FALSE, buf); 413 if (!buf_valid(buf)) 414 /* Autocommands deleted the buffer. */ 415 goto aucmd_abort; 416 buf->b_closing = FALSE; 417 if (abort_if_last && one_window()) 418 /* Autocommands made this the only window. */ 419 goto aucmd_abort; 420 } 421 # ifdef FEAT_EVAL 422 if (aborting()) /* autocmds may abort script processing */ 423 return; 424 # endif 425 } 426 nwindows = buf->b_nwindows; 427 #endif 428 429 /* decrease the link count from windows (unless not in any window) */ 430 if (buf->b_nwindows > 0) 431 --buf->b_nwindows; 432 433 /* Return when a window is displaying the buffer or when it's not 434 * unloaded. */ 435 if (buf->b_nwindows > 0 || !unload_buf) 436 return; 437 438 /* Always remove the buffer when there is no file name. */ 439 if (buf->b_ffname == NULL) 440 del_buf = TRUE; 441 442 /* 443 * Free all things allocated for this buffer. 444 * Also calls the "BufDelete" autocommands when del_buf is TRUE. 445 */ 446 #ifdef FEAT_AUTOCMD 447 /* Remember if we are closing the current buffer. Restore the number of 448 * windows, so that autocommands in buf_freeall() don't get confused. */ 449 is_curbuf = (buf == curbuf); 450 buf->b_nwindows = nwindows; 451 #endif 452 453 buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); 454 if ( 455 #ifdef FEAT_WINDOWS 456 win_valid(win) && 457 #else 458 win != NULL && 459 #endif 460 win->w_buffer == buf) 461 win->w_buffer = NULL; /* make sure we don't use the buffer now */ 462 463 #ifdef FEAT_AUTOCMD 464 /* Autocommands may have deleted the buffer. */ 465 if (!buf_valid(buf)) 466 return; 467 # ifdef FEAT_EVAL 468 if (aborting()) /* autocmds may abort script processing */ 469 return; 470 # endif 471 472 /* Autocommands may have opened or closed windows for this buffer. 473 * Decrement the count for the close we do here. */ 474 if (buf->b_nwindows > 0) 475 --buf->b_nwindows; 476 477 /* 478 * It's possible that autocommands change curbuf to the one being deleted. 479 * This might cause the previous curbuf to be deleted unexpectedly. But 480 * in some cases it's OK to delete the curbuf, because a new one is 481 * obtained anyway. Therefore only return if curbuf changed to the 482 * deleted buffer. 483 */ 484 if (buf == curbuf && !is_curbuf) 485 return; 486 #endif 487 488 /* Change directories when the 'acd' option is set. */ 489 DO_AUTOCHDIR 490 491 /* 492 * Remove the buffer from the list. 493 */ 494 if (wipe_buf) 495 { 496 #ifdef FEAT_SUN_WORKSHOP 497 if (usingSunWorkShop) 498 workshop_file_closed_lineno((char *)buf->b_ffname, 499 (int)buf->b_last_cursor.lnum); 500 #endif 501 vim_free(buf->b_ffname); 502 vim_free(buf->b_sfname); 503 if (buf->b_prev == NULL) 504 firstbuf = buf->b_next; 505 else 506 buf->b_prev->b_next = buf->b_next; 507 if (buf->b_next == NULL) 508 lastbuf = buf->b_prev; 509 else 510 buf->b_next->b_prev = buf->b_prev; 511 free_buffer(buf); 512 } 513 else 514 { 515 if (del_buf) 516 { 517 /* Free all internal variables and reset option values, to make 518 * ":bdel" compatible with Vim 5.7. */ 519 free_buffer_stuff(buf, TRUE); 520 521 /* Make it look like a new buffer. */ 522 buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; 523 524 /* Init the options when loaded again. */ 525 buf->b_p_initialized = FALSE; 526 } 527 buf_clear_file(buf); 528 if (del_buf) 529 buf->b_p_bl = FALSE; 530 } 531 } 532 533 /* 534 * Make buffer not contain a file. 535 */ 536 void 537 buf_clear_file(buf) 538 buf_T *buf; 539 { 540 buf->b_ml.ml_line_count = 1; 541 unchanged(buf, TRUE); 542 #ifndef SHORT_FNAME 543 buf->b_shortname = FALSE; 544 #endif 545 buf->b_p_eol = TRUE; 546 buf->b_start_eol = TRUE; 547 #ifdef FEAT_MBYTE 548 buf->b_p_bomb = FALSE; 549 buf->b_start_bomb = FALSE; 550 #endif 551 buf->b_ml.ml_mfp = NULL; 552 buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */ 553 #ifdef FEAT_NETBEANS_INTG 554 netbeans_deleted_all_lines(buf); 555 #endif 556 } 557 558 /* 559 * buf_freeall() - free all things allocated for a buffer that are related to 560 * the file. flags: 561 * BFA_DEL buffer is going to be deleted 562 * BFA_WIPE buffer is going to be wiped out 563 * BFA_KEEP_UNDO do not free undo information 564 */ 565 void 566 buf_freeall(buf, flags) 567 buf_T *buf; 568 int flags; 569 { 570 #ifdef FEAT_AUTOCMD 571 int is_curbuf = (buf == curbuf); 572 573 buf->b_closing = TRUE; 574 apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf); 575 if (!buf_valid(buf)) /* autocommands may delete the buffer */ 576 return; 577 if ((flags & BFA_DEL) && buf->b_p_bl) 578 { 579 apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, FALSE, buf); 580 if (!buf_valid(buf)) /* autocommands may delete the buffer */ 581 return; 582 } 583 if (flags & BFA_WIPE) 584 { 585 apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, 586 FALSE, buf); 587 if (!buf_valid(buf)) /* autocommands may delete the buffer */ 588 return; 589 } 590 buf->b_closing = FALSE; 591 # ifdef FEAT_EVAL 592 if (aborting()) /* autocmds may abort script processing */ 593 return; 594 # endif 595 596 /* 597 * It's possible that autocommands change curbuf to the one being deleted. 598 * This might cause curbuf to be deleted unexpectedly. But in some cases 599 * it's OK to delete the curbuf, because a new one is obtained anyway. 600 * Therefore only return if curbuf changed to the deleted buffer. 601 */ 602 if (buf == curbuf && !is_curbuf) 603 return; 604 #endif 605 #ifdef FEAT_DIFF 606 diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */ 607 #endif 608 #ifdef FEAT_SYN_HL 609 /* Remove any ownsyntax, unless exiting. */ 610 if (firstwin != NULL && curwin->w_buffer == buf) 611 reset_synblock(curwin); 612 #endif 613 614 #ifdef FEAT_FOLDING 615 /* No folds in an empty buffer. */ 616 # ifdef FEAT_WINDOWS 617 { 618 win_T *win; 619 tabpage_T *tp; 620 621 FOR_ALL_TAB_WINDOWS(tp, win) 622 if (win->w_buffer == buf) 623 clearFolding(win); 624 } 625 # else 626 if (curwin->w_buffer == buf) 627 clearFolding(curwin); 628 # endif 629 #endif 630 631 #ifdef FEAT_TCL 632 tcl_buffer_free(buf); 633 #endif 634 ml_close(buf, TRUE); /* close and delete the memline/memfile */ 635 buf->b_ml.ml_line_count = 0; /* no lines in buffer */ 636 if ((flags & BFA_KEEP_UNDO) == 0) 637 { 638 u_blockfree(buf); /* free the memory allocated for undo */ 639 u_clearall(buf); /* reset all undo information */ 640 } 641 #ifdef FEAT_SYN_HL 642 syntax_clear(&buf->b_s); /* reset syntax info */ 643 #endif 644 buf->b_flags &= ~BF_READERR; /* a read error is no longer relevant */ 645 } 646 647 /* 648 * Free a buffer structure and the things it contains related to the buffer 649 * itself (not the file, that must have been done already). 650 */ 651 static void 652 free_buffer(buf) 653 buf_T *buf; 654 { 655 free_buffer_stuff(buf, TRUE); 656 #ifdef FEAT_EVAL 657 unref_var_dict(buf->b_vars); 658 #endif 659 #ifdef FEAT_LUA 660 lua_buffer_free(buf); 661 #endif 662 #ifdef FEAT_MZSCHEME 663 mzscheme_buffer_free(buf); 664 #endif 665 #ifdef FEAT_PERL 666 perl_buf_free(buf); 667 #endif 668 #ifdef FEAT_PYTHON 669 python_buffer_free(buf); 670 #endif 671 #ifdef FEAT_PYTHON3 672 python3_buffer_free(buf); 673 #endif 674 #ifdef FEAT_RUBY 675 ruby_buffer_free(buf); 676 #endif 677 #ifdef FEAT_AUTOCMD 678 aubuflocal_remove(buf); 679 #endif 680 vim_free(buf); 681 } 682 683 /* 684 * Free stuff in the buffer for ":bdel" and when wiping out the buffer. 685 */ 686 static void 687 free_buffer_stuff(buf, free_options) 688 buf_T *buf; 689 int free_options; /* free options as well */ 690 { 691 if (free_options) 692 { 693 clear_wininfo(buf); /* including window-local options */ 694 free_buf_options(buf, TRUE); 695 #ifdef FEAT_SPELL 696 ga_clear(&buf->b_s.b_langp); 697 #endif 698 } 699 #ifdef FEAT_EVAL 700 vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */ 701 hash_init(&buf->b_vars->dv_hashtab); 702 #endif 703 #ifdef FEAT_USR_CMDS 704 uc_clear(&buf->b_ucmds); /* clear local user commands */ 705 #endif 706 #ifdef FEAT_SIGNS 707 buf_delete_signs(buf); /* delete any signs */ 708 #endif 709 #ifdef FEAT_NETBEANS_INTG 710 netbeans_file_killed(buf); 711 #endif 712 #ifdef FEAT_LOCALMAP 713 map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ 714 map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ 715 #endif 716 #ifdef FEAT_MBYTE 717 vim_free(buf->b_start_fenc); 718 buf->b_start_fenc = NULL; 719 #endif 720 } 721 722 /* 723 * Free the b_wininfo list for buffer "buf". 724 */ 725 static void 726 clear_wininfo(buf) 727 buf_T *buf; 728 { 729 wininfo_T *wip; 730 731 while (buf->b_wininfo != NULL) 732 { 733 wip = buf->b_wininfo; 734 buf->b_wininfo = wip->wi_next; 735 if (wip->wi_optset) 736 { 737 clear_winopt(&wip->wi_opt); 738 #ifdef FEAT_FOLDING 739 deleteFoldRecurse(&wip->wi_folds); 740 #endif 741 } 742 vim_free(wip); 743 } 744 } 745 746 #if defined(FEAT_LISTCMDS) || defined(PROTO) 747 /* 748 * Go to another buffer. Handles the result of the ATTENTION dialog. 749 */ 750 void 751 goto_buffer(eap, start, dir, count) 752 exarg_T *eap; 753 int start; 754 int dir; 755 int count; 756 { 757 # if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION) 758 buf_T *old_curbuf = curbuf; 759 760 swap_exists_action = SEA_DIALOG; 761 # endif 762 (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, 763 start, dir, count, eap->forceit); 764 # if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION) 765 if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') 766 { 767 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 768 cleanup_T cs; 769 770 /* Reset the error/interrupt/exception state here so that 771 * aborting() returns FALSE when closing a window. */ 772 enter_cleanup(&cs); 773 # endif 774 775 /* Quitting means closing the split window, nothing else. */ 776 win_close(curwin, TRUE); 777 swap_exists_action = SEA_NONE; 778 swap_exists_did_quit = TRUE; 779 780 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 781 /* Restore the error/interrupt/exception state if not discarded by a 782 * new aborting error, interrupt, or uncaught exception. */ 783 leave_cleanup(&cs); 784 # endif 785 } 786 else 787 handle_swap_exists(old_curbuf); 788 # endif 789 } 790 #endif 791 792 #if defined(HAS_SWAP_EXISTS_ACTION) || defined(PROTO) 793 /* 794 * Handle the situation of swap_exists_action being set. 795 * It is allowed for "old_curbuf" to be NULL or invalid. 796 */ 797 void 798 handle_swap_exists(old_curbuf) 799 buf_T *old_curbuf; 800 { 801 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 802 cleanup_T cs; 803 # endif 804 #ifdef FEAT_SYN_HL 805 long old_tw = curbuf->b_p_tw; 806 #endif 807 808 if (swap_exists_action == SEA_QUIT) 809 { 810 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 811 /* Reset the error/interrupt/exception state here so that 812 * aborting() returns FALSE when closing a buffer. */ 813 enter_cleanup(&cs); 814 # endif 815 816 /* User selected Quit at ATTENTION prompt. Go back to previous 817 * buffer. If that buffer is gone or the same as the current one, 818 * open a new, empty buffer. */ 819 swap_exists_action = SEA_NONE; /* don't want it again */ 820 swap_exists_did_quit = TRUE; 821 close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE); 822 if (!buf_valid(old_curbuf) || old_curbuf == curbuf) 823 old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); 824 if (old_curbuf != NULL) 825 { 826 enter_buffer(old_curbuf); 827 #ifdef FEAT_SYN_HL 828 if (old_tw != curbuf->b_p_tw) 829 check_colorcolumn(curwin); 830 #endif 831 } 832 /* If "old_curbuf" is NULL we are in big trouble here... */ 833 834 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 835 /* Restore the error/interrupt/exception state if not discarded by a 836 * new aborting error, interrupt, or uncaught exception. */ 837 leave_cleanup(&cs); 838 # endif 839 } 840 else if (swap_exists_action == SEA_RECOVER) 841 { 842 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 843 /* Reset the error/interrupt/exception state here so that 844 * aborting() returns FALSE when closing a buffer. */ 845 enter_cleanup(&cs); 846 # endif 847 848 /* User selected Recover at ATTENTION prompt. */ 849 msg_scroll = TRUE; 850 ml_recover(); 851 MSG_PUTS("\n"); /* don't overwrite the last message */ 852 cmdline_row = msg_row; 853 do_modelines(0); 854 855 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 856 /* Restore the error/interrupt/exception state if not discarded by a 857 * new aborting error, interrupt, or uncaught exception. */ 858 leave_cleanup(&cs); 859 # endif 860 } 861 swap_exists_action = SEA_NONE; 862 } 863 #endif 864 865 #if defined(FEAT_LISTCMDS) || defined(PROTO) 866 /* 867 * do_bufdel() - delete or unload buffer(s) 868 * 869 * addr_count == 0: ":bdel" - delete current buffer 870 * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete 871 * buffer "end_bnr", then any other arguments. 872 * addr_count == 2: ":N,N bdel" - delete buffers in range 873 * 874 * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or 875 * DOBUF_DEL (":bdel") 876 * 877 * Returns error message or NULL 878 */ 879 char_u * 880 do_bufdel(command, arg, addr_count, start_bnr, end_bnr, forceit) 881 int command; 882 char_u *arg; /* pointer to extra arguments */ 883 int addr_count; 884 int start_bnr; /* first buffer number in a range */ 885 int end_bnr; /* buffer nr or last buffer nr in a range */ 886 int forceit; 887 { 888 int do_current = 0; /* delete current buffer? */ 889 int deleted = 0; /* number of buffers deleted */ 890 char_u *errormsg = NULL; /* return value */ 891 int bnr; /* buffer number */ 892 char_u *p; 893 894 if (addr_count == 0) 895 { 896 (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit); 897 } 898 else 899 { 900 if (addr_count == 2) 901 { 902 if (*arg) /* both range and argument is not allowed */ 903 return (char_u *)_(e_trailing); 904 bnr = start_bnr; 905 } 906 else /* addr_count == 1 */ 907 bnr = end_bnr; 908 909 for ( ;!got_int; ui_breakcheck()) 910 { 911 /* 912 * delete the current buffer last, otherwise when the 913 * current buffer is deleted, the next buffer becomes 914 * the current one and will be loaded, which may then 915 * also be deleted, etc. 916 */ 917 if (bnr == curbuf->b_fnum) 918 do_current = bnr; 919 else if (do_buffer(command, DOBUF_FIRST, FORWARD, (int)bnr, 920 forceit) == OK) 921 ++deleted; 922 923 /* 924 * find next buffer number to delete/unload 925 */ 926 if (addr_count == 2) 927 { 928 if (++bnr > end_bnr) 929 break; 930 } 931 else /* addr_count == 1 */ 932 { 933 arg = skipwhite(arg); 934 if (*arg == NUL) 935 break; 936 if (!VIM_ISDIGIT(*arg)) 937 { 938 p = skiptowhite_esc(arg); 939 bnr = buflist_findpat(arg, p, command == DOBUF_WIPE, 940 FALSE, FALSE); 941 if (bnr < 0) /* failed */ 942 break; 943 arg = p; 944 } 945 else 946 bnr = getdigits(&arg); 947 } 948 } 949 if (!got_int && do_current && do_buffer(command, DOBUF_FIRST, 950 FORWARD, do_current, forceit) == OK) 951 ++deleted; 952 953 if (deleted == 0) 954 { 955 if (command == DOBUF_UNLOAD) 956 STRCPY(IObuff, _("E515: No buffers were unloaded")); 957 else if (command == DOBUF_DEL) 958 STRCPY(IObuff, _("E516: No buffers were deleted")); 959 else 960 STRCPY(IObuff, _("E517: No buffers were wiped out")); 961 errormsg = IObuff; 962 } 963 else if (deleted >= p_report) 964 { 965 if (command == DOBUF_UNLOAD) 966 { 967 if (deleted == 1) 968 MSG(_("1 buffer unloaded")); 969 else 970 smsg((char_u *)_("%d buffers unloaded"), deleted); 971 } 972 else if (command == DOBUF_DEL) 973 { 974 if (deleted == 1) 975 MSG(_("1 buffer deleted")); 976 else 977 smsg((char_u *)_("%d buffers deleted"), deleted); 978 } 979 else 980 { 981 if (deleted == 1) 982 MSG(_("1 buffer wiped out")); 983 else 984 smsg((char_u *)_("%d buffers wiped out"), deleted); 985 } 986 } 987 } 988 989 990 return errormsg; 991 } 992 #endif /* FEAT_LISTCMDS */ 993 994 #if defined(FEAT_LISTCMDS) || defined(FEAT_PYTHON) \ 995 || defined(FEAT_PYTHON3) || defined(PROTO) 996 997 static int empty_curbuf __ARGS((int close_others, int forceit, int action)); 998 999 /* 1000 * Make the current buffer empty. 1001 * Used when it is wiped out and it's the last buffer. 1002 */ 1003 static int 1004 empty_curbuf(close_others, forceit, action) 1005 int close_others; 1006 int forceit; 1007 int action; 1008 { 1009 int retval; 1010 buf_T *buf = curbuf; 1011 1012 if (action == DOBUF_UNLOAD) 1013 { 1014 EMSG(_("E90: Cannot unload last buffer")); 1015 return FAIL; 1016 } 1017 1018 if (close_others) 1019 { 1020 /* Close any other windows on this buffer, then make it empty. */ 1021 #ifdef FEAT_WINDOWS 1022 close_windows(buf, TRUE); 1023 #endif 1024 } 1025 1026 setpcmark(); 1027 retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, 1028 forceit ? ECMD_FORCEIT : 0, curwin); 1029 1030 /* 1031 * do_ecmd() may create a new buffer, then we have to delete 1032 * the old one. But do_ecmd() may have done that already, check 1033 * if the buffer still exists. 1034 */ 1035 if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0) 1036 close_buffer(NULL, buf, action, FALSE); 1037 if (!close_others) 1038 need_fileinfo = FALSE; 1039 return retval; 1040 } 1041 /* 1042 * Implementation of the commands for the buffer list. 1043 * 1044 * action == DOBUF_GOTO go to specified buffer 1045 * action == DOBUF_SPLIT split window and go to specified buffer 1046 * action == DOBUF_UNLOAD unload specified buffer(s) 1047 * action == DOBUF_DEL delete specified buffer(s) from buffer list 1048 * action == DOBUF_WIPE delete specified buffer(s) really 1049 * 1050 * start == DOBUF_CURRENT go to "count" buffer from current buffer 1051 * start == DOBUF_FIRST go to "count" buffer from first buffer 1052 * start == DOBUF_LAST go to "count" buffer from last buffer 1053 * start == DOBUF_MOD go to "count" modified buffer from current buffer 1054 * 1055 * Return FAIL or OK. 1056 */ 1057 int 1058 do_buffer(action, start, dir, count, forceit) 1059 int action; 1060 int start; 1061 int dir; /* FORWARD or BACKWARD */ 1062 int count; /* buffer number or number of buffers */ 1063 int forceit; /* TRUE for :...! */ 1064 { 1065 buf_T *buf; 1066 buf_T *bp; 1067 int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL 1068 || action == DOBUF_WIPE); 1069 1070 switch (start) 1071 { 1072 case DOBUF_FIRST: buf = firstbuf; break; 1073 case DOBUF_LAST: buf = lastbuf; break; 1074 default: buf = curbuf; break; 1075 } 1076 if (start == DOBUF_MOD) /* find next modified buffer */ 1077 { 1078 while (count-- > 0) 1079 { 1080 do 1081 { 1082 buf = buf->b_next; 1083 if (buf == NULL) 1084 buf = firstbuf; 1085 } 1086 while (buf != curbuf && !bufIsChanged(buf)); 1087 } 1088 if (!bufIsChanged(buf)) 1089 { 1090 EMSG(_("E84: No modified buffer found")); 1091 return FAIL; 1092 } 1093 } 1094 else if (start == DOBUF_FIRST && count) /* find specified buffer number */ 1095 { 1096 while (buf != NULL && buf->b_fnum != count) 1097 buf = buf->b_next; 1098 } 1099 else 1100 { 1101 bp = NULL; 1102 while (count > 0 || (!unload && !buf->b_p_bl && bp != buf)) 1103 { 1104 /* remember the buffer where we start, we come back there when all 1105 * buffers are unlisted. */ 1106 if (bp == NULL) 1107 bp = buf; 1108 if (dir == FORWARD) 1109 { 1110 buf = buf->b_next; 1111 if (buf == NULL) 1112 buf = firstbuf; 1113 } 1114 else 1115 { 1116 buf = buf->b_prev; 1117 if (buf == NULL) 1118 buf = lastbuf; 1119 } 1120 /* don't count unlisted buffers */ 1121 if (unload || buf->b_p_bl) 1122 { 1123 --count; 1124 bp = NULL; /* use this buffer as new starting point */ 1125 } 1126 if (bp == buf) 1127 { 1128 /* back where we started, didn't find anything. */ 1129 EMSG(_("E85: There is no listed buffer")); 1130 return FAIL; 1131 } 1132 } 1133 } 1134 1135 if (buf == NULL) /* could not find it */ 1136 { 1137 if (start == DOBUF_FIRST) 1138 { 1139 /* don't warn when deleting */ 1140 if (!unload) 1141 EMSGN(_("E86: Buffer %ld does not exist"), count); 1142 } 1143 else if (dir == FORWARD) 1144 EMSG(_("E87: Cannot go beyond last buffer")); 1145 else 1146 EMSG(_("E88: Cannot go before first buffer")); 1147 return FAIL; 1148 } 1149 1150 #ifdef FEAT_GUI 1151 need_mouse_correct = TRUE; 1152 #endif 1153 1154 #ifdef FEAT_LISTCMDS 1155 /* 1156 * delete buffer buf from memory and/or the list 1157 */ 1158 if (unload) 1159 { 1160 int forward; 1161 1162 /* When unloading or deleting a buffer that's already unloaded and 1163 * unlisted: fail silently. */ 1164 if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl) 1165 return FAIL; 1166 1167 if (!forceit && bufIsChanged(buf)) 1168 { 1169 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) 1170 if ((p_confirm || cmdmod.confirm) && p_write) 1171 { 1172 dialog_changed(buf, FALSE); 1173 # ifdef FEAT_AUTOCMD 1174 if (!buf_valid(buf)) 1175 /* Autocommand deleted buffer, oops! It's not changed 1176 * now. */ 1177 return FAIL; 1178 # endif 1179 /* If it's still changed fail silently, the dialog already 1180 * mentioned why it fails. */ 1181 if (bufIsChanged(buf)) 1182 return FAIL; 1183 } 1184 else 1185 #endif 1186 { 1187 EMSGN(_("E89: No write since last change for buffer %ld (add ! to override)"), 1188 buf->b_fnum); 1189 return FAIL; 1190 } 1191 } 1192 1193 /* 1194 * If deleting the last (listed) buffer, make it empty. 1195 * The last (listed) buffer cannot be unloaded. 1196 */ 1197 for (bp = firstbuf; bp != NULL; bp = bp->b_next) 1198 if (bp->b_p_bl && bp != buf) 1199 break; 1200 if (bp == NULL && buf == curbuf) 1201 return empty_curbuf(TRUE, forceit, action); 1202 1203 #ifdef FEAT_WINDOWS 1204 /* 1205 * If the deleted buffer is the current one, close the current window 1206 * (unless it's the only window). Repeat this so long as we end up in 1207 * a window with this buffer. 1208 */ 1209 while (buf == curbuf 1210 # ifdef FEAT_AUTOCMD 1211 && !(curwin->w_closing || curwin->w_buffer->b_closing) 1212 # endif 1213 && (firstwin != lastwin || first_tabpage->tp_next != NULL)) 1214 { 1215 if (win_close(curwin, FALSE) == FAIL) 1216 break; 1217 } 1218 #endif 1219 1220 /* 1221 * If the buffer to be deleted is not the current one, delete it here. 1222 */ 1223 if (buf != curbuf) 1224 { 1225 #ifdef FEAT_WINDOWS 1226 close_windows(buf, FALSE); 1227 #endif 1228 if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0) 1229 close_buffer(NULL, buf, action, FALSE); 1230 return OK; 1231 } 1232 1233 /* 1234 * Deleting the current buffer: Need to find another buffer to go to. 1235 * There should be another, otherwise it would have been handled 1236 * above. However, autocommands may have deleted all buffers. 1237 * First use au_new_curbuf, if it is valid. 1238 * Then prefer the buffer we most recently visited. 1239 * Else try to find one that is loaded, after the current buffer, 1240 * then before the current buffer. 1241 * Finally use any buffer. 1242 */ 1243 buf = NULL; /* selected buffer */ 1244 bp = NULL; /* used when no loaded buffer found */ 1245 #ifdef FEAT_AUTOCMD 1246 if (au_new_curbuf != NULL && buf_valid(au_new_curbuf)) 1247 buf = au_new_curbuf; 1248 # ifdef FEAT_JUMPLIST 1249 else 1250 # endif 1251 #endif 1252 #ifdef FEAT_JUMPLIST 1253 if (curwin->w_jumplistlen > 0) 1254 { 1255 int jumpidx; 1256 1257 jumpidx = curwin->w_jumplistidx - 1; 1258 if (jumpidx < 0) 1259 jumpidx = curwin->w_jumplistlen - 1; 1260 1261 forward = jumpidx; 1262 while (jumpidx != curwin->w_jumplistidx) 1263 { 1264 buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); 1265 if (buf != NULL) 1266 { 1267 if (buf == curbuf || !buf->b_p_bl) 1268 buf = NULL; /* skip current and unlisted bufs */ 1269 else if (buf->b_ml.ml_mfp == NULL) 1270 { 1271 /* skip unloaded buf, but may keep it for later */ 1272 if (bp == NULL) 1273 bp = buf; 1274 buf = NULL; 1275 } 1276 } 1277 if (buf != NULL) /* found a valid buffer: stop searching */ 1278 break; 1279 /* advance to older entry in jump list */ 1280 if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) 1281 break; 1282 if (--jumpidx < 0) 1283 jumpidx = curwin->w_jumplistlen - 1; 1284 if (jumpidx == forward) /* List exhausted for sure */ 1285 break; 1286 } 1287 } 1288 #endif 1289 1290 if (buf == NULL) /* No previous buffer, Try 2'nd approach */ 1291 { 1292 forward = TRUE; 1293 buf = curbuf->b_next; 1294 for (;;) 1295 { 1296 if (buf == NULL) 1297 { 1298 if (!forward) /* tried both directions */ 1299 break; 1300 buf = curbuf->b_prev; 1301 forward = FALSE; 1302 continue; 1303 } 1304 /* in non-help buffer, try to skip help buffers, and vv */ 1305 if (buf->b_help == curbuf->b_help && buf->b_p_bl) 1306 { 1307 if (buf->b_ml.ml_mfp != NULL) /* found loaded buffer */ 1308 break; 1309 if (bp == NULL) /* remember unloaded buf for later */ 1310 bp = buf; 1311 } 1312 if (forward) 1313 buf = buf->b_next; 1314 else 1315 buf = buf->b_prev; 1316 } 1317 } 1318 if (buf == NULL) /* No loaded buffer, use unloaded one */ 1319 buf = bp; 1320 if (buf == NULL) /* No loaded buffer, find listed one */ 1321 { 1322 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 1323 if (buf->b_p_bl && buf != curbuf) 1324 break; 1325 } 1326 if (buf == NULL) /* Still no buffer, just take one */ 1327 { 1328 if (curbuf->b_next != NULL) 1329 buf = curbuf->b_next; 1330 else 1331 buf = curbuf->b_prev; 1332 } 1333 } 1334 1335 if (buf == NULL) 1336 { 1337 /* Autocommands must have wiped out all other buffers. Only option 1338 * now is to make the current buffer empty. */ 1339 return empty_curbuf(FALSE, forceit, action); 1340 } 1341 1342 /* 1343 * make buf current buffer 1344 */ 1345 if (action == DOBUF_SPLIT) /* split window first */ 1346 { 1347 # ifdef FEAT_WINDOWS 1348 /* If 'switchbuf' contains "useopen": jump to first window containing 1349 * "buf" if one exists */ 1350 if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf)) 1351 return OK; 1352 /* If 'switchbuf' contains "usetab": jump to first window in any tab 1353 * page containing "buf" if one exists */ 1354 if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf)) 1355 return OK; 1356 if (win_split(0, 0) == FAIL) 1357 # endif 1358 return FAIL; 1359 } 1360 #endif 1361 1362 /* go to current buffer - nothing to do */ 1363 if (buf == curbuf) 1364 return OK; 1365 1366 /* 1367 * Check if the current buffer may be abandoned. 1368 */ 1369 if (action == DOBUF_GOTO && !can_abandon(curbuf, forceit)) 1370 { 1371 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) 1372 if ((p_confirm || cmdmod.confirm) && p_write) 1373 { 1374 dialog_changed(curbuf, FALSE); 1375 # ifdef FEAT_AUTOCMD 1376 if (!buf_valid(buf)) 1377 /* Autocommand deleted buffer, oops! */ 1378 return FAIL; 1379 # endif 1380 } 1381 if (bufIsChanged(curbuf)) 1382 #endif 1383 { 1384 EMSG(_(e_nowrtmsg)); 1385 return FAIL; 1386 } 1387 } 1388 1389 /* Go to the other buffer. */ 1390 set_curbuf(buf, action); 1391 1392 #if defined(FEAT_LISTCMDS) \ 1393 && (defined(FEAT_SCROLLBIND) || defined(FEAT_CURSORBIND)) 1394 if (action == DOBUF_SPLIT) 1395 { 1396 RESET_BINDING(curwin); /* reset 'scrollbind' and 'cursorbind' */ 1397 } 1398 #endif 1399 1400 #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 1401 if (aborting()) /* autocmds may abort script processing */ 1402 return FAIL; 1403 #endif 1404 1405 return OK; 1406 } 1407 #endif 1408 1409 /* 1410 * Set current buffer to "buf". Executes autocommands and closes current 1411 * buffer. "action" tells how to close the current buffer: 1412 * DOBUF_GOTO free or hide it 1413 * DOBUF_SPLIT nothing 1414 * DOBUF_UNLOAD unload it 1415 * DOBUF_DEL delete it 1416 * DOBUF_WIPE wipe it out 1417 */ 1418 void 1419 set_curbuf(buf, action) 1420 buf_T *buf; 1421 int action; 1422 { 1423 buf_T *prevbuf; 1424 int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL 1425 || action == DOBUF_WIPE); 1426 #ifdef FEAT_SYN_HL 1427 long old_tw = curbuf->b_p_tw; 1428 #endif 1429 1430 setpcmark(); 1431 if (!cmdmod.keepalt) 1432 curwin->w_alt_fnum = curbuf->b_fnum; /* remember alternate file */ 1433 buflist_altfpos(curwin); /* remember curpos */ 1434 1435 #ifdef FEAT_VISUAL 1436 /* Don't restart Select mode after switching to another buffer. */ 1437 VIsual_reselect = FALSE; 1438 #endif 1439 1440 /* close_windows() or apply_autocmds() may change curbuf */ 1441 prevbuf = curbuf; 1442 1443 #ifdef FEAT_AUTOCMD 1444 apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); 1445 # ifdef FEAT_EVAL 1446 if (buf_valid(prevbuf) && !aborting()) 1447 # else 1448 if (buf_valid(prevbuf)) 1449 # endif 1450 #endif 1451 { 1452 #ifdef FEAT_SYN_HL 1453 if (prevbuf == curwin->w_buffer) 1454 reset_synblock(curwin); 1455 #endif 1456 #ifdef FEAT_WINDOWS 1457 if (unload) 1458 close_windows(prevbuf, FALSE); 1459 #endif 1460 #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 1461 if (buf_valid(prevbuf) && !aborting()) 1462 #else 1463 if (buf_valid(prevbuf)) 1464 #endif 1465 { 1466 #ifdef FEAT_WINDOWS 1467 win_T *previouswin = curwin; 1468 #endif 1469 if (prevbuf == curbuf) 1470 u_sync(FALSE); 1471 close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf, 1472 unload ? action : (action == DOBUF_GOTO 1473 && !P_HID(prevbuf) 1474 && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0, FALSE); 1475 #ifdef FEAT_WINDOWS 1476 if (curwin != previouswin && win_valid(previouswin)) 1477 /* autocommands changed curwin, Grr! */ 1478 curwin = previouswin; 1479 #endif 1480 } 1481 } 1482 #ifdef FEAT_AUTOCMD 1483 /* An autocommand may have deleted "buf", already entered it (e.g., when 1484 * it did ":bunload") or aborted the script processing! 1485 * If curwin->w_buffer is null, enter_buffer() will make it valid again */ 1486 if ((buf_valid(buf) && buf != curbuf 1487 # ifdef FEAT_EVAL 1488 && !aborting() 1489 # endif 1490 # ifdef FEAT_WINDOWS 1491 ) || curwin->w_buffer == NULL 1492 # endif 1493 ) 1494 #endif 1495 { 1496 enter_buffer(buf); 1497 #ifdef FEAT_SYN_HL 1498 if (old_tw != curbuf->b_p_tw) 1499 check_colorcolumn(curwin); 1500 #endif 1501 } 1502 } 1503 1504 /* 1505 * Enter a new current buffer. 1506 * Old curbuf must have been abandoned already! This also means "curbuf" may 1507 * be pointing to freed memory. 1508 */ 1509 void 1510 enter_buffer(buf) 1511 buf_T *buf; 1512 { 1513 /* Copy buffer and window local option values. Not for a help buffer. */ 1514 buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); 1515 if (!buf->b_help) 1516 get_winopts(buf); 1517 #ifdef FEAT_FOLDING 1518 else 1519 /* Remove all folds in the window. */ 1520 clearFolding(curwin); 1521 foldUpdateAll(curwin); /* update folds (later). */ 1522 #endif 1523 1524 /* Get the buffer in the current window. */ 1525 curwin->w_buffer = buf; 1526 curbuf = buf; 1527 ++curbuf->b_nwindows; 1528 1529 #ifdef FEAT_DIFF 1530 if (curwin->w_p_diff) 1531 diff_buf_add(curbuf); 1532 #endif 1533 1534 #ifdef FEAT_SYN_HL 1535 curwin->w_s = &(buf->b_s); 1536 #endif 1537 1538 /* Cursor on first line by default. */ 1539 curwin->w_cursor.lnum = 1; 1540 curwin->w_cursor.col = 0; 1541 #ifdef FEAT_VIRTUALEDIT 1542 curwin->w_cursor.coladd = 0; 1543 #endif 1544 curwin->w_set_curswant = TRUE; 1545 #ifdef FEAT_AUTOCMD 1546 curwin->w_topline_was_set = FALSE; 1547 #endif 1548 1549 /* mark cursor position as being invalid */ 1550 curwin->w_valid = 0; 1551 1552 /* Make sure the buffer is loaded. */ 1553 if (curbuf->b_ml.ml_mfp == NULL) /* need to load the file */ 1554 { 1555 #ifdef FEAT_AUTOCMD 1556 /* If there is no filetype, allow for detecting one. Esp. useful for 1557 * ":ball" used in a autocommand. If there already is a filetype we 1558 * might prefer to keep it. */ 1559 if (*curbuf->b_p_ft == NUL) 1560 did_filetype = FALSE; 1561 #endif 1562 1563 open_buffer(FALSE, NULL, 0); 1564 } 1565 else 1566 { 1567 if (!msg_silent) 1568 need_fileinfo = TRUE; /* display file info after redraw */ 1569 (void)buf_check_timestamp(curbuf, FALSE); /* check if file changed */ 1570 #ifdef FEAT_AUTOCMD 1571 curwin->w_topline = 1; 1572 # ifdef FEAT_DIFF 1573 curwin->w_topfill = 0; 1574 # endif 1575 apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); 1576 apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); 1577 #endif 1578 } 1579 1580 /* If autocommands did not change the cursor position, restore cursor lnum 1581 * and possibly cursor col. */ 1582 if (curwin->w_cursor.lnum == 1 && inindent(0)) 1583 buflist_getfpos(); 1584 1585 check_arg_idx(curwin); /* check for valid arg_idx */ 1586 #ifdef FEAT_TITLE 1587 maketitle(); 1588 #endif 1589 #ifdef FEAT_AUTOCMD 1590 /* when autocmds didn't change it */ 1591 if (curwin->w_topline == 1 && !curwin->w_topline_was_set) 1592 #endif 1593 scroll_cursor_halfway(FALSE); /* redisplay at correct position */ 1594 1595 #ifdef FEAT_NETBEANS_INTG 1596 /* Send fileOpened event because we've changed buffers. */ 1597 netbeans_file_activated(curbuf); 1598 #endif 1599 1600 /* Change directories when the 'acd' option is set. */ 1601 DO_AUTOCHDIR 1602 1603 #ifdef FEAT_KEYMAP 1604 if (curbuf->b_kmap_state & KEYMAP_INIT) 1605 (void)keymap_init(); 1606 #endif 1607 #ifdef FEAT_SPELL 1608 /* May need to set the spell language. Can only do this after the buffer 1609 * has been properly setup. */ 1610 if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) 1611 (void)did_set_spelllang(curwin); 1612 #endif 1613 1614 redraw_later(NOT_VALID); 1615 } 1616 1617 #if defined(FEAT_AUTOCHDIR) || defined(PROTO) 1618 /* 1619 * Change to the directory of the current buffer. 1620 */ 1621 void 1622 do_autochdir() 1623 { 1624 if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK) 1625 shorten_fnames(TRUE); 1626 } 1627 #endif 1628 1629 /* 1630 * functions for dealing with the buffer list 1631 */ 1632 1633 /* 1634 * Add a file name to the buffer list. Return a pointer to the buffer. 1635 * If the same file name already exists return a pointer to that buffer. 1636 * If it does not exist, or if fname == NULL, a new entry is created. 1637 * If (flags & BLN_CURBUF) is TRUE, may use current buffer. 1638 * If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list. 1639 * If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer. 1640 * This is the ONLY way to create a new buffer. 1641 */ 1642 static int top_file_num = 1; /* highest file number */ 1643 1644 buf_T * 1645 buflist_new(ffname, sfname, lnum, flags) 1646 char_u *ffname; /* full path of fname or relative */ 1647 char_u *sfname; /* short fname or NULL */ 1648 linenr_T lnum; /* preferred cursor line */ 1649 int flags; /* BLN_ defines */ 1650 { 1651 buf_T *buf; 1652 #ifdef UNIX 1653 struct stat st; 1654 #endif 1655 1656 fname_expand(curbuf, &ffname, &sfname); /* will allocate ffname */ 1657 1658 /* 1659 * If file name already exists in the list, update the entry. 1660 */ 1661 #ifdef UNIX 1662 /* On Unix we can use inode numbers when the file exists. Works better 1663 * for hard links. */ 1664 if (sfname == NULL || mch_stat((char *)sfname, &st) < 0) 1665 st.st_dev = (dev_T)-1; 1666 #endif 1667 if (ffname != NULL && !(flags & BLN_DUMMY) && (buf = 1668 #ifdef UNIX 1669 buflist_findname_stat(ffname, &st) 1670 #else 1671 buflist_findname(ffname) 1672 #endif 1673 ) != NULL) 1674 { 1675 vim_free(ffname); 1676 if (lnum != 0) 1677 buflist_setfpos(buf, curwin, lnum, (colnr_T)0, FALSE); 1678 /* copy the options now, if 'cpo' doesn't have 's' and not done 1679 * already */ 1680 buf_copy_options(buf, 0); 1681 if ((flags & BLN_LISTED) && !buf->b_p_bl) 1682 { 1683 buf->b_p_bl = TRUE; 1684 #ifdef FEAT_AUTOCMD 1685 if (!(flags & BLN_DUMMY)) 1686 apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf); 1687 #endif 1688 } 1689 return buf; 1690 } 1691 1692 /* 1693 * If the current buffer has no name and no contents, use the current 1694 * buffer. Otherwise: Need to allocate a new buffer structure. 1695 * 1696 * This is the ONLY place where a new buffer structure is allocated! 1697 * (A spell file buffer is allocated in spell.c, but that's not a normal 1698 * buffer.) 1699 */ 1700 buf = NULL; 1701 if ((flags & BLN_CURBUF) 1702 && curbuf != NULL 1703 && curbuf->b_ffname == NULL 1704 && curbuf->b_nwindows <= 1 1705 && (curbuf->b_ml.ml_mfp == NULL || bufempty())) 1706 { 1707 buf = curbuf; 1708 #ifdef FEAT_AUTOCMD 1709 /* It's like this buffer is deleted. Watch out for autocommands that 1710 * change curbuf! If that happens, allocate a new buffer anyway. */ 1711 if (curbuf->b_p_bl) 1712 apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); 1713 if (buf == curbuf) 1714 apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf); 1715 # ifdef FEAT_EVAL 1716 if (aborting()) /* autocmds may abort script processing */ 1717 return NULL; 1718 # endif 1719 #endif 1720 #ifdef FEAT_QUICKFIX 1721 # ifdef FEAT_AUTOCMD 1722 if (buf == curbuf) 1723 # endif 1724 { 1725 /* Make sure 'bufhidden' and 'buftype' are empty */ 1726 clear_string_option(&buf->b_p_bh); 1727 clear_string_option(&buf->b_p_bt); 1728 } 1729 #endif 1730 } 1731 if (buf != curbuf || curbuf == NULL) 1732 { 1733 buf = (buf_T *)alloc_clear((unsigned)sizeof(buf_T)); 1734 if (buf == NULL) 1735 { 1736 vim_free(ffname); 1737 return NULL; 1738 } 1739 #ifdef FEAT_EVAL 1740 /* init b: variables */ 1741 buf->b_vars = dict_alloc(); 1742 if (buf->b_vars == NULL) 1743 { 1744 vim_free(ffname); 1745 vim_free(buf); 1746 return NULL; 1747 } 1748 init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); 1749 #endif 1750 } 1751 1752 if (ffname != NULL) 1753 { 1754 buf->b_ffname = ffname; 1755 buf->b_sfname = vim_strsave(sfname); 1756 } 1757 1758 clear_wininfo(buf); 1759 buf->b_wininfo = (wininfo_T *)alloc_clear((unsigned)sizeof(wininfo_T)); 1760 1761 if ((ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) 1762 || buf->b_wininfo == NULL) 1763 { 1764 vim_free(buf->b_ffname); 1765 buf->b_ffname = NULL; 1766 vim_free(buf->b_sfname); 1767 buf->b_sfname = NULL; 1768 if (buf != curbuf) 1769 free_buffer(buf); 1770 return NULL; 1771 } 1772 1773 if (buf == curbuf) 1774 { 1775 /* free all things allocated for this buffer */ 1776 buf_freeall(buf, 0); 1777 if (buf != curbuf) /* autocommands deleted the buffer! */ 1778 return NULL; 1779 #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 1780 if (aborting()) /* autocmds may abort script processing */ 1781 return NULL; 1782 #endif 1783 /* buf->b_nwindows = 0; why was this here? */ 1784 free_buffer_stuff(buf, FALSE); /* delete local variables et al. */ 1785 1786 /* Init the options. */ 1787 buf->b_p_initialized = FALSE; 1788 buf_copy_options(buf, BCO_ENTER); 1789 1790 #ifdef FEAT_KEYMAP 1791 /* need to reload lmaps and set b:keymap_name */ 1792 curbuf->b_kmap_state |= KEYMAP_INIT; 1793 #endif 1794 } 1795 else 1796 { 1797 /* 1798 * put new buffer at the end of the buffer list 1799 */ 1800 buf->b_next = NULL; 1801 if (firstbuf == NULL) /* buffer list is empty */ 1802 { 1803 buf->b_prev = NULL; 1804 firstbuf = buf; 1805 } 1806 else /* append new buffer at end of list */ 1807 { 1808 lastbuf->b_next = buf; 1809 buf->b_prev = lastbuf; 1810 } 1811 lastbuf = buf; 1812 1813 buf->b_fnum = top_file_num++; 1814 if (top_file_num < 0) /* wrap around (may cause duplicates) */ 1815 { 1816 EMSG(_("W14: Warning: List of file names overflow")); 1817 if (emsg_silent == 0) 1818 { 1819 out_flush(); 1820 ui_delay(3000L, TRUE); /* make sure it is noticed */ 1821 } 1822 top_file_num = 1; 1823 } 1824 1825 /* 1826 * Always copy the options from the current buffer. 1827 */ 1828 buf_copy_options(buf, BCO_ALWAYS); 1829 } 1830 1831 buf->b_wininfo->wi_fpos.lnum = lnum; 1832 buf->b_wininfo->wi_win = curwin; 1833 1834 #ifdef FEAT_SYN_HL 1835 hash_init(&buf->b_s.b_keywtab); 1836 hash_init(&buf->b_s.b_keywtab_ic); 1837 #endif 1838 1839 buf->b_fname = buf->b_sfname; 1840 #ifdef UNIX 1841 if (st.st_dev == (dev_T)-1) 1842 buf->b_dev_valid = FALSE; 1843 else 1844 { 1845 buf->b_dev_valid = TRUE; 1846 buf->b_dev = st.st_dev; 1847 buf->b_ino = st.st_ino; 1848 } 1849 #endif 1850 buf->b_u_synced = TRUE; 1851 buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; 1852 if (flags & BLN_DUMMY) 1853 buf->b_flags |= BF_DUMMY; 1854 buf_clear_file(buf); 1855 clrallmarks(buf); /* clear marks */ 1856 fmarks_check_names(buf); /* check file marks for this file */ 1857 buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */ 1858 #ifdef FEAT_AUTOCMD 1859 if (!(flags & BLN_DUMMY)) 1860 { 1861 apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf); 1862 if (flags & BLN_LISTED) 1863 apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf); 1864 # ifdef FEAT_EVAL 1865 if (aborting()) /* autocmds may abort script processing */ 1866 return NULL; 1867 # endif 1868 } 1869 #endif 1870 1871 return buf; 1872 } 1873 1874 /* 1875 * Free the memory for the options of a buffer. 1876 * If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and 1877 * 'fileencoding'. 1878 */ 1879 void 1880 free_buf_options(buf, free_p_ff) 1881 buf_T *buf; 1882 int free_p_ff; 1883 { 1884 if (free_p_ff) 1885 { 1886 #ifdef FEAT_MBYTE 1887 clear_string_option(&buf->b_p_fenc); 1888 #endif 1889 clear_string_option(&buf->b_p_ff); 1890 #ifdef FEAT_QUICKFIX 1891 clear_string_option(&buf->b_p_bh); 1892 clear_string_option(&buf->b_p_bt); 1893 #endif 1894 } 1895 #ifdef FEAT_FIND_ID 1896 clear_string_option(&buf->b_p_def); 1897 clear_string_option(&buf->b_p_inc); 1898 # ifdef FEAT_EVAL 1899 clear_string_option(&buf->b_p_inex); 1900 # endif 1901 #endif 1902 #if defined(FEAT_CINDENT) && defined(FEAT_EVAL) 1903 clear_string_option(&buf->b_p_inde); 1904 clear_string_option(&buf->b_p_indk); 1905 #endif 1906 #if defined(FEAT_BEVAL) && defined(FEAT_EVAL) 1907 clear_string_option(&buf->b_p_bexpr); 1908 #endif 1909 #if defined(FEAT_CRYPT) 1910 clear_string_option(&buf->b_p_cm); 1911 #endif 1912 #if defined(FEAT_EVAL) 1913 clear_string_option(&buf->b_p_fex); 1914 #endif 1915 #ifdef FEAT_CRYPT 1916 clear_string_option(&buf->b_p_key); 1917 #endif 1918 clear_string_option(&buf->b_p_kp); 1919 clear_string_option(&buf->b_p_mps); 1920 clear_string_option(&buf->b_p_fo); 1921 clear_string_option(&buf->b_p_flp); 1922 clear_string_option(&buf->b_p_isk); 1923 #ifdef FEAT_KEYMAP 1924 clear_string_option(&buf->b_p_keymap); 1925 ga_clear(&buf->b_kmap_ga); 1926 #endif 1927 #ifdef FEAT_COMMENTS 1928 clear_string_option(&buf->b_p_com); 1929 #endif 1930 #ifdef FEAT_FOLDING 1931 clear_string_option(&buf->b_p_cms); 1932 #endif 1933 clear_string_option(&buf->b_p_nf); 1934 #ifdef FEAT_SYN_HL 1935 clear_string_option(&buf->b_p_syn); 1936 #endif 1937 #ifdef FEAT_SPELL 1938 clear_string_option(&buf->b_s.b_p_spc); 1939 clear_string_option(&buf->b_s.b_p_spf); 1940 vim_regfree(buf->b_s.b_cap_prog); 1941 buf->b_s.b_cap_prog = NULL; 1942 clear_string_option(&buf->b_s.b_p_spl); 1943 #endif 1944 #ifdef FEAT_SEARCHPATH 1945 clear_string_option(&buf->b_p_sua); 1946 #endif 1947 #ifdef FEAT_AUTOCMD 1948 clear_string_option(&buf->b_p_ft); 1949 #endif 1950 #ifdef FEAT_CINDENT 1951 clear_string_option(&buf->b_p_cink); 1952 clear_string_option(&buf->b_p_cino); 1953 #endif 1954 #if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT) 1955 clear_string_option(&buf->b_p_cinw); 1956 #endif 1957 #ifdef FEAT_INS_EXPAND 1958 clear_string_option(&buf->b_p_cpt); 1959 #endif 1960 #ifdef FEAT_COMPL_FUNC 1961 clear_string_option(&buf->b_p_cfu); 1962 clear_string_option(&buf->b_p_ofu); 1963 #endif 1964 #ifdef FEAT_QUICKFIX 1965 clear_string_option(&buf->b_p_gp); 1966 clear_string_option(&buf->b_p_mp); 1967 clear_string_option(&buf->b_p_efm); 1968 #endif 1969 clear_string_option(&buf->b_p_ep); 1970 clear_string_option(&buf->b_p_path); 1971 clear_string_option(&buf->b_p_tags); 1972 #ifdef FEAT_INS_EXPAND 1973 clear_string_option(&buf->b_p_dict); 1974 clear_string_option(&buf->b_p_tsr); 1975 #endif 1976 #ifdef FEAT_TEXTOBJ 1977 clear_string_option(&buf->b_p_qe); 1978 #endif 1979 buf->b_p_ar = -1; 1980 buf->b_p_ul = NO_LOCAL_UNDOLEVEL; 1981 #ifdef FEAT_LISP 1982 clear_string_option(&buf->b_p_lw); 1983 #endif 1984 } 1985 1986 /* 1987 * get alternate file n 1988 * set linenr to lnum or altfpos.lnum if lnum == 0 1989 * also set cursor column to altfpos.col if 'startofline' is not set. 1990 * if (options & GETF_SETMARK) call setpcmark() 1991 * if (options & GETF_ALT) we are jumping to an alternate file. 1992 * if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping 1993 * 1994 * return FAIL for failure, OK for success 1995 */ 1996 int 1997 buflist_getfile(n, lnum, options, forceit) 1998 int n; 1999 linenr_T lnum; 2000 int options; 2001 int forceit; 2002 { 2003 buf_T *buf; 2004 #ifdef FEAT_WINDOWS 2005 win_T *wp = NULL; 2006 #endif 2007 pos_T *fpos; 2008 colnr_T col; 2009 2010 buf = buflist_findnr(n); 2011 if (buf == NULL) 2012 { 2013 if ((options & GETF_ALT) && n == 0) 2014 EMSG(_(e_noalt)); 2015 else 2016 EMSGN(_("E92: Buffer %ld not found"), n); 2017 return FAIL; 2018 } 2019 2020 /* if alternate file is the current buffer, nothing to do */ 2021 if (buf == curbuf) 2022 return OK; 2023 2024 if (text_locked()) 2025 { 2026 text_locked_msg(); 2027 return FAIL; 2028 } 2029 #ifdef FEAT_AUTOCMD 2030 if (curbuf_locked()) 2031 return FAIL; 2032 #endif 2033 2034 /* altfpos may be changed by getfile(), get it now */ 2035 if (lnum == 0) 2036 { 2037 fpos = buflist_findfpos(buf); 2038 lnum = fpos->lnum; 2039 col = fpos->col; 2040 } 2041 else 2042 col = 0; 2043 2044 #ifdef FEAT_WINDOWS 2045 if (options & GETF_SWITCH) 2046 { 2047 /* If 'switchbuf' contains "useopen": jump to first window containing 2048 * "buf" if one exists */ 2049 if (swb_flags & SWB_USEOPEN) 2050 wp = buf_jump_open_win(buf); 2051 /* If 'switchbuf' contains "usetab": jump to first window in any tab 2052 * page containing "buf" if one exists */ 2053 if (wp == NULL && (swb_flags & SWB_USETAB)) 2054 wp = buf_jump_open_tab(buf); 2055 /* If 'switchbuf' contains "split" or "newtab" and the current buffer 2056 * isn't empty: open new window */ 2057 if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty()) 2058 { 2059 if (swb_flags & SWB_NEWTAB) /* Open in a new tab */ 2060 tabpage_new(); 2061 else if (win_split(0, 0) == FAIL) /* Open in a new window */ 2062 return FAIL; 2063 RESET_BINDING(curwin); 2064 } 2065 } 2066 #endif 2067 2068 ++RedrawingDisabled; 2069 if (getfile(buf->b_fnum, NULL, NULL, (options & GETF_SETMARK), 2070 lnum, forceit) <= 0) 2071 { 2072 --RedrawingDisabled; 2073 2074 /* cursor is at to BOL and w_cursor.lnum is checked due to getfile() */ 2075 if (!p_sol && col != 0) 2076 { 2077 curwin->w_cursor.col = col; 2078 check_cursor_col(); 2079 #ifdef FEAT_VIRTUALEDIT 2080 curwin->w_cursor.coladd = 0; 2081 #endif 2082 curwin->w_set_curswant = TRUE; 2083 } 2084 return OK; 2085 } 2086 --RedrawingDisabled; 2087 return FAIL; 2088 } 2089 2090 /* 2091 * go to the last know line number for the current buffer 2092 */ 2093 void 2094 buflist_getfpos() 2095 { 2096 pos_T *fpos; 2097 2098 fpos = buflist_findfpos(curbuf); 2099 2100 curwin->w_cursor.lnum = fpos->lnum; 2101 check_cursor_lnum(); 2102 2103 if (p_sol) 2104 curwin->w_cursor.col = 0; 2105 else 2106 { 2107 curwin->w_cursor.col = fpos->col; 2108 check_cursor_col(); 2109 #ifdef FEAT_VIRTUALEDIT 2110 curwin->w_cursor.coladd = 0; 2111 #endif 2112 curwin->w_set_curswant = TRUE; 2113 } 2114 } 2115 2116 #if defined(FEAT_QUICKFIX) || defined(FEAT_EVAL) || defined(PROTO) 2117 /* 2118 * Find file in buffer list by name (it has to be for the current window). 2119 * Returns NULL if not found. 2120 */ 2121 buf_T * 2122 buflist_findname_exp(fname) 2123 char_u *fname; 2124 { 2125 char_u *ffname; 2126 buf_T *buf = NULL; 2127 2128 /* First make the name into a full path name */ 2129 ffname = FullName_save(fname, 2130 #ifdef UNIX 2131 TRUE /* force expansion, get rid of symbolic links */ 2132 #else 2133 FALSE 2134 #endif 2135 ); 2136 if (ffname != NULL) 2137 { 2138 buf = buflist_findname(ffname); 2139 vim_free(ffname); 2140 } 2141 return buf; 2142 } 2143 #endif 2144 2145 /* 2146 * Find file in buffer list by name (it has to be for the current window). 2147 * "ffname" must have a full path. 2148 * Skips dummy buffers. 2149 * Returns NULL if not found. 2150 */ 2151 buf_T * 2152 buflist_findname(ffname) 2153 char_u *ffname; 2154 { 2155 #ifdef UNIX 2156 struct stat st; 2157 2158 if (mch_stat((char *)ffname, &st) < 0) 2159 st.st_dev = (dev_T)-1; 2160 return buflist_findname_stat(ffname, &st); 2161 } 2162 2163 /* 2164 * Same as buflist_findname(), but pass the stat structure to avoid getting it 2165 * twice for the same file. 2166 * Returns NULL if not found. 2167 */ 2168 static buf_T * 2169 buflist_findname_stat(ffname, stp) 2170 char_u *ffname; 2171 struct stat *stp; 2172 { 2173 #endif 2174 buf_T *buf; 2175 2176 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 2177 if ((buf->b_flags & BF_DUMMY) == 0 && !otherfile_buf(buf, ffname 2178 #ifdef UNIX 2179 , stp 2180 #endif 2181 )) 2182 return buf; 2183 return NULL; 2184 } 2185 2186 #if defined(FEAT_LISTCMDS) || defined(FEAT_EVAL) || defined(FEAT_PERL) \ 2187 || defined(PROTO) 2188 /* 2189 * Find file in buffer list by a regexp pattern. 2190 * Return fnum of the found buffer. 2191 * Return < 0 for error. 2192 */ 2193 int 2194 buflist_findpat(pattern, pattern_end, unlisted, diffmode, curtab_only) 2195 char_u *pattern; 2196 char_u *pattern_end; /* pointer to first char after pattern */ 2197 int unlisted; /* find unlisted buffers */ 2198 int diffmode UNUSED; /* find diff-mode buffers only */ 2199 int curtab_only; /* find buffers in current tab only */ 2200 { 2201 buf_T *buf; 2202 regprog_T *prog; 2203 int match = -1; 2204 int find_listed; 2205 char_u *pat; 2206 char_u *patend; 2207 int attempt; 2208 char_u *p; 2209 int toggledollar; 2210 2211 if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) 2212 { 2213 if (*pattern == '%') 2214 match = curbuf->b_fnum; 2215 else 2216 match = curwin->w_alt_fnum; 2217 #ifdef FEAT_DIFF 2218 if (diffmode && !diff_mode_buf(buflist_findnr(match))) 2219 match = -1; 2220 #endif 2221 } 2222 2223 /* 2224 * Try four ways of matching a listed buffer: 2225 * attempt == 0: without '^' or '$' (at any position) 2226 * attempt == 1: with '^' at start (only at position 0) 2227 * attempt == 2: with '$' at end (only match at end) 2228 * attempt == 3: with '^' at start and '$' at end (only full match) 2229 * Repeat this for finding an unlisted buffer if there was no matching 2230 * listed buffer. 2231 */ 2232 else 2233 { 2234 pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, FALSE); 2235 if (pat == NULL) 2236 return -1; 2237 patend = pat + STRLEN(pat) - 1; 2238 toggledollar = (patend > pat && *patend == '$'); 2239 2240 /* First try finding a listed buffer. If not found and "unlisted" 2241 * is TRUE, try finding an unlisted buffer. */ 2242 find_listed = TRUE; 2243 for (;;) 2244 { 2245 for (attempt = 0; attempt <= 3; ++attempt) 2246 { 2247 /* may add '^' and '$' */ 2248 if (toggledollar) 2249 *patend = (attempt < 2) ? NUL : '$'; /* add/remove '$' */ 2250 p = pat; 2251 if (*p == '^' && !(attempt & 1)) /* add/remove '^' */ 2252 ++p; 2253 prog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); 2254 if (prog == NULL) 2255 { 2256 vim_free(pat); 2257 return -1; 2258 } 2259 2260 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 2261 if (buf->b_p_bl == find_listed 2262 #ifdef FEAT_DIFF 2263 && (!diffmode || diff_mode_buf(buf)) 2264 #endif 2265 && buflist_match(prog, buf) != NULL) 2266 { 2267 if (curtab_only) 2268 { 2269 /* Ignore the match if the buffer is not open in 2270 * the current tab. */ 2271 #ifdef FEAT_WINDOWS 2272 win_T *wp; 2273 2274 for (wp = firstwin; wp != NULL; wp = wp->w_next) 2275 if (wp->w_buffer == buf) 2276 break; 2277 if (wp == NULL) 2278 continue; 2279 #else 2280 if (curwin->w_buffer != buf) 2281 continue; 2282 #endif 2283 } 2284 if (match >= 0) /* already found a match */ 2285 { 2286 match = -2; 2287 break; 2288 } 2289 match = buf->b_fnum; /* remember first match */ 2290 } 2291 2292 vim_regfree(prog); 2293 if (match >= 0) /* found one match */ 2294 break; 2295 } 2296 2297 /* Only search for unlisted buffers if there was no match with 2298 * a listed buffer. */ 2299 if (!unlisted || !find_listed || match != -1) 2300 break; 2301 find_listed = FALSE; 2302 } 2303 2304 vim_free(pat); 2305 } 2306 2307 if (match == -2) 2308 EMSG2(_("E93: More than one match for %s"), pattern); 2309 else if (match < 0) 2310 EMSG2(_("E94: No matching buffer for %s"), pattern); 2311 return match; 2312 } 2313 #endif 2314 2315 #if defined(FEAT_CMDL_COMPL) || defined(PROTO) 2316 2317 /* 2318 * Find all buffer names that match. 2319 * For command line expansion of ":buf" and ":sbuf". 2320 * Return OK if matches found, FAIL otherwise. 2321 */ 2322 int 2323 ExpandBufnames(pat, num_file, file, options) 2324 char_u *pat; 2325 int *num_file; 2326 char_u ***file; 2327 int options; 2328 { 2329 int count = 0; 2330 buf_T *buf; 2331 int round; 2332 char_u *p; 2333 int attempt; 2334 regprog_T *prog; 2335 char_u *patc; 2336 2337 *num_file = 0; /* return values in case of FAIL */ 2338 *file = NULL; 2339 2340 /* Make a copy of "pat" and change "^" to "\(^\|[\/]\)". */ 2341 if (*pat == '^') 2342 { 2343 patc = alloc((unsigned)STRLEN(pat) + 11); 2344 if (patc == NULL) 2345 return FAIL; 2346 STRCPY(patc, "\\(^\\|[\\/]\\)"); 2347 STRCPY(patc + 11, pat + 1); 2348 } 2349 else 2350 patc = pat; 2351 2352 /* 2353 * attempt == 0: try match with '\<', match at start of word 2354 * attempt == 1: try match without '\<', match anywhere 2355 */ 2356 for (attempt = 0; attempt <= 1; ++attempt) 2357 { 2358 if (attempt > 0 && patc == pat) 2359 break; /* there was no anchor, no need to try again */ 2360 prog = vim_regcomp(patc + attempt * 11, RE_MAGIC); 2361 if (prog == NULL) 2362 { 2363 if (patc != pat) 2364 vim_free(patc); 2365 return FAIL; 2366 } 2367 2368 /* 2369 * round == 1: Count the matches. 2370 * round == 2: Build the array to keep the matches. 2371 */ 2372 for (round = 1; round <= 2; ++round) 2373 { 2374 count = 0; 2375 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 2376 { 2377 if (!buf->b_p_bl) /* skip unlisted buffers */ 2378 continue; 2379 p = buflist_match(prog, buf); 2380 if (p != NULL) 2381 { 2382 if (round == 1) 2383 ++count; 2384 else 2385 { 2386 if (options & WILD_HOME_REPLACE) 2387 p = home_replace_save(buf, p); 2388 else 2389 p = vim_strsave(p); 2390 (*file)[count++] = p; 2391 } 2392 } 2393 } 2394 if (count == 0) /* no match found, break here */ 2395 break; 2396 if (round == 1) 2397 { 2398 *file = (char_u **)alloc((unsigned)(count * sizeof(char_u *))); 2399 if (*file == NULL) 2400 { 2401 vim_regfree(prog); 2402 if (patc != pat) 2403 vim_free(patc); 2404 return FAIL; 2405 } 2406 } 2407 } 2408 vim_regfree(prog); 2409 if (count) /* match(es) found, break here */ 2410 break; 2411 } 2412 2413 if (patc != pat) 2414 vim_free(patc); 2415 2416 *num_file = count; 2417 return (count == 0 ? FAIL : OK); 2418 } 2419 2420 #endif /* FEAT_CMDL_COMPL */ 2421 2422 #ifdef HAVE_BUFLIST_MATCH 2423 /* 2424 * Check for a match on the file name for buffer "buf" with regprog "prog". 2425 */ 2426 static char_u * 2427 buflist_match(prog, buf) 2428 regprog_T *prog; 2429 buf_T *buf; 2430 { 2431 char_u *match; 2432 2433 /* First try the short file name, then the long file name. */ 2434 match = fname_match(prog, buf->b_sfname); 2435 if (match == NULL) 2436 match = fname_match(prog, buf->b_ffname); 2437 2438 return match; 2439 } 2440 2441 /* 2442 * Try matching the regexp in "prog" with file name "name". 2443 * Return "name" when there is a match, NULL when not. 2444 */ 2445 static char_u * 2446 fname_match(prog, name) 2447 regprog_T *prog; 2448 char_u *name; 2449 { 2450 char_u *match = NULL; 2451 char_u *p; 2452 regmatch_T regmatch; 2453 2454 if (name != NULL) 2455 { 2456 regmatch.regprog = prog; 2457 regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */ 2458 if (vim_regexec(®match, name, (colnr_T)0)) 2459 match = name; 2460 else 2461 { 2462 /* Replace $(HOME) with '~' and try matching again. */ 2463 p = home_replace_save(NULL, name); 2464 if (p != NULL && vim_regexec(®match, p, (colnr_T)0)) 2465 match = name; 2466 vim_free(p); 2467 } 2468 } 2469 2470 return match; 2471 } 2472 #endif 2473 2474 /* 2475 * find file in buffer list by number 2476 */ 2477 buf_T * 2478 buflist_findnr(nr) 2479 int nr; 2480 { 2481 buf_T *buf; 2482 2483 if (nr == 0) 2484 nr = curwin->w_alt_fnum; 2485 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 2486 if (buf->b_fnum == nr) 2487 return (buf); 2488 return NULL; 2489 } 2490 2491 /* 2492 * Get name of file 'n' in the buffer list. 2493 * When the file has no name an empty string is returned. 2494 * home_replace() is used to shorten the file name (used for marks). 2495 * Returns a pointer to allocated memory, of NULL when failed. 2496 */ 2497 char_u * 2498 buflist_nr2name(n, fullname, helptail) 2499 int n; 2500 int fullname; 2501 int helptail; /* for help buffers return tail only */ 2502 { 2503 buf_T *buf; 2504 2505 buf = buflist_findnr(n); 2506 if (buf == NULL) 2507 return NULL; 2508 return home_replace_save(helptail ? buf : NULL, 2509 fullname ? buf->b_ffname : buf->b_fname); 2510 } 2511 2512 /* 2513 * Set the "lnum" and "col" for the buffer "buf" and the current window. 2514 * When "copy_options" is TRUE save the local window option values. 2515 * When "lnum" is 0 only do the options. 2516 */ 2517 static void 2518 buflist_setfpos(buf, win, lnum, col, copy_options) 2519 buf_T *buf; 2520 win_T *win; 2521 linenr_T lnum; 2522 colnr_T col; 2523 int copy_options; 2524 { 2525 wininfo_T *wip; 2526 2527 for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) 2528 if (wip->wi_win == win) 2529 break; 2530 if (wip == NULL) 2531 { 2532 /* allocate a new entry */ 2533 wip = (wininfo_T *)alloc_clear((unsigned)sizeof(wininfo_T)); 2534 if (wip == NULL) 2535 return; 2536 wip->wi_win = win; 2537 if (lnum == 0) /* set lnum even when it's 0 */ 2538 lnum = 1; 2539 } 2540 else 2541 { 2542 /* remove the entry from the list */ 2543 if (wip->wi_prev) 2544 wip->wi_prev->wi_next = wip->wi_next; 2545 else 2546 buf->b_wininfo = wip->wi_next; 2547 if (wip->wi_next) 2548 wip->wi_next->wi_prev = wip->wi_prev; 2549 if (copy_options && wip->wi_optset) 2550 { 2551 clear_winopt(&wip->wi_opt); 2552 #ifdef FEAT_FOLDING 2553 deleteFoldRecurse(&wip->wi_folds); 2554 #endif 2555 } 2556 } 2557 if (lnum != 0) 2558 { 2559 wip->wi_fpos.lnum = lnum; 2560 wip->wi_fpos.col = col; 2561 } 2562 if (copy_options) 2563 { 2564 /* Save the window-specific option values. */ 2565 copy_winopt(&win->w_onebuf_opt, &wip->wi_opt); 2566 #ifdef FEAT_FOLDING 2567 wip->wi_fold_manual = win->w_fold_manual; 2568 cloneFoldGrowArray(&win->w_folds, &wip->wi_folds); 2569 #endif 2570 wip->wi_optset = TRUE; 2571 } 2572 2573 /* insert the entry in front of the list */ 2574 wip->wi_next = buf->b_wininfo; 2575 buf->b_wininfo = wip; 2576 wip->wi_prev = NULL; 2577 if (wip->wi_next) 2578 wip->wi_next->wi_prev = wip; 2579 2580 return; 2581 } 2582 2583 #ifdef FEAT_DIFF 2584 static int wininfo_other_tab_diff __ARGS((wininfo_T *wip)); 2585 2586 /* 2587 * Return TRUE when "wip" has 'diff' set and the diff is only for another tab 2588 * page. That's because a diff is local to a tab page. 2589 */ 2590 static int 2591 wininfo_other_tab_diff(wip) 2592 wininfo_T *wip; 2593 { 2594 win_T *wp; 2595 2596 if (wip->wi_opt.wo_diff) 2597 { 2598 for (wp = firstwin; wp != NULL; wp = wp->w_next) 2599 /* return FALSE when it's a window in the current tab page, thus 2600 * the buffer was in diff mode here */ 2601 if (wip->wi_win == wp) 2602 return FALSE; 2603 return TRUE; 2604 } 2605 return FALSE; 2606 } 2607 #endif 2608 2609 /* 2610 * Find info for the current window in buffer "buf". 2611 * If not found, return the info for the most recently used window. 2612 * When "skip_diff_buffer" is TRUE avoid windows with 'diff' set that is in 2613 * another tab page. 2614 * Returns NULL when there isn't any info. 2615 */ 2616 static wininfo_T * 2617 find_wininfo(buf, skip_diff_buffer) 2618 buf_T *buf; 2619 int skip_diff_buffer UNUSED; 2620 { 2621 wininfo_T *wip; 2622 2623 for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) 2624 if (wip->wi_win == curwin 2625 #ifdef FEAT_DIFF 2626 && (!skip_diff_buffer || !wininfo_other_tab_diff(wip)) 2627 #endif 2628 ) 2629 break; 2630 2631 /* If no wininfo for curwin, use the first in the list (that doesn't have 2632 * 'diff' set and is in another tab page). */ 2633 if (wip == NULL) 2634 { 2635 #ifdef FEAT_DIFF 2636 if (skip_diff_buffer) 2637 { 2638 for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) 2639 if (!wininfo_other_tab_diff(wip)) 2640 break; 2641 } 2642 else 2643 #endif 2644 wip = buf->b_wininfo; 2645 } 2646 return wip; 2647 } 2648 2649 /* 2650 * Reset the local window options to the values last used in this window. 2651 * If the buffer wasn't used in this window before, use the values from 2652 * the most recently used window. If the values were never set, use the 2653 * global values for the window. 2654 */ 2655 void 2656 get_winopts(buf) 2657 buf_T *buf; 2658 { 2659 wininfo_T *wip; 2660 2661 clear_winopt(&curwin->w_onebuf_opt); 2662 #ifdef FEAT_FOLDING 2663 clearFolding(curwin); 2664 #endif 2665 2666 wip = find_wininfo(buf, TRUE); 2667 if (wip != NULL && wip->wi_optset) 2668 { 2669 copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt); 2670 #ifdef FEAT_FOLDING 2671 curwin->w_fold_manual = wip->wi_fold_manual; 2672 curwin->w_foldinvalid = TRUE; 2673 cloneFoldGrowArray(&wip->wi_folds, &curwin->w_folds); 2674 #endif 2675 } 2676 else 2677 copy_winopt(&curwin->w_allbuf_opt, &curwin->w_onebuf_opt); 2678 2679 #ifdef FEAT_FOLDING 2680 /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */ 2681 if (p_fdls >= 0) 2682 curwin->w_p_fdl = p_fdls; 2683 #endif 2684 #ifdef FEAT_SYN_HL 2685 check_colorcolumn(curwin); 2686 #endif 2687 } 2688 2689 /* 2690 * Find the position (lnum and col) for the buffer 'buf' for the current 2691 * window. 2692 * Returns a pointer to no_position if no position is found. 2693 */ 2694 pos_T * 2695 buflist_findfpos(buf) 2696 buf_T *buf; 2697 { 2698 wininfo_T *wip; 2699 static pos_T no_position = INIT_POS_T(1, 0, 0); 2700 2701 wip = find_wininfo(buf, FALSE); 2702 if (wip != NULL) 2703 return &(wip->wi_fpos); 2704 else 2705 return &no_position; 2706 } 2707 2708 /* 2709 * Find the lnum for the buffer 'buf' for the current window. 2710 */ 2711 linenr_T 2712 buflist_findlnum(buf) 2713 buf_T *buf; 2714 { 2715 return buflist_findfpos(buf)->lnum; 2716 } 2717 2718 #if defined(FEAT_LISTCMDS) || defined(PROTO) 2719 /* 2720 * List all know file names (for :files and :buffers command). 2721 */ 2722 void 2723 buflist_list(eap) 2724 exarg_T *eap; 2725 { 2726 buf_T *buf; 2727 int len; 2728 int i; 2729 2730 for (buf = firstbuf; buf != NULL && !got_int; buf = buf->b_next) 2731 { 2732 /* skip unlisted buffers, unless ! was used */ 2733 if (!buf->b_p_bl && !eap->forceit) 2734 continue; 2735 msg_putchar('\n'); 2736 if (buf_spname(buf) != NULL) 2737 vim_strncpy(NameBuff, buf_spname(buf), MAXPATHL - 1); 2738 else 2739 home_replace(buf, buf->b_fname, NameBuff, MAXPATHL, TRUE); 2740 2741 len = vim_snprintf((char *)IObuff, IOSIZE - 20, "%3d%c%c%c%c%c \"%s\"", 2742 buf->b_fnum, 2743 buf->b_p_bl ? ' ' : 'u', 2744 buf == curbuf ? '%' : 2745 (curwin->w_alt_fnum == buf->b_fnum ? '#' : ' '), 2746 buf->b_ml.ml_mfp == NULL ? ' ' : 2747 (buf->b_nwindows == 0 ? 'h' : 'a'), 2748 !buf->b_p_ma ? '-' : (buf->b_p_ro ? '=' : ' '), 2749 (buf->b_flags & BF_READERR) ? 'x' 2750 : (bufIsChanged(buf) ? '+' : ' '), 2751 NameBuff); 2752 2753 /* put "line 999" in column 40 or after the file name */ 2754 i = 40 - vim_strsize(IObuff); 2755 do 2756 { 2757 IObuff[len++] = ' '; 2758 } while (--i > 0 && len < IOSIZE - 18); 2759 vim_snprintf((char *)IObuff + len, (size_t)(IOSIZE - len), 2760 _("line %ld"), buf == curbuf ? curwin->w_cursor.lnum 2761 : (long)buflist_findlnum(buf)); 2762 msg_outtrans(IObuff); 2763 out_flush(); /* output one line at a time */ 2764 ui_breakcheck(); 2765 } 2766 } 2767 #endif 2768 2769 /* 2770 * Get file name and line number for file 'fnum'. 2771 * Used by DoOneCmd() for translating '%' and '#'. 2772 * Used by insert_reg() and cmdline_paste() for '#' register. 2773 * Return FAIL if not found, OK for success. 2774 */ 2775 int 2776 buflist_name_nr(fnum, fname, lnum) 2777 int fnum; 2778 char_u **fname; 2779 linenr_T *lnum; 2780 { 2781 buf_T *buf; 2782 2783 buf = buflist_findnr(fnum); 2784 if (buf == NULL || buf->b_fname == NULL) 2785 return FAIL; 2786 2787 *fname = buf->b_fname; 2788 *lnum = buflist_findlnum(buf); 2789 2790 return OK; 2791 } 2792 2793 /* 2794 * Set the file name for "buf"' to 'ffname', short file name to 'sfname'. 2795 * The file name with the full path is also remembered, for when :cd is used. 2796 * Returns FAIL for failure (file name already in use by other buffer) 2797 * OK otherwise. 2798 */ 2799 int 2800 setfname(buf, ffname, sfname, message) 2801 buf_T *buf; 2802 char_u *ffname, *sfname; 2803 int message; /* give message when buffer already exists */ 2804 { 2805 buf_T *obuf = NULL; 2806 #ifdef UNIX 2807 struct stat st; 2808 #endif 2809 2810 if (ffname == NULL || *ffname == NUL) 2811 { 2812 /* Removing the name. */ 2813 vim_free(buf->b_ffname); 2814 vim_free(buf->b_sfname); 2815 buf->b_ffname = NULL; 2816 buf->b_sfname = NULL; 2817 #ifdef UNIX 2818 st.st_dev = (dev_T)-1; 2819 #endif 2820 } 2821 else 2822 { 2823 fname_expand(buf, &ffname, &sfname); /* will allocate ffname */ 2824 if (ffname == NULL) /* out of memory */ 2825 return FAIL; 2826 2827 /* 2828 * if the file name is already used in another buffer: 2829 * - if the buffer is loaded, fail 2830 * - if the buffer is not loaded, delete it from the list 2831 */ 2832 #ifdef UNIX 2833 if (mch_stat((char *)ffname, &st) < 0) 2834 st.st_dev = (dev_T)-1; 2835 #endif 2836 if (!(buf->b_flags & BF_DUMMY)) 2837 #ifdef UNIX 2838 obuf = buflist_findname_stat(ffname, &st); 2839 #else 2840 obuf = buflist_findname(ffname); 2841 #endif 2842 if (obuf != NULL && obuf != buf) 2843 { 2844 if (obuf->b_ml.ml_mfp != NULL) /* it's loaded, fail */ 2845 { 2846 if (message) 2847 EMSG(_("E95: Buffer with this name already exists")); 2848 vim_free(ffname); 2849 return FAIL; 2850 } 2851 /* delete from the list */ 2852 close_buffer(NULL, obuf, DOBUF_WIPE, FALSE); 2853 } 2854 sfname = vim_strsave(sfname); 2855 if (ffname == NULL || sfname == NULL) 2856 { 2857 vim_free(sfname); 2858 vim_free(ffname); 2859 return FAIL; 2860 } 2861 #ifdef USE_FNAME_CASE 2862 # ifdef USE_LONG_FNAME 2863 if (USE_LONG_FNAME) 2864 # endif 2865 fname_case(sfname, 0); /* set correct case for short file name */ 2866 #endif 2867 vim_free(buf->b_ffname); 2868 vim_free(buf->b_sfname); 2869 buf->b_ffname = ffname; 2870 buf->b_sfname = sfname; 2871 } 2872 buf->b_fname = buf->b_sfname; 2873 #ifdef UNIX 2874 if (st.st_dev == (dev_T)-1) 2875 buf->b_dev_valid = FALSE; 2876 else 2877 { 2878 buf->b_dev_valid = TRUE; 2879 buf->b_dev = st.st_dev; 2880 buf->b_ino = st.st_ino; 2881 } 2882 #endif 2883 2884 #ifndef SHORT_FNAME 2885 buf->b_shortname = FALSE; 2886 #endif 2887 2888 buf_name_changed(buf); 2889 return OK; 2890 } 2891 2892 /* 2893 * Crude way of changing the name of a buffer. Use with care! 2894 * The name should be relative to the current directory. 2895 */ 2896 void 2897 buf_set_name(fnum, name) 2898 int fnum; 2899 char_u *name; 2900 { 2901 buf_T *buf; 2902 2903 buf = buflist_findnr(fnum); 2904 if (buf != NULL) 2905 { 2906 vim_free(buf->b_sfname); 2907 vim_free(buf->b_ffname); 2908 buf->b_ffname = vim_strsave(name); 2909 buf->b_sfname = NULL; 2910 /* Allocate ffname and expand into full path. Also resolves .lnk 2911 * files on Win32. */ 2912 fname_expand(buf, &buf->b_ffname, &buf->b_sfname); 2913 buf->b_fname = buf->b_sfname; 2914 } 2915 } 2916 2917 /* 2918 * Take care of what needs to be done when the name of buffer "buf" has 2919 * changed. 2920 */ 2921 void 2922 buf_name_changed(buf) 2923 buf_T *buf; 2924 { 2925 /* 2926 * If the file name changed, also change the name of the swapfile 2927 */ 2928 if (buf->b_ml.ml_mfp != NULL) 2929 ml_setname(buf); 2930 2931 if (curwin->w_buffer == buf) 2932 check_arg_idx(curwin); /* check file name for arg list */ 2933 #ifdef FEAT_TITLE 2934 maketitle(); /* set window title */ 2935 #endif 2936 #ifdef FEAT_WINDOWS 2937 status_redraw_all(); /* status lines need to be redrawn */ 2938 #endif 2939 fmarks_check_names(buf); /* check named file marks */ 2940 ml_timestamp(buf); /* reset timestamp */ 2941 } 2942 2943 /* 2944 * set alternate file name for current window 2945 * 2946 * Used by do_one_cmd(), do_write() and do_ecmd(). 2947 * Return the buffer. 2948 */ 2949 buf_T * 2950 setaltfname(ffname, sfname, lnum) 2951 char_u *ffname; 2952 char_u *sfname; 2953 linenr_T lnum; 2954 { 2955 buf_T *buf; 2956 2957 /* Create a buffer. 'buflisted' is not set if it's a new buffer */ 2958 buf = buflist_new(ffname, sfname, lnum, 0); 2959 if (buf != NULL && !cmdmod.keepalt) 2960 curwin->w_alt_fnum = buf->b_fnum; 2961 return buf; 2962 } 2963 2964 /* 2965 * Get alternate file name for current window. 2966 * Return NULL if there isn't any, and give error message if requested. 2967 */ 2968 char_u * 2969 getaltfname(errmsg) 2970 int errmsg; /* give error message */ 2971 { 2972 char_u *fname; 2973 linenr_T dummy; 2974 2975 if (buflist_name_nr(0, &fname, &dummy) == FAIL) 2976 { 2977 if (errmsg) 2978 EMSG(_(e_noalt)); 2979 return NULL; 2980 } 2981 return fname; 2982 } 2983 2984 /* 2985 * Add a file name to the buflist and return its number. 2986 * Uses same flags as buflist_new(), except BLN_DUMMY. 2987 * 2988 * used by qf_init(), main() and doarglist() 2989 */ 2990 int 2991 buflist_add(fname, flags) 2992 char_u *fname; 2993 int flags; 2994 { 2995 buf_T *buf; 2996 2997 buf = buflist_new(fname, NULL, (linenr_T)0, flags); 2998 if (buf != NULL) 2999 return buf->b_fnum; 3000 return 0; 3001 } 3002 3003 #if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) 3004 /* 3005 * Adjust slashes in file names. Called after 'shellslash' was set. 3006 */ 3007 void 3008 buflist_slash_adjust() 3009 { 3010 buf_T *bp; 3011 3012 for (bp = firstbuf; bp != NULL; bp = bp->b_next) 3013 { 3014 if (bp->b_ffname != NULL) 3015 slash_adjust(bp->b_ffname); 3016 if (bp->b_sfname != NULL) 3017 slash_adjust(bp->b_sfname); 3018 } 3019 } 3020 #endif 3021 3022 /* 3023 * Set alternate cursor position for the current buffer and window "win". 3024 * Also save the local window option values. 3025 */ 3026 void 3027 buflist_altfpos(win) 3028 win_T *win; 3029 { 3030 buflist_setfpos(curbuf, win, win->w_cursor.lnum, win->w_cursor.col, TRUE); 3031 } 3032 3033 /* 3034 * Return TRUE if 'ffname' is not the same file as current file. 3035 * Fname must have a full path (expanded by mch_FullName()). 3036 */ 3037 int 3038 otherfile(ffname) 3039 char_u *ffname; 3040 { 3041 return otherfile_buf(curbuf, ffname 3042 #ifdef UNIX 3043 , NULL 3044 #endif 3045 ); 3046 } 3047 3048 static int 3049 otherfile_buf(buf, ffname 3050 #ifdef UNIX 3051 , stp 3052 #endif 3053 ) 3054 buf_T *buf; 3055 char_u *ffname; 3056 #ifdef UNIX 3057 struct stat *stp; 3058 #endif 3059 { 3060 /* no name is different */ 3061 if (ffname == NULL || *ffname == NUL || buf->b_ffname == NULL) 3062 return TRUE; 3063 if (fnamecmp(ffname, buf->b_ffname) == 0) 3064 return FALSE; 3065 #ifdef UNIX 3066 { 3067 struct stat st; 3068 3069 /* If no struct stat given, get it now */ 3070 if (stp == NULL) 3071 { 3072 if (!buf->b_dev_valid || mch_stat((char *)ffname, &st) < 0) 3073 st.st_dev = (dev_T)-1; 3074 stp = &st; 3075 } 3076 /* Use dev/ino to check if the files are the same, even when the names 3077 * are different (possible with links). Still need to compare the 3078 * name above, for when the file doesn't exist yet. 3079 * Problem: The dev/ino changes when a file is deleted (and created 3080 * again) and remains the same when renamed/moved. We don't want to 3081 * mch_stat() each buffer each time, that would be too slow. Get the 3082 * dev/ino again when they appear to match, but not when they appear 3083 * to be different: Could skip a buffer when it's actually the same 3084 * file. */ 3085 if (buf_same_ino(buf, stp)) 3086 { 3087 buf_setino(buf); 3088 if (buf_same_ino(buf, stp)) 3089 return FALSE; 3090 } 3091 } 3092 #endif 3093 return TRUE; 3094 } 3095 3096 #if defined(UNIX) || defined(PROTO) 3097 /* 3098 * Set inode and device number for a buffer. 3099 * Must always be called when b_fname is changed!. 3100 */ 3101 void 3102 buf_setino(buf) 3103 buf_T *buf; 3104 { 3105 struct stat st; 3106 3107 if (buf->b_fname != NULL && mch_stat((char *)buf->b_fname, &st) >= 0) 3108 { 3109 buf->b_dev_valid = TRUE; 3110 buf->b_dev = st.st_dev; 3111 buf->b_ino = st.st_ino; 3112 } 3113 else 3114 buf->b_dev_valid = FALSE; 3115 } 3116 3117 /* 3118 * Return TRUE if dev/ino in buffer "buf" matches with "stp". 3119 */ 3120 static int 3121 buf_same_ino(buf, stp) 3122 buf_T *buf; 3123 struct stat *stp; 3124 { 3125 return (buf->b_dev_valid 3126 && stp->st_dev == buf->b_dev 3127 && stp->st_ino == buf->b_ino); 3128 } 3129 #endif 3130 3131 /* 3132 * Print info about the current buffer. 3133 */ 3134 void 3135 fileinfo(fullname, shorthelp, dont_truncate) 3136 int fullname; /* when non-zero print full path */ 3137 int shorthelp; 3138 int dont_truncate; 3139 { 3140 char_u *name; 3141 int n; 3142 char_u *p; 3143 char_u *buffer; 3144 size_t len; 3145 3146 buffer = alloc(IOSIZE); 3147 if (buffer == NULL) 3148 return; 3149 3150 if (fullname > 1) /* 2 CTRL-G: include buffer number */ 3151 { 3152 vim_snprintf((char *)buffer, IOSIZE, "buf %d: ", curbuf->b_fnum); 3153 p = buffer + STRLEN(buffer); 3154 } 3155 else 3156 p = buffer; 3157 3158 *p++ = '"'; 3159 if (buf_spname(curbuf) != NULL) 3160 vim_strncpy(p, buf_spname(curbuf), IOSIZE - (p - buffer) - 1); 3161 else 3162 { 3163 if (!fullname && curbuf->b_fname != NULL) 3164 name = curbuf->b_fname; 3165 else 3166 name = curbuf->b_ffname; 3167 home_replace(shorthelp ? curbuf : NULL, name, p, 3168 (int)(IOSIZE - (p - buffer)), TRUE); 3169 } 3170 3171 vim_snprintf_add((char *)buffer, IOSIZE, "\"%s%s%s%s%s%s", 3172 curbufIsChanged() ? (shortmess(SHM_MOD) 3173 ? " [+]" : _(" [Modified]")) : " ", 3174 (curbuf->b_flags & BF_NOTEDITED) 3175 #ifdef FEAT_QUICKFIX 3176 && !bt_dontwrite(curbuf) 3177 #endif 3178 ? _("[Not edited]") : "", 3179 (curbuf->b_flags & BF_NEW) 3180 #ifdef FEAT_QUICKFIX 3181 && !bt_dontwrite(curbuf) 3182 #endif 3183 ? _("[New file]") : "", 3184 (curbuf->b_flags & BF_READERR) ? _("[Read errors]") : "", 3185 curbuf->b_p_ro ? (shortmess(SHM_RO) ? _("[RO]") 3186 : _("[readonly]")) : "", 3187 (curbufIsChanged() || (curbuf->b_flags & BF_WRITE_MASK) 3188 || curbuf->b_p_ro) ? 3189 " " : ""); 3190 /* With 32 bit longs and more than 21,474,836 lines multiplying by 100 3191 * causes an overflow, thus for large numbers divide instead. */ 3192 if (curwin->w_cursor.lnum > 1000000L) 3193 n = (int)(((long)curwin->w_cursor.lnum) / 3194 ((long)curbuf->b_ml.ml_line_count / 100L)); 3195 else 3196 n = (int)(((long)curwin->w_cursor.lnum * 100L) / 3197 (long)curbuf->b_ml.ml_line_count); 3198 if (curbuf->b_ml.ml_flags & ML_EMPTY) 3199 { 3200 vim_snprintf_add((char *)buffer, IOSIZE, "%s", _(no_lines_msg)); 3201 } 3202 #ifdef FEAT_CMDL_INFO 3203 else if (p_ru) 3204 { 3205 /* Current line and column are already on the screen -- webb */ 3206 if (curbuf->b_ml.ml_line_count == 1) 3207 vim_snprintf_add((char *)buffer, IOSIZE, _("1 line --%d%%--"), n); 3208 else 3209 vim_snprintf_add((char *)buffer, IOSIZE, _("%ld lines --%d%%--"), 3210 (long)curbuf->b_ml.ml_line_count, n); 3211 } 3212 #endif 3213 else 3214 { 3215 vim_snprintf_add((char *)buffer, IOSIZE, 3216 _("line %ld of %ld --%d%%-- col "), 3217 (long)curwin->w_cursor.lnum, 3218 (long)curbuf->b_ml.ml_line_count, 3219 n); 3220 validate_virtcol(); 3221 len = STRLEN(buffer); 3222 col_print(buffer + len, IOSIZE - len, 3223 (int)curwin->w_cursor.col + 1, (int)curwin->w_virtcol + 1); 3224 } 3225 3226 (void)append_arg_number(curwin, buffer, IOSIZE, !shortmess(SHM_FILE)); 3227 3228 if (dont_truncate) 3229 { 3230 /* Temporarily set msg_scroll to avoid the message being truncated. 3231 * First call msg_start() to get the message in the right place. */ 3232 msg_start(); 3233 n = msg_scroll; 3234 msg_scroll = TRUE; 3235 msg(buffer); 3236 msg_scroll = n; 3237 } 3238 else 3239 { 3240 p = msg_trunc_attr(buffer, FALSE, 0); 3241 if (restart_edit != 0 || (msg_scrolled && !need_wait_return)) 3242 /* Need to repeat the message after redrawing when: 3243 * - When restart_edit is set (otherwise there will be a delay 3244 * before redrawing). 3245 * - When the screen was scrolled but there is no wait-return 3246 * prompt. */ 3247 set_keep_msg(p, 0); 3248 } 3249 3250 vim_free(buffer); 3251 } 3252 3253 void 3254 col_print(buf, buflen, col, vcol) 3255 char_u *buf; 3256 size_t buflen; 3257 int col; 3258 int vcol; 3259 { 3260 if (col == vcol) 3261 vim_snprintf((char *)buf, buflen, "%d", col); 3262 else 3263 vim_snprintf((char *)buf, buflen, "%d-%d", col, vcol); 3264 } 3265 3266 #if defined(FEAT_TITLE) || defined(PROTO) 3267 /* 3268 * put file name in title bar of window and in icon title 3269 */ 3270 3271 static char_u *lasttitle = NULL; 3272 static char_u *lasticon = NULL; 3273 3274 void 3275 maketitle() 3276 { 3277 char_u *p; 3278 char_u *t_str = NULL; 3279 char_u *i_name; 3280 char_u *i_str = NULL; 3281 int maxlen = 0; 3282 int len; 3283 int mustset; 3284 char_u buf[IOSIZE]; 3285 int off; 3286 3287 if (!redrawing()) 3288 { 3289 /* Postpone updating the title when 'lazyredraw' is set. */ 3290 need_maketitle = TRUE; 3291 return; 3292 } 3293 3294 need_maketitle = FALSE; 3295 if (!p_title && !p_icon && lasttitle == NULL && lasticon == NULL) 3296 return; 3297 3298 if (p_title) 3299 { 3300 if (p_titlelen > 0) 3301 { 3302 maxlen = p_titlelen * Columns / 100; 3303 if (maxlen < 10) 3304 maxlen = 10; 3305 } 3306 3307 t_str = buf; 3308 if (*p_titlestring != NUL) 3309 { 3310 #ifdef FEAT_STL_OPT 3311 if (stl_syntax & STL_IN_TITLE) 3312 { 3313 int use_sandbox = FALSE; 3314 int save_called_emsg = called_emsg; 3315 3316 # ifdef FEAT_EVAL 3317 use_sandbox = was_set_insecurely((char_u *)"titlestring", 0); 3318 # endif 3319 called_emsg = FALSE; 3320 build_stl_str_hl(curwin, t_str, sizeof(buf), 3321 p_titlestring, use_sandbox, 3322 0, maxlen, NULL, NULL); 3323 if (called_emsg) 3324 set_string_option_direct((char_u *)"titlestring", -1, 3325 (char_u *)"", OPT_FREE, SID_ERROR); 3326 called_emsg |= save_called_emsg; 3327 } 3328 else 3329 #endif 3330 t_str = p_titlestring; 3331 } 3332 else 3333 { 3334 /* format: "fname + (path) (1 of 2) - VIM" */ 3335 3336 #define SPACE_FOR_FNAME (IOSIZE - 100) 3337 #define SPACE_FOR_DIR (IOSIZE - 20) 3338 #define SPACE_FOR_ARGNR (IOSIZE - 10) /* at least room for " - VIM" */ 3339 if (curbuf->b_fname == NULL) 3340 vim_strncpy(buf, (char_u *)_("[No Name]"), SPACE_FOR_FNAME); 3341 else 3342 { 3343 p = transstr(gettail(curbuf->b_fname)); 3344 vim_strncpy(buf, p, SPACE_FOR_FNAME); 3345 vim_free(p); 3346 } 3347 3348 switch (bufIsChanged(curbuf) 3349 + (curbuf->b_p_ro * 2) 3350 + (!curbuf->b_p_ma * 4)) 3351 { 3352 case 1: STRCAT(buf, " +"); break; 3353 case 2: STRCAT(buf, " ="); break; 3354 case 3: STRCAT(buf, " =+"); break; 3355 case 4: 3356 case 6: STRCAT(buf, " -"); break; 3357 case 5: 3358 case 7: STRCAT(buf, " -+"); break; 3359 } 3360 3361 if (curbuf->b_fname != NULL) 3362 { 3363 /* Get path of file, replace home dir with ~ */ 3364 off = (int)STRLEN(buf); 3365 buf[off++] = ' '; 3366 buf[off++] = '('; 3367 home_replace(curbuf, curbuf->b_ffname, 3368 buf + off, SPACE_FOR_DIR - off, TRUE); 3369 #ifdef BACKSLASH_IN_FILENAME 3370 /* avoid "c:/name" to be reduced to "c" */ 3371 if (isalpha(buf[off]) && buf[off + 1] == ':') 3372 off += 2; 3373 #endif 3374 /* remove the file name */ 3375 p = gettail_sep(buf + off); 3376 if (p == buf + off) 3377 /* must be a help buffer */ 3378 vim_strncpy(buf + off, (char_u *)_("help"), 3379 (size_t)(SPACE_FOR_DIR - off - 1)); 3380 else 3381 *p = NUL; 3382 3383 /* Translate unprintable chars and concatenate. Keep some 3384 * room for the server name. When there is no room (very long 3385 * file name) use (...). */ 3386 if (off < SPACE_FOR_DIR) 3387 { 3388 p = transstr(buf + off); 3389 vim_strncpy(buf + off, p, (size_t)(SPACE_FOR_DIR - off)); 3390 vim_free(p); 3391 } 3392 else 3393 { 3394 vim_strncpy(buf + off, (char_u *)"...", 3395 (size_t)(SPACE_FOR_ARGNR - off)); 3396 } 3397 STRCAT(buf, ")"); 3398 } 3399 3400 append_arg_number(curwin, buf, SPACE_FOR_ARGNR, FALSE); 3401 3402 #if defined(FEAT_CLIENTSERVER) 3403 if (serverName != NULL) 3404 { 3405 STRCAT(buf, " - "); 3406 vim_strcat(buf, serverName, IOSIZE); 3407 } 3408 else 3409 #endif 3410 STRCAT(buf, " - VIM"); 3411 3412 if (maxlen > 0) 3413 { 3414 /* make it shorter by removing a bit in the middle */ 3415 if (vim_strsize(buf) > maxlen) 3416 trunc_string(buf, buf, maxlen, IOSIZE); 3417 } 3418 } 3419 } 3420 mustset = ti_change(t_str, &lasttitle); 3421 3422 if (p_icon) 3423 { 3424 i_str = buf; 3425 if (*p_iconstring != NUL) 3426 { 3427 #ifdef FEAT_STL_OPT 3428 if (stl_syntax & STL_IN_ICON) 3429 { 3430 int use_sandbox = FALSE; 3431 int save_called_emsg = called_emsg; 3432 3433 # ifdef FEAT_EVAL 3434 use_sandbox = was_set_insecurely((char_u *)"iconstring", 0); 3435 # endif 3436 called_emsg = FALSE; 3437 build_stl_str_hl(curwin, i_str, sizeof(buf), 3438 p_iconstring, use_sandbox, 3439 0, 0, NULL, NULL); 3440 if (called_emsg) 3441 set_string_option_direct((char_u *)"iconstring", -1, 3442 (char_u *)"", OPT_FREE, SID_ERROR); 3443 called_emsg |= save_called_emsg; 3444 } 3445 else 3446 #endif 3447 i_str = p_iconstring; 3448 } 3449 else 3450 { 3451 if (buf_spname(curbuf) != NULL) 3452 i_name = buf_spname(curbuf); 3453 else /* use file name only in icon */ 3454 i_name = gettail(curbuf->b_ffname); 3455 *i_str = NUL; 3456 /* Truncate name at 100 bytes. */ 3457 len = (int)STRLEN(i_name); 3458 if (len > 100) 3459 { 3460 len -= 100; 3461 #ifdef FEAT_MBYTE 3462 if (has_mbyte) 3463 len += (*mb_tail_off)(i_name, i_name + len) + 1; 3464 #endif 3465 i_name += len; 3466 } 3467 STRCPY(i_str, i_name); 3468 trans_characters(i_str, IOSIZE); 3469 } 3470 } 3471 3472 mustset |= ti_change(i_str, &lasticon); 3473 3474 if (mustset) 3475 resettitle(); 3476 } 3477 3478 /* 3479 * Used for title and icon: Check if "str" differs from "*last". Set "*last" 3480 * from "str" if it does. 3481 * Return TRUE when "*last" changed. 3482 */ 3483 static int 3484 ti_change(str, last) 3485 char_u *str; 3486 char_u **last; 3487 { 3488 if ((str == NULL) != (*last == NULL) 3489 || (str != NULL && *last != NULL && STRCMP(str, *last) != 0)) 3490 { 3491 vim_free(*last); 3492 if (str == NULL) 3493 *last = NULL; 3494 else 3495 *last = vim_strsave(str); 3496 return TRUE; 3497 } 3498 return FALSE; 3499 } 3500 3501 /* 3502 * Put current window title back (used after calling a shell) 3503 */ 3504 void 3505 resettitle() 3506 { 3507 mch_settitle(lasttitle, lasticon); 3508 } 3509 3510 # if defined(EXITFREE) || defined(PROTO) 3511 void 3512 free_titles() 3513 { 3514 vim_free(lasttitle); 3515 vim_free(lasticon); 3516 } 3517 # endif 3518 3519 #endif /* FEAT_TITLE */ 3520 3521 #if defined(FEAT_STL_OPT) || defined(FEAT_GUI_TABLINE) || defined(PROTO) 3522 /* 3523 * Build a string from the status line items in "fmt". 3524 * Return length of string in screen cells. 3525 * 3526 * Normally works for window "wp", except when working for 'tabline' then it 3527 * is "curwin". 3528 * 3529 * Items are drawn interspersed with the text that surrounds it 3530 * Specials: %-<wid>(xxx%) => group, %= => middle marker, %< => truncation 3531 * Item: %-<minwid>.<maxwid><itemch> All but <itemch> are optional 3532 * 3533 * If maxwidth is not zero, the string will be filled at any middle marker 3534 * or truncated if too long, fillchar is used for all whitespace. 3535 */ 3536 int 3537 build_stl_str_hl(wp, out, outlen, fmt, use_sandbox, fillchar, 3538 maxwidth, hltab, tabtab) 3539 win_T *wp; 3540 char_u *out; /* buffer to write into != NameBuff */ 3541 size_t outlen; /* length of out[] */ 3542 char_u *fmt; 3543 int use_sandbox UNUSED; /* "fmt" was set insecurely, use sandbox */ 3544 int fillchar; 3545 int maxwidth; 3546 struct stl_hlrec *hltab; /* return: HL attributes (can be NULL) */ 3547 struct stl_hlrec *tabtab; /* return: tab page nrs (can be NULL) */ 3548 { 3549 char_u *p; 3550 char_u *s; 3551 char_u *t; 3552 int byteval; 3553 #ifdef FEAT_EVAL 3554 win_T *o_curwin; 3555 buf_T *o_curbuf; 3556 #endif 3557 int empty_line; 3558 colnr_T virtcol; 3559 long l; 3560 long n; 3561 int prevchar_isflag; 3562 int prevchar_isitem; 3563 int itemisflag; 3564 int fillable; 3565 char_u *str; 3566 long num; 3567 int width; 3568 int itemcnt; 3569 int curitem; 3570 int groupitem[STL_MAX_ITEM]; 3571 int groupdepth; 3572 struct stl_item 3573 { 3574 char_u *start; 3575 int minwid; 3576 int maxwid; 3577 enum 3578 { 3579 Normal, 3580 Empty, 3581 Group, 3582 Middle, 3583 Highlight, 3584 TabPage, 3585 Trunc 3586 } type; 3587 } item[STL_MAX_ITEM]; 3588 int minwid; 3589 int maxwid; 3590 int zeropad; 3591 char_u base; 3592 char_u opt; 3593 #define TMPLEN 70 3594 char_u tmp[TMPLEN]; 3595 char_u *usefmt = fmt; 3596 struct stl_hlrec *sp; 3597 3598 #ifdef FEAT_EVAL 3599 /* 3600 * When the format starts with "%!" then evaluate it as an expression and 3601 * use the result as the actual format string. 3602 */ 3603 if (fmt[0] == '%' && fmt[1] == '!') 3604 { 3605 usefmt = eval_to_string_safe(fmt + 2, NULL, use_sandbox); 3606 if (usefmt == NULL) 3607 usefmt = fmt; 3608 } 3609 #endif 3610 3611 if (fillchar == 0) 3612 fillchar = ' '; 3613 #ifdef FEAT_MBYTE 3614 /* Can't handle a multi-byte fill character yet. */ 3615 else if (mb_char2len(fillchar) > 1) 3616 fillchar = '-'; 3617 #endif 3618 3619 /* Get line & check if empty (cursorpos will show "0-1"). Note that 3620 * p will become invalid when getting another buffer line. */ 3621 p = ml_get_buf(wp->w_buffer, wp->w_cursor.lnum, FALSE); 3622 empty_line = (*p == NUL); 3623 3624 /* Get the byte value now, in case we need it below. This is more 3625 * efficient than making a copy of the line. */ 3626 if (wp->w_cursor.col > (colnr_T)STRLEN(p)) 3627 byteval = 0; 3628 else 3629 #ifdef FEAT_MBYTE 3630 byteval = (*mb_ptr2char)(p + wp->w_cursor.col); 3631 #else 3632 byteval = p[wp->w_cursor.col]; 3633 #endif 3634 3635 groupdepth = 0; 3636 p = out; 3637 curitem = 0; 3638 prevchar_isflag = TRUE; 3639 prevchar_isitem = FALSE; 3640 for (s = usefmt; *s; ) 3641 { 3642 if (curitem == STL_MAX_ITEM) 3643 { 3644 /* There are too many items. Add the error code to the statusline 3645 * to give the user a hint about what went wrong. */ 3646 if (p + 6 < out + outlen) 3647 { 3648 mch_memmove(p, " E541", (size_t)5); 3649 p += 5; 3650 } 3651 break; 3652 } 3653 3654 if (*s != NUL && *s != '%') 3655 prevchar_isflag = prevchar_isitem = FALSE; 3656 3657 /* 3658 * Handle up to the next '%' or the end. 3659 */ 3660 while (*s != NUL && *s != '%' && p + 1 < out + outlen) 3661 *p++ = *s++; 3662 if (*s == NUL || p + 1 >= out + outlen) 3663 break; 3664 3665 /* 3666 * Handle one '%' item. 3667 */ 3668 s++; 3669 if (*s == NUL) /* ignore trailing % */ 3670 break; 3671 if (*s == '%') 3672 { 3673 if (p + 1 >= out + outlen) 3674 break; 3675 *p++ = *s++; 3676 prevchar_isflag = prevchar_isitem = FALSE; 3677 continue; 3678 } 3679 if (*s == STL_MIDDLEMARK) 3680 { 3681 s++; 3682 if (groupdepth > 0) 3683 continue; 3684 item[curitem].type = Middle; 3685 item[curitem++].start = p; 3686 continue; 3687 } 3688 if (*s == STL_TRUNCMARK) 3689 { 3690 s++; 3691 item[curitem].type = Trunc; 3692 item[curitem++].start = p; 3693 continue; 3694 } 3695 if (*s == ')') 3696 { 3697 s++; 3698 if (groupdepth < 1) 3699 continue; 3700 groupdepth--; 3701 3702 t = item[groupitem[groupdepth]].start; 3703 *p = NUL; 3704 l = vim_strsize(t); 3705 if (curitem > groupitem[groupdepth] + 1 3706 && item[groupitem[groupdepth]].minwid == 0) 3707 { 3708 /* remove group if all items are empty */ 3709 for (n = groupitem[groupdepth] + 1; n < curitem; n++) 3710 if (item[n].type == Normal) 3711 break; 3712 if (n == curitem) 3713 { 3714 p = t; 3715 l = 0; 3716 } 3717 } 3718 if (l > item[groupitem[groupdepth]].maxwid) 3719 { 3720 /* truncate, remove n bytes of text at the start */ 3721 #ifdef FEAT_MBYTE 3722 if (has_mbyte) 3723 { 3724 /* Find the first character that should be included. */ 3725 n = 0; 3726 while (l >= item[groupitem[groupdepth]].maxwid) 3727 { 3728 l -= ptr2cells(t + n); 3729 n += (*mb_ptr2len)(t + n); 3730 } 3731 } 3732 else 3733 #endif 3734 n = (long)(p - t) - item[groupitem[groupdepth]].maxwid + 1; 3735 3736 *t = '<'; 3737 mch_memmove(t + 1, t + n, (size_t)(p - (t + n))); 3738 p = p - n + 1; 3739 #ifdef FEAT_MBYTE 3740 /* Fill up space left over by half a double-wide char. */ 3741 while (++l < item[groupitem[groupdepth]].minwid) 3742 *p++ = fillchar; 3743 #endif 3744 3745 /* correct the start of the items for the truncation */ 3746 for (l = groupitem[groupdepth] + 1; l < curitem; l++) 3747 { 3748 item[l].start -= n; 3749 if (item[l].start < t) 3750 item[l].start = t; 3751 } 3752 } 3753 else if (abs(item[groupitem[groupdepth]].minwid) > l) 3754 { 3755 /* fill */ 3756 n = item[groupitem[groupdepth]].minwid; 3757 if (n < 0) 3758 { 3759 /* fill by appending characters */ 3760 n = 0 - n; 3761 while (l++ < n && p + 1 < out + outlen) 3762 *p++ = fillchar; 3763 } 3764 else 3765 { 3766 /* fill by inserting characters */ 3767 mch_memmove(t + n - l, t, (size_t)(p - t)); 3768 l = n - l; 3769 if (p + l >= out + outlen) 3770 l = (long)((out + outlen) - p - 1); 3771 p += l; 3772 for (n = groupitem[groupdepth] + 1; n < curitem; n++) 3773 item[n].start += l; 3774 for ( ; l > 0; l--) 3775 *t++ = fillchar; 3776 } 3777 } 3778 continue; 3779 } 3780 minwid = 0; 3781 maxwid = 9999; 3782 zeropad = FALSE; 3783 l = 1; 3784 if (*s == '0') 3785 { 3786 s++; 3787 zeropad = TRUE; 3788 } 3789 if (*s == '-') 3790 { 3791 s++; 3792 l = -1; 3793 } 3794 if (VIM_ISDIGIT(*s)) 3795 { 3796 minwid = (int)getdigits(&s); 3797 if (minwid < 0) /* overflow */ 3798 minwid = 0; 3799 } 3800 if (*s == STL_USER_HL) 3801 { 3802 item[curitem].type = Highlight; 3803 item[curitem].start = p; 3804 item[curitem].minwid = minwid > 9 ? 1 : minwid; 3805 s++; 3806 curitem++; 3807 continue; 3808 } 3809 if (*s == STL_TABPAGENR || *s == STL_TABCLOSENR) 3810 { 3811 if (*s == STL_TABCLOSENR) 3812 { 3813 if (minwid == 0) 3814 { 3815 /* %X ends the close label, go back to the previously 3816 * define tab label nr. */ 3817 for (n = curitem - 1; n >= 0; --n) 3818 if (item[n].type == TabPage && item[n].minwid >= 0) 3819 { 3820 minwid = item[n].minwid; 3821 break; 3822 } 3823 } 3824 else 3825 /* close nrs are stored as negative values */ 3826 minwid = - minwid; 3827 } 3828 item[curitem].type = TabPage; 3829 item[curitem].start = p; 3830 item[curitem].minwid = minwid; 3831 s++; 3832 curitem++; 3833 continue; 3834 } 3835 if (*s == '.') 3836 { 3837 s++; 3838 if (VIM_ISDIGIT(*s)) 3839 { 3840 maxwid = (int)getdigits(&s); 3841 if (maxwid <= 0) /* overflow */ 3842 maxwid = 50; 3843 } 3844 } 3845 minwid = (minwid > 50 ? 50 : minwid) * l; 3846 if (*s == '(') 3847 { 3848 groupitem[groupdepth++] = curitem; 3849 item[curitem].type = Group; 3850 item[curitem].start = p; 3851 item[curitem].minwid = minwid; 3852 item[curitem].maxwid = maxwid; 3853 s++; 3854 curitem++; 3855 continue; 3856 } 3857 if (vim_strchr(STL_ALL, *s) == NULL) 3858 { 3859 s++; 3860 continue; 3861 } 3862 opt = *s++; 3863 3864 /* OK - now for the real work */ 3865 base = 'D'; 3866 itemisflag = FALSE; 3867 fillable = TRUE; 3868 num = -1; 3869 str = NULL; 3870 switch (opt) 3871 { 3872 case STL_FILEPATH: 3873 case STL_FULLPATH: 3874 case STL_FILENAME: 3875 fillable = FALSE; /* don't change ' ' to fillchar */ 3876 if (buf_spname(wp->w_buffer) != NULL) 3877 vim_strncpy(NameBuff, buf_spname(wp->w_buffer), MAXPATHL - 1); 3878 else 3879 { 3880 t = (opt == STL_FULLPATH) ? wp->w_buffer->b_ffname 3881 : wp->w_buffer->b_fname; 3882 home_replace(wp->w_buffer, t, NameBuff, MAXPATHL, TRUE); 3883 } 3884 trans_characters(NameBuff, MAXPATHL); 3885 if (opt != STL_FILENAME) 3886 str = NameBuff; 3887 else 3888 str = gettail(NameBuff); 3889 break; 3890 3891 case STL_VIM_EXPR: /* '{' */ 3892 itemisflag = TRUE; 3893 t = p; 3894 while (*s != '}' && *s != NUL && p + 1 < out + outlen) 3895 *p++ = *s++; 3896 if (*s != '}') /* missing '}' or out of space */ 3897 break; 3898 s++; 3899 *p = 0; 3900 p = t; 3901 3902 #ifdef FEAT_EVAL 3903 vim_snprintf((char *)tmp, sizeof(tmp), "%d", curbuf->b_fnum); 3904 set_internal_string_var((char_u *)"actual_curbuf", tmp); 3905 3906 o_curbuf = curbuf; 3907 o_curwin = curwin; 3908 curwin = wp; 3909 curbuf = wp->w_buffer; 3910 3911 str = eval_to_string_safe(p, &t, use_sandbox); 3912 3913 curwin = o_curwin; 3914 curbuf = o_curbuf; 3915 do_unlet((char_u *)"g:actual_curbuf", TRUE); 3916 3917 if (str != NULL && *str != 0) 3918 { 3919 if (*skipdigits(str) == NUL) 3920 { 3921 num = atoi((char *)str); 3922 vim_free(str); 3923 str = NULL; 3924 itemisflag = FALSE; 3925 } 3926 } 3927 #endif 3928 break; 3929 3930 case STL_LINE: 3931 num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) 3932 ? 0L : (long)(wp->w_cursor.lnum); 3933 break; 3934 3935 case STL_NUMLINES: 3936 num = wp->w_buffer->b_ml.ml_line_count; 3937 break; 3938 3939 case STL_COLUMN: 3940 num = !(State & INSERT) && empty_line 3941 ? 0 : (int)wp->w_cursor.col + 1; 3942 break; 3943 3944 case STL_VIRTCOL: 3945 case STL_VIRTCOL_ALT: 3946 /* In list mode virtcol needs to be recomputed */ 3947 virtcol = wp->w_virtcol; 3948 if (wp->w_p_list && lcs_tab1 == NUL) 3949 { 3950 wp->w_p_list = FALSE; 3951 getvcol(wp, &wp->w_cursor, NULL, &virtcol, NULL); 3952 wp->w_p_list = TRUE; 3953 } 3954 ++virtcol; 3955 /* Don't display %V if it's the same as %c. */ 3956 if (opt == STL_VIRTCOL_ALT 3957 && (virtcol == (colnr_T)(!(State & INSERT) && empty_line 3958 ? 0 : (int)wp->w_cursor.col + 1))) 3959 break; 3960 num = (long)virtcol; 3961 break; 3962 3963 case STL_PERCENTAGE: 3964 num = (int)(((long)wp->w_cursor.lnum * 100L) / 3965 (long)wp->w_buffer->b_ml.ml_line_count); 3966 break; 3967 3968 case STL_ALTPERCENT: 3969 str = tmp; 3970 get_rel_pos(wp, str, TMPLEN); 3971 break; 3972 3973 case STL_ARGLISTSTAT: 3974 fillable = FALSE; 3975 tmp[0] = 0; 3976 if (append_arg_number(wp, tmp, (int)sizeof(tmp), FALSE)) 3977 str = tmp; 3978 break; 3979 3980 case STL_KEYMAP: 3981 fillable = FALSE; 3982 if (get_keymap_str(wp, tmp, TMPLEN)) 3983 str = tmp; 3984 break; 3985 case STL_PAGENUM: 3986 #if defined(FEAT_PRINTER) || defined(FEAT_GUI_TABLINE) 3987 num = printer_page_num; 3988 #else 3989 num = 0; 3990 #endif 3991 break; 3992 3993 case STL_BUFNO: 3994 num = wp->w_buffer->b_fnum; 3995 break; 3996 3997 case STL_OFFSET_X: 3998 base = 'X'; 3999 case STL_OFFSET: 4000 #ifdef FEAT_BYTEOFF 4001 l = ml_find_line_or_offset(wp->w_buffer, wp->w_cursor.lnum, NULL); 4002 num = (wp->w_buffer->b_ml.ml_flags & ML_EMPTY) || l < 0 ? 4003 0L : l + 1 + (!(State & INSERT) && empty_line ? 4004 0 : (int)wp->w_cursor.col); 4005 #endif 4006 break; 4007 4008 case STL_BYTEVAL_X: 4009 base = 'X'; 4010 case STL_BYTEVAL: 4011 num = byteval; 4012 if (num == NL) 4013 num = 0; 4014 else if (num == CAR && get_fileformat(wp->w_buffer) == EOL_MAC) 4015 num = NL; 4016 break; 4017 4018 case STL_ROFLAG: 4019 case STL_ROFLAG_ALT: 4020 itemisflag = TRUE; 4021 if (wp->w_buffer->b_p_ro) 4022 str = (char_u *)((opt == STL_ROFLAG_ALT) ? ",RO" : _("[RO]")); 4023 break; 4024 4025 case STL_HELPFLAG: 4026 case STL_HELPFLAG_ALT: 4027 itemisflag = TRUE; 4028 if (wp->w_buffer->b_help) 4029 str = (char_u *)((opt == STL_HELPFLAG_ALT) ? ",HLP" 4030 : _("[Help]")); 4031 break; 4032 4033 #ifdef FEAT_AUTOCMD 4034 case STL_FILETYPE: 4035 if (*wp->w_buffer->b_p_ft != NUL 4036 && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 3) 4037 { 4038 vim_snprintf((char *)tmp, sizeof(tmp), "[%s]", 4039 wp->w_buffer->b_p_ft); 4040 str = tmp; 4041 } 4042 break; 4043 4044 case STL_FILETYPE_ALT: 4045 itemisflag = TRUE; 4046 if (*wp->w_buffer->b_p_ft != NUL 4047 && STRLEN(wp->w_buffer->b_p_ft) < TMPLEN - 2) 4048 { 4049 vim_snprintf((char *)tmp, sizeof(tmp), ",%s", 4050 wp->w_buffer->b_p_ft); 4051 for (t = tmp; *t != 0; t++) 4052 *t = TOUPPER_LOC(*t); 4053 str = tmp; 4054 } 4055 break; 4056 #endif 4057 4058 #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) 4059 case STL_PREVIEWFLAG: 4060 case STL_PREVIEWFLAG_ALT: 4061 itemisflag = TRUE; 4062 if (wp->w_p_pvw) 4063 str = (char_u *)((opt == STL_PREVIEWFLAG_ALT) ? ",PRV" 4064 : _("[Preview]")); 4065 break; 4066 4067 case STL_QUICKFIX: 4068 if (bt_quickfix(wp->w_buffer)) 4069 str = (char_u *)(wp->w_llist_ref 4070 ? _(msg_loclist) 4071 : _(msg_qflist)); 4072 break; 4073 #endif 4074 4075 case STL_MODIFIED: 4076 case STL_MODIFIED_ALT: 4077 itemisflag = TRUE; 4078 switch ((opt == STL_MODIFIED_ALT) 4079 + bufIsChanged(wp->w_buffer) * 2 4080 + (!wp->w_buffer->b_p_ma) * 4) 4081 { 4082 case 2: str = (char_u *)"[+]"; break; 4083 case 3: str = (char_u *)",+"; break; 4084 case 4: str = (char_u *)"[-]"; break; 4085 case 5: str = (char_u *)",-"; break; 4086 case 6: str = (char_u *)"[+-]"; break; 4087 case 7: str = (char_u *)",+-"; break; 4088 } 4089 break; 4090 4091 case STL_HIGHLIGHT: 4092 t = s; 4093 while (*s != '#' && *s != NUL) 4094 ++s; 4095 if (*s == '#') 4096 { 4097 item[curitem].type = Highlight; 4098 item[curitem].start = p; 4099 item[curitem].minwid = -syn_namen2id(t, (int)(s - t)); 4100 curitem++; 4101 } 4102 if (*s != NUL) 4103 ++s; 4104 continue; 4105 } 4106 4107 item[curitem].start = p; 4108 item[curitem].type = Normal; 4109 if (str != NULL && *str) 4110 { 4111 t = str; 4112 if (itemisflag) 4113 { 4114 if ((t[0] && t[1]) 4115 && ((!prevchar_isitem && *t == ',') 4116 || (prevchar_isflag && *t == ' '))) 4117 t++; 4118 prevchar_isflag = TRUE; 4119 } 4120 l = vim_strsize(t); 4121 if (l > 0) 4122 prevchar_isitem = TRUE; 4123 if (l > maxwid) 4124 { 4125 while (l >= maxwid) 4126 #ifdef FEAT_MBYTE 4127 if (has_mbyte) 4128 { 4129 l -= ptr2cells(t); 4130 t += (*mb_ptr2len)(t); 4131 } 4132 else 4133 #endif 4134 l -= byte2cells(*t++); 4135 if (p + 1 >= out + outlen) 4136 break; 4137 *p++ = '<'; 4138 } 4139 if (minwid > 0) 4140 { 4141 for (; l < minwid && p + 1 < out + outlen; l++) 4142 { 4143 /* Don't put a "-" in front of a digit. */ 4144 if (l + 1 == minwid && fillchar == '-' && VIM_ISDIGIT(*t)) 4145 *p++ = ' '; 4146 else 4147 *p++ = fillchar; 4148 } 4149 minwid = 0; 4150 } 4151 else 4152 minwid *= -1; 4153 while (*t && p + 1 < out + outlen) 4154 { 4155 *p++ = *t++; 4156 /* Change a space by fillchar, unless fillchar is '-' and a 4157 * digit follows. */ 4158 if (fillable && p[-1] == ' ' 4159 && (!VIM_ISDIGIT(*t) || fillchar != '-')) 4160 p[-1] = fillchar; 4161 } 4162 for (; l < minwid && p + 1 < out + outlen; l++) 4163 *p++ = fillchar; 4164 } 4165 else if (num >= 0) 4166 { 4167 int nbase = (base == 'D' ? 10 : (base == 'O' ? 8 : 16)); 4168 char_u nstr[20]; 4169 4170 if (p + 20 >= out + outlen) 4171 break; /* not sufficient space */ 4172 prevchar_isitem = TRUE; 4173 t = nstr; 4174 if (opt == STL_VIRTCOL_ALT) 4175 { 4176 *t++ = '-'; 4177 minwid--; 4178 } 4179 *t++ = '%'; 4180 if (zeropad) 4181 *t++ = '0'; 4182 *t++ = '*'; 4183 *t++ = nbase == 16 ? base : (char_u)(nbase == 8 ? 'o' : 'd'); 4184 *t = 0; 4185 4186 for (n = num, l = 1; n >= nbase; n /= nbase) 4187 l++; 4188 if (opt == STL_VIRTCOL_ALT) 4189 l++; 4190 if (l > maxwid) 4191 { 4192 l += 2; 4193 n = l - maxwid; 4194 while (l-- > maxwid) 4195 num /= nbase; 4196 *t++ = '>'; 4197 *t++ = '%'; 4198 *t = t[-3]; 4199 *++t = 0; 4200 vim_snprintf((char *)p, outlen - (p - out), (char *)nstr, 4201 0, num, n); 4202 } 4203 else 4204 vim_snprintf((char *)p, outlen - (p - out), (char *)nstr, 4205 minwid, num); 4206 p += STRLEN(p); 4207 } 4208 else 4209 item[curitem].type = Empty; 4210 4211 if (opt == STL_VIM_EXPR) 4212 vim_free(str); 4213 4214 if (num >= 0 || (!itemisflag && str && *str)) 4215 prevchar_isflag = FALSE; /* Item not NULL, but not a flag */ 4216 curitem++; 4217 } 4218 *p = NUL; 4219 itemcnt = curitem; 4220 4221 #ifdef FEAT_EVAL 4222 if (usefmt != fmt) 4223 vim_free(usefmt); 4224 #endif 4225 4226 width = vim_strsize(out); 4227 if (maxwidth > 0 && width > maxwidth) 4228 { 4229 /* Result is too long, must truncate somewhere. */ 4230 l = 0; 4231 if (itemcnt == 0) 4232 s = out; 4233 else 4234 { 4235 for ( ; l < itemcnt; l++) 4236 if (item[l].type == Trunc) 4237 { 4238 /* Truncate at %< item. */ 4239 s = item[l].start; 4240 break; 4241 } 4242 if (l == itemcnt) 4243 { 4244 /* No %< item, truncate first item. */ 4245 s = item[0].start; 4246 l = 0; 4247 } 4248 } 4249 4250 if (width - vim_strsize(s) >= maxwidth) 4251 { 4252 /* Truncation mark is beyond max length */ 4253 #ifdef FEAT_MBYTE 4254 if (has_mbyte) 4255 { 4256 s = out; 4257 width = 0; 4258 for (;;) 4259 { 4260 width += ptr2cells(s); 4261 if (width >= maxwidth) 4262 break; 4263 s += (*mb_ptr2len)(s); 4264 } 4265 /* Fill up for half a double-wide character. */ 4266 while (++width < maxwidth) 4267 *s++ = fillchar; 4268 } 4269 else 4270 #endif 4271 s = out + maxwidth - 1; 4272 for (l = 0; l < itemcnt; l++) 4273 if (item[l].start > s) 4274 break; 4275 itemcnt = l; 4276 *s++ = '>'; 4277 *s = 0; 4278 } 4279 else 4280 { 4281 #ifdef FEAT_MBYTE 4282 if (has_mbyte) 4283 { 4284 n = 0; 4285 while (width >= maxwidth) 4286 { 4287 width -= ptr2cells(s + n); 4288 n += (*mb_ptr2len)(s + n); 4289 } 4290 } 4291 else 4292 #endif 4293 n = width - maxwidth + 1; 4294 p = s + n; 4295 STRMOVE(s + 1, p); 4296 *s = '<'; 4297 4298 /* Fill up for half a double-wide character. */ 4299 while (++width < maxwidth) 4300 { 4301 s = s + STRLEN(s); 4302 *s++ = fillchar; 4303 *s = NUL; 4304 } 4305 4306 --n; /* count the '<' */ 4307 for (; l < itemcnt; l++) 4308 { 4309 if (item[l].start - n >= s) 4310 item[l].start -= n; 4311 else 4312 item[l].start = s; 4313 } 4314 } 4315 width = maxwidth; 4316 } 4317 else if (width < maxwidth && STRLEN(out) + maxwidth - width + 1 < outlen) 4318 { 4319 /* Apply STL_MIDDLE if any */ 4320 for (l = 0; l < itemcnt; l++) 4321 if (item[l].type == Middle) 4322 break; 4323 if (l < itemcnt) 4324 { 4325 p = item[l].start + maxwidth - width; 4326 STRMOVE(p, item[l].start); 4327 for (s = item[l].start; s < p; s++) 4328 *s = fillchar; 4329 for (l++; l < itemcnt; l++) 4330 item[l].start += maxwidth - width; 4331 width = maxwidth; 4332 } 4333 } 4334 4335 /* Store the info about highlighting. */ 4336 if (hltab != NULL) 4337 { 4338 sp = hltab; 4339 for (l = 0; l < itemcnt; l++) 4340 { 4341 if (item[l].type == Highlight) 4342 { 4343 sp->start = item[l].start; 4344 sp->userhl = item[l].minwid; 4345 sp++; 4346 } 4347 } 4348 sp->start = NULL; 4349 sp->userhl = 0; 4350 } 4351 4352 /* Store the info about tab pages labels. */ 4353 if (tabtab != NULL) 4354 { 4355 sp = tabtab; 4356 for (l = 0; l < itemcnt; l++) 4357 { 4358 if (item[l].type == TabPage) 4359 { 4360 sp->start = item[l].start; 4361 sp->userhl = item[l].minwid; 4362 sp++; 4363 } 4364 } 4365 sp->start = NULL; 4366 sp->userhl = 0; 4367 } 4368 4369 return width; 4370 } 4371 #endif /* FEAT_STL_OPT */ 4372 4373 #if defined(FEAT_STL_OPT) || defined(FEAT_CMDL_INFO) \ 4374 || defined(FEAT_GUI_TABLINE) || defined(PROTO) 4375 /* 4376 * Get relative cursor position in window into "buf[buflen]", in the form 99%, 4377 * using "Top", "Bot" or "All" when appropriate. 4378 */ 4379 void 4380 get_rel_pos(wp, buf, buflen) 4381 win_T *wp; 4382 char_u *buf; 4383 int buflen; 4384 { 4385 long above; /* number of lines above window */ 4386 long below; /* number of lines below window */ 4387 4388 above = wp->w_topline - 1; 4389 #ifdef FEAT_DIFF 4390 above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill; 4391 #endif 4392 below = wp->w_buffer->b_ml.ml_line_count - wp->w_botline + 1; 4393 if (below <= 0) 4394 vim_strncpy(buf, (char_u *)(above == 0 ? _("All") : _("Bot")), 4395 (size_t)(buflen - 1)); 4396 else if (above <= 0) 4397 vim_strncpy(buf, (char_u *)_("Top"), (size_t)(buflen - 1)); 4398 else 4399 vim_snprintf((char *)buf, (size_t)buflen, "%2d%%", above > 1000000L 4400 ? (int)(above / ((above + below) / 100L)) 4401 : (int)(above * 100L / (above + below))); 4402 } 4403 #endif 4404 4405 /* 4406 * Append (file 2 of 8) to "buf[buflen]", if editing more than one file. 4407 * Return TRUE if it was appended. 4408 */ 4409 static int 4410 append_arg_number(wp, buf, buflen, add_file) 4411 win_T *wp; 4412 char_u *buf; 4413 int buflen; 4414 int add_file; /* Add "file" before the arg number */ 4415 { 4416 char_u *p; 4417 4418 if (ARGCOUNT <= 1) /* nothing to do */ 4419 return FALSE; 4420 4421 p = buf + STRLEN(buf); /* go to the end of the buffer */ 4422 if (p - buf + 35 >= buflen) /* getting too long */ 4423 return FALSE; 4424 *p++ = ' '; 4425 *p++ = '('; 4426 if (add_file) 4427 { 4428 STRCPY(p, "file "); 4429 p += 5; 4430 } 4431 vim_snprintf((char *)p, (size_t)(buflen - (p - buf)), 4432 wp->w_arg_idx_invalid ? "(%d) of %d)" 4433 : "%d of %d)", wp->w_arg_idx + 1, ARGCOUNT); 4434 return TRUE; 4435 } 4436 4437 /* 4438 * If fname is not a full path, make it a full path. 4439 * Returns pointer to allocated memory (NULL for failure). 4440 */ 4441 char_u * 4442 fix_fname(fname) 4443 char_u *fname; 4444 { 4445 /* 4446 * Force expanding the path always for Unix, because symbolic links may 4447 * mess up the full path name, even though it starts with a '/'. 4448 * Also expand when there is ".." in the file name, try to remove it, 4449 * because "c:/src/../README" is equal to "c:/README". 4450 * Similarly "c:/src//file" is equal to "c:/src/file". 4451 * For MS-Windows also expand names like "longna~1" to "longname". 4452 */ 4453 #ifdef UNIX 4454 return FullName_save(fname, TRUE); 4455 #else 4456 if (!vim_isAbsName(fname) 4457 || strstr((char *)fname, "..") != NULL 4458 || strstr((char *)fname, "//") != NULL 4459 # ifdef BACKSLASH_IN_FILENAME 4460 || strstr((char *)fname, "\\\\") != NULL 4461 # endif 4462 # if defined(MSWIN) || defined(DJGPP) 4463 || vim_strchr(fname, '~') != NULL 4464 # endif 4465 ) 4466 return FullName_save(fname, FALSE); 4467 4468 fname = vim_strsave(fname); 4469 4470 # ifdef USE_FNAME_CASE 4471 # ifdef USE_LONG_FNAME 4472 if (USE_LONG_FNAME) 4473 # endif 4474 { 4475 if (fname != NULL) 4476 fname_case(fname, 0); /* set correct case for file name */ 4477 } 4478 # endif 4479 4480 return fname; 4481 #endif 4482 } 4483 4484 /* 4485 * Make "ffname" a full file name, set "sfname" to "ffname" if not NULL. 4486 * "ffname" becomes a pointer to allocated memory (or NULL). 4487 */ 4488 void 4489 fname_expand(buf, ffname, sfname) 4490 buf_T *buf UNUSED; 4491 char_u **ffname; 4492 char_u **sfname; 4493 { 4494 if (*ffname == NULL) /* if no file name given, nothing to do */ 4495 return; 4496 if (*sfname == NULL) /* if no short file name given, use ffname */ 4497 *sfname = *ffname; 4498 *ffname = fix_fname(*ffname); /* expand to full path */ 4499 4500 #ifdef FEAT_SHORTCUT 4501 if (!buf->b_p_bin) 4502 { 4503 char_u *rfname; 4504 4505 /* If the file name is a shortcut file, use the file it links to. */ 4506 rfname = mch_resolve_shortcut(*ffname); 4507 if (rfname != NULL) 4508 { 4509 vim_free(*ffname); 4510 *ffname = rfname; 4511 *sfname = rfname; 4512 } 4513 } 4514 #endif 4515 } 4516 4517 /* 4518 * Get the file name for an argument list entry. 4519 */ 4520 char_u * 4521 alist_name(aep) 4522 aentry_T *aep; 4523 { 4524 buf_T *bp; 4525 4526 /* Use the name from the associated buffer if it exists. */ 4527 bp = buflist_findnr(aep->ae_fnum); 4528 if (bp == NULL || bp->b_fname == NULL) 4529 return aep->ae_fname; 4530 return bp->b_fname; 4531 } 4532 4533 #if defined(FEAT_WINDOWS) || defined(PROTO) 4534 /* 4535 * do_arg_all(): Open up to 'count' windows, one for each argument. 4536 */ 4537 void 4538 do_arg_all(count, forceit, keep_tabs) 4539 int count; 4540 int forceit; /* hide buffers in current windows */ 4541 int keep_tabs; /* keep current tabs, for ":tab drop file" */ 4542 { 4543 int i; 4544 win_T *wp, *wpnext; 4545 char_u *opened; /* Array of weight for which args are open: 4546 * 0: not opened 4547 * 1: opened in other tab 4548 * 2: opened in curtab 4549 * 3: opened in curtab and curwin 4550 */ 4551 int opened_len; /* length of opened[] */ 4552 int use_firstwin = FALSE; /* use first window for arglist */ 4553 int split_ret = OK; 4554 int p_ea_save; 4555 alist_T *alist; /* argument list to be used */ 4556 buf_T *buf; 4557 tabpage_T *tpnext; 4558 int had_tab = cmdmod.tab; 4559 win_T *old_curwin, *last_curwin; 4560 tabpage_T *old_curtab, *last_curtab; 4561 win_T *new_curwin = NULL; 4562 tabpage_T *new_curtab = NULL; 4563 4564 if (ARGCOUNT <= 0) 4565 { 4566 /* Don't give an error message. We don't want it when the ":all" 4567 * command is in the .vimrc. */ 4568 return; 4569 } 4570 setpcmark(); 4571 4572 opened_len = ARGCOUNT; 4573 opened = alloc_clear((unsigned)opened_len); 4574 if (opened == NULL) 4575 return; 4576 4577 /* Autocommands may do anything to the argument list. Make sure it's not 4578 * freed while we are working here by "locking" it. We still have to 4579 * watch out for its size to be changed. */ 4580 alist = curwin->w_alist; 4581 ++alist->al_refcount; 4582 4583 old_curwin = curwin; 4584 old_curtab = curtab; 4585 4586 #ifdef FEAT_GUI 4587 need_mouse_correct = TRUE; 4588 #endif 4589 4590 /* 4591 * Try closing all windows that are not in the argument list. 4592 * Also close windows that are not full width; 4593 * When 'hidden' or "forceit" set the buffer becomes hidden. 4594 * Windows that have a changed buffer and can't be hidden won't be closed. 4595 * When the ":tab" modifier was used do this for all tab pages. 4596 */ 4597 if (had_tab > 0) 4598 goto_tabpage_tp(first_tabpage, TRUE, TRUE); 4599 for (;;) 4600 { 4601 tpnext = curtab->tp_next; 4602 for (wp = firstwin; wp != NULL; wp = wpnext) 4603 { 4604 wpnext = wp->w_next; 4605 buf = wp->w_buffer; 4606 if (buf->b_ffname == NULL 4607 || (!keep_tabs && buf->b_nwindows > 1) 4608 #ifdef FEAT_VERTSPLIT 4609 || wp->w_width != Columns 4610 #endif 4611 ) 4612 i = opened_len; 4613 else 4614 { 4615 /* check if the buffer in this window is in the arglist */ 4616 for (i = 0; i < opened_len; ++i) 4617 { 4618 if (i < alist->al_ga.ga_len 4619 && (AARGLIST(alist)[i].ae_fnum == buf->b_fnum 4620 || fullpathcmp(alist_name(&AARGLIST(alist)[i]), 4621 buf->b_ffname, TRUE) & FPC_SAME)) 4622 { 4623 int weight = 1; 4624 4625 if (old_curtab == curtab) 4626 { 4627 ++weight; 4628 if (old_curwin == wp) 4629 ++weight; 4630 } 4631 4632 if (weight > (int)opened[i]) 4633 { 4634 opened[i] = (char_u)weight; 4635 if (i == 0) 4636 { 4637 if (new_curwin != NULL) 4638 new_curwin->w_arg_idx = opened_len; 4639 new_curwin = wp; 4640 new_curtab = curtab; 4641 } 4642 } 4643 else if (keep_tabs) 4644 i = opened_len; 4645 4646 if (wp->w_alist != alist) 4647 { 4648 /* Use the current argument list for all windows 4649 * containing a file from it. */ 4650 alist_unlink(wp->w_alist); 4651 wp->w_alist = alist; 4652 ++wp->w_alist->al_refcount; 4653 } 4654 break; 4655 } 4656 } 4657 } 4658 wp->w_arg_idx = i; 4659 4660 if (i == opened_len && !keep_tabs)/* close this window */ 4661 { 4662 if (P_HID(buf) || forceit || buf->b_nwindows > 1 4663 || !bufIsChanged(buf)) 4664 { 4665 /* If the buffer was changed, and we would like to hide it, 4666 * try autowriting. */ 4667 if (!P_HID(buf) && buf->b_nwindows <= 1 4668 && bufIsChanged(buf)) 4669 { 4670 (void)autowrite(buf, FALSE); 4671 #ifdef FEAT_AUTOCMD 4672 /* check if autocommands removed the window */ 4673 if (!win_valid(wp) || !buf_valid(buf)) 4674 { 4675 wpnext = firstwin; /* start all over... */ 4676 continue; 4677 } 4678 #endif 4679 } 4680 #ifdef FEAT_WINDOWS 4681 /* don't close last window */ 4682 if (firstwin == lastwin 4683 && (first_tabpage->tp_next == NULL || !had_tab)) 4684 #endif 4685 use_firstwin = TRUE; 4686 #ifdef FEAT_WINDOWS 4687 else 4688 { 4689 win_close(wp, !P_HID(buf) && !bufIsChanged(buf)); 4690 # ifdef FEAT_AUTOCMD 4691 /* check if autocommands removed the next window */ 4692 if (!win_valid(wpnext)) 4693 wpnext = firstwin; /* start all over... */ 4694 # endif 4695 } 4696 #endif 4697 } 4698 } 4699 } 4700 4701 /* Without the ":tab" modifier only do the current tab page. */ 4702 if (had_tab == 0 || tpnext == NULL) 4703 break; 4704 4705 # ifdef FEAT_AUTOCMD 4706 /* check if autocommands removed the next tab page */ 4707 if (!valid_tabpage(tpnext)) 4708 tpnext = first_tabpage; /* start all over...*/ 4709 # endif 4710 goto_tabpage_tp(tpnext, TRUE, TRUE); 4711 } 4712 4713 /* 4714 * Open a window for files in the argument list that don't have one. 4715 * ARGCOUNT may change while doing this, because of autocommands. 4716 */ 4717 if (count > opened_len || count <= 0) 4718 count = opened_len; 4719 4720 #ifdef FEAT_AUTOCMD 4721 /* Don't execute Win/Buf Enter/Leave autocommands here. */ 4722 ++autocmd_no_enter; 4723 ++autocmd_no_leave; 4724 #endif 4725 last_curwin = curwin; 4726 last_curtab = curtab; 4727 win_enter(lastwin, FALSE); 4728 #ifdef FEAT_WINDOWS 4729 /* ":drop all" should re-use an empty window to avoid "--remote-tab" 4730 * leaving an empty tab page when executed locally. */ 4731 if (keep_tabs && bufempty() && curbuf->b_nwindows == 1 4732 && curbuf->b_ffname == NULL && !curbuf->b_changed) 4733 use_firstwin = TRUE; 4734 #endif 4735 4736 for (i = 0; i < count && i < opened_len && !got_int; ++i) 4737 { 4738 if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) 4739 arg_had_last = TRUE; 4740 if (opened[i] > 0) 4741 { 4742 /* Move the already present window to below the current window */ 4743 if (curwin->w_arg_idx != i) 4744 { 4745 for (wpnext = firstwin; wpnext != NULL; wpnext = wpnext->w_next) 4746 { 4747 if (wpnext->w_arg_idx == i) 4748 { 4749 if (keep_tabs) 4750 { 4751 new_curwin = wpnext; 4752 new_curtab = curtab; 4753 } 4754 else 4755 win_move_after(wpnext, curwin); 4756 break; 4757 } 4758 } 4759 } 4760 } 4761 else if (split_ret == OK) 4762 { 4763 if (!use_firstwin) /* split current window */ 4764 { 4765 p_ea_save = p_ea; 4766 p_ea = TRUE; /* use space from all windows */ 4767 split_ret = win_split(0, WSP_ROOM | WSP_BELOW); 4768 p_ea = p_ea_save; 4769 if (split_ret == FAIL) 4770 continue; 4771 } 4772 #ifdef FEAT_AUTOCMD 4773 else /* first window: do autocmd for leaving this buffer */ 4774 --autocmd_no_leave; 4775 #endif 4776 4777 /* 4778 * edit file "i" 4779 */ 4780 curwin->w_arg_idx = i; 4781 if (i == 0) 4782 { 4783 new_curwin = curwin; 4784 new_curtab = curtab; 4785 } 4786 (void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, 4787 ECMD_ONE, 4788 ((P_HID(curwin->w_buffer) 4789 || bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) 4790 + ECMD_OLDBUF, curwin); 4791 #ifdef FEAT_AUTOCMD 4792 if (use_firstwin) 4793 ++autocmd_no_leave; 4794 #endif 4795 use_firstwin = FALSE; 4796 } 4797 ui_breakcheck(); 4798 4799 /* When ":tab" was used open a new tab for a new window repeatedly. */ 4800 if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) 4801 cmdmod.tab = 9999; 4802 } 4803 4804 /* Remove the "lock" on the argument list. */ 4805 alist_unlink(alist); 4806 4807 #ifdef FEAT_AUTOCMD 4808 --autocmd_no_enter; 4809 #endif 4810 /* restore last referenced tabpage's curwin */ 4811 if (last_curtab != new_curtab) 4812 { 4813 if (valid_tabpage(last_curtab)) 4814 goto_tabpage_tp(last_curtab, TRUE, TRUE); 4815 if (win_valid(last_curwin)) 4816 win_enter(last_curwin, FALSE); 4817 } 4818 /* to window with first arg */ 4819 if (valid_tabpage(new_curtab)) 4820 goto_tabpage_tp(new_curtab, TRUE, TRUE); 4821 if (win_valid(new_curwin)) 4822 win_enter(new_curwin, FALSE); 4823 4824 #ifdef FEAT_AUTOCMD 4825 --autocmd_no_leave; 4826 #endif 4827 vim_free(opened); 4828 } 4829 4830 # if defined(FEAT_LISTCMDS) || defined(PROTO) 4831 /* 4832 * Open a window for a number of buffers. 4833 */ 4834 void 4835 ex_buffer_all(eap) 4836 exarg_T *eap; 4837 { 4838 buf_T *buf; 4839 win_T *wp, *wpnext; 4840 int split_ret = OK; 4841 int p_ea_save; 4842 int open_wins = 0; 4843 int r; 4844 int count; /* Maximum number of windows to open. */ 4845 int all; /* When TRUE also load inactive buffers. */ 4846 #ifdef FEAT_WINDOWS 4847 int had_tab = cmdmod.tab; 4848 tabpage_T *tpnext; 4849 #endif 4850 4851 if (eap->addr_count == 0) /* make as many windows as possible */ 4852 count = 9999; 4853 else 4854 count = eap->line2; /* make as many windows as specified */ 4855 if (eap->cmdidx == CMD_unhide || eap->cmdidx == CMD_sunhide) 4856 all = FALSE; 4857 else 4858 all = TRUE; 4859 4860 setpcmark(); 4861 4862 #ifdef FEAT_GUI 4863 need_mouse_correct = TRUE; 4864 #endif 4865 4866 /* 4867 * Close superfluous windows (two windows for the same buffer). 4868 * Also close windows that are not full-width. 4869 */ 4870 #ifdef FEAT_WINDOWS 4871 if (had_tab > 0) 4872 goto_tabpage_tp(first_tabpage, TRUE, TRUE); 4873 for (;;) 4874 { 4875 #endif 4876 tpnext = curtab->tp_next; 4877 for (wp = firstwin; wp != NULL; wp = wpnext) 4878 { 4879 wpnext = wp->w_next; 4880 if ((wp->w_buffer->b_nwindows > 1 4881 #ifdef FEAT_VERTSPLIT 4882 || ((cmdmod.split & WSP_VERT) 4883 ? wp->w_height + wp->w_status_height < Rows - p_ch 4884 - tabline_height() 4885 : wp->w_width != Columns) 4886 #endif 4887 #ifdef FEAT_WINDOWS 4888 || (had_tab > 0 && wp != firstwin) 4889 #endif 4890 ) && firstwin != lastwin 4891 #ifdef FEAT_AUTOCMD 4892 && !(wp->w_closing || wp->w_buffer->b_closing) 4893 #endif 4894 ) 4895 { 4896 win_close(wp, FALSE); 4897 #ifdef FEAT_AUTOCMD 4898 wpnext = firstwin; /* just in case an autocommand does 4899 something strange with windows */ 4900 tpnext = first_tabpage; /* start all over...*/ 4901 open_wins = 0; 4902 #endif 4903 } 4904 else 4905 ++open_wins; 4906 } 4907 4908 #ifdef FEAT_WINDOWS 4909 /* Without the ":tab" modifier only do the current tab page. */ 4910 if (had_tab == 0 || tpnext == NULL) 4911 break; 4912 goto_tabpage_tp(tpnext, TRUE, TRUE); 4913 } 4914 #endif 4915 4916 /* 4917 * Go through the buffer list. When a buffer doesn't have a window yet, 4918 * open one. Otherwise move the window to the right position. 4919 * Watch out for autocommands that delete buffers or windows! 4920 */ 4921 #ifdef FEAT_AUTOCMD 4922 /* Don't execute Win/Buf Enter/Leave autocommands here. */ 4923 ++autocmd_no_enter; 4924 #endif 4925 win_enter(lastwin, FALSE); 4926 #ifdef FEAT_AUTOCMD 4927 ++autocmd_no_leave; 4928 #endif 4929 for (buf = firstbuf; buf != NULL && open_wins < count; buf = buf->b_next) 4930 { 4931 /* Check if this buffer needs a window */ 4932 if ((!all && buf->b_ml.ml_mfp == NULL) || !buf->b_p_bl) 4933 continue; 4934 4935 #ifdef FEAT_WINDOWS 4936 if (had_tab != 0) 4937 { 4938 /* With the ":tab" modifier don't move the window. */ 4939 if (buf->b_nwindows > 0) 4940 wp = lastwin; /* buffer has a window, skip it */ 4941 else 4942 wp = NULL; 4943 } 4944 else 4945 #endif 4946 { 4947 /* Check if this buffer already has a window */ 4948 for (wp = firstwin; wp != NULL; wp = wp->w_next) 4949 if (wp->w_buffer == buf) 4950 break; 4951 /* If the buffer already has a window, move it */ 4952 if (wp != NULL) 4953 win_move_after(wp, curwin); 4954 } 4955 4956 if (wp == NULL && split_ret == OK) 4957 { 4958 /* Split the window and put the buffer in it */ 4959 p_ea_save = p_ea; 4960 p_ea = TRUE; /* use space from all windows */ 4961 split_ret = win_split(0, WSP_ROOM | WSP_BELOW); 4962 ++open_wins; 4963 p_ea = p_ea_save; 4964 if (split_ret == FAIL) 4965 continue; 4966 4967 /* Open the buffer in this window. */ 4968 #if defined(HAS_SWAP_EXISTS_ACTION) 4969 swap_exists_action = SEA_DIALOG; 4970 #endif 4971 set_curbuf(buf, DOBUF_GOTO); 4972 #ifdef FEAT_AUTOCMD 4973 if (!buf_valid(buf)) /* autocommands deleted the buffer!!! */ 4974 { 4975 #if defined(HAS_SWAP_EXISTS_ACTION) 4976 swap_exists_action = SEA_NONE; 4977 # endif 4978 break; 4979 } 4980 #endif 4981 #if defined(HAS_SWAP_EXISTS_ACTION) 4982 if (swap_exists_action == SEA_QUIT) 4983 { 4984 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 4985 cleanup_T cs; 4986 4987 /* Reset the error/interrupt/exception state here so that 4988 * aborting() returns FALSE when closing a window. */ 4989 enter_cleanup(&cs); 4990 # endif 4991 4992 /* User selected Quit at ATTENTION prompt; close this window. */ 4993 win_close(curwin, TRUE); 4994 --open_wins; 4995 swap_exists_action = SEA_NONE; 4996 swap_exists_did_quit = TRUE; 4997 4998 # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL) 4999 /* Restore the error/interrupt/exception state if not 5000 * discarded by a new aborting error, interrupt, or uncaught 5001 * exception. */ 5002 leave_cleanup(&cs); 5003 # endif 5004 } 5005 else 5006 handle_swap_exists(NULL); 5007 #endif 5008 } 5009 5010 ui_breakcheck(); 5011 if (got_int) 5012 { 5013 (void)vgetc(); /* only break the file loading, not the rest */ 5014 break; 5015 } 5016 #ifdef FEAT_EVAL 5017 /* Autocommands deleted the buffer or aborted script processing!!! */ 5018 if (aborting()) 5019 break; 5020 #endif 5021 #ifdef FEAT_WINDOWS 5022 /* When ":tab" was used open a new tab for a new window repeatedly. */ 5023 if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) 5024 cmdmod.tab = 9999; 5025 #endif 5026 } 5027 #ifdef FEAT_AUTOCMD 5028 --autocmd_no_enter; 5029 #endif 5030 win_enter(firstwin, FALSE); /* back to first window */ 5031 #ifdef FEAT_AUTOCMD 5032 --autocmd_no_leave; 5033 #endif 5034 5035 /* 5036 * Close superfluous windows. 5037 */ 5038 for (wp = lastwin; open_wins > count; ) 5039 { 5040 r = (P_HID(wp->w_buffer) || !bufIsChanged(wp->w_buffer) 5041 || autowrite(wp->w_buffer, FALSE) == OK); 5042 #ifdef FEAT_AUTOCMD 5043 if (!win_valid(wp)) 5044 { 5045 /* BufWrite Autocommands made the window invalid, start over */ 5046 wp = lastwin; 5047 } 5048 else 5049 #endif 5050 if (r) 5051 { 5052 win_close(wp, !P_HID(wp->w_buffer)); 5053 --open_wins; 5054 wp = lastwin; 5055 } 5056 else 5057 { 5058 wp = wp->w_prev; 5059 if (wp == NULL) 5060 break; 5061 } 5062 } 5063 } 5064 # endif /* FEAT_LISTCMDS */ 5065 5066 #endif /* FEAT_WINDOWS */ 5067 5068 static int chk_modeline __ARGS((linenr_T, int)); 5069 5070 /* 5071 * do_modelines() - process mode lines for the current file 5072 * 5073 * "flags" can be: 5074 * OPT_WINONLY only set options local to window 5075 * OPT_NOWIN don't set options local to window 5076 * 5077 * Returns immediately if the "ml" option isn't set. 5078 */ 5079 void 5080 do_modelines(flags) 5081 int flags; 5082 { 5083 linenr_T lnum; 5084 int nmlines; 5085 static int entered = 0; 5086 5087 if (!curbuf->b_p_ml || (nmlines = (int)p_mls) == 0) 5088 return; 5089 5090 /* Disallow recursive entry here. Can happen when executing a modeline 5091 * triggers an autocommand, which reloads modelines with a ":do". */ 5092 if (entered) 5093 return; 5094 5095 ++entered; 5096 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count && lnum <= nmlines; 5097 ++lnum) 5098 if (chk_modeline(lnum, flags) == FAIL) 5099 nmlines = 0; 5100 5101 for (lnum = curbuf->b_ml.ml_line_count; lnum > 0 && lnum > nmlines 5102 && lnum > curbuf->b_ml.ml_line_count - nmlines; --lnum) 5103 if (chk_modeline(lnum, flags) == FAIL) 5104 nmlines = 0; 5105 --entered; 5106 } 5107 5108 #include "version.h" /* for version number */ 5109 5110 /* 5111 * chk_modeline() - check a single line for a mode string 5112 * Return FAIL if an error encountered. 5113 */ 5114 static int 5115 chk_modeline(lnum, flags) 5116 linenr_T lnum; 5117 int flags; /* Same as for do_modelines(). */ 5118 { 5119 char_u *s; 5120 char_u *e; 5121 char_u *linecopy; /* local copy of any modeline found */ 5122 int prev; 5123 int vers; 5124 int end; 5125 int retval = OK; 5126 char_u *save_sourcing_name; 5127 linenr_T save_sourcing_lnum; 5128 #ifdef FEAT_EVAL 5129 scid_T save_SID; 5130 #endif 5131 5132 prev = -1; 5133 for (s = ml_get(lnum); *s != NUL; ++s) 5134 { 5135 if (prev == -1 || vim_isspace(prev)) 5136 { 5137 if ((prev != -1 && STRNCMP(s, "ex:", (size_t)3) == 0) 5138 || STRNCMP(s, "vi:", (size_t)3) == 0) 5139 break; 5140 /* Accept both "vim" and "Vim". */ 5141 if ((s[0] == 'v' || s[0] == 'V') && s[1] == 'i' && s[2] == 'm') 5142 { 5143 if (s[3] == '<' || s[3] == '=' || s[3] == '>') 5144 e = s + 4; 5145 else 5146 e = s + 3; 5147 vers = getdigits(&e); 5148 if (*e == ':' 5149 && (s[0] != 'V' 5150 || STRNCMP(skipwhite(e + 1), "set", 3) == 0) 5151 && (s[3] == ':' 5152 || (VIM_VERSION_100 >= vers && isdigit(s[3])) 5153 || (VIM_VERSION_100 < vers && s[3] == '<') 5154 || (VIM_VERSION_100 > vers && s[3] == '>') 5155 || (VIM_VERSION_100 == vers && s[3] == '='))) 5156 break; 5157 } 5158 } 5159 prev = *s; 5160 } 5161 5162 if (*s) 5163 { 5164 do /* skip over "ex:", "vi:" or "vim:" */ 5165 ++s; 5166 while (s[-1] != ':'); 5167 5168 s = linecopy = vim_strsave(s); /* copy the line, it will change */ 5169 if (linecopy == NULL) 5170 return FAIL; 5171 5172 save_sourcing_lnum = sourcing_lnum; 5173 save_sourcing_name = sourcing_name; 5174 sourcing_lnum = lnum; /* prepare for emsg() */ 5175 sourcing_name = (char_u *)"modelines"; 5176 5177 end = FALSE; 5178 while (end == FALSE) 5179 { 5180 s = skipwhite(s); 5181 if (*s == NUL) 5182 break; 5183 5184 /* 5185 * Find end of set command: ':' or end of line. 5186 * Skip over "\:", replacing it with ":". 5187 */ 5188 for (e = s; *e != ':' && *e != NUL; ++e) 5189 if (e[0] == '\\' && e[1] == ':') 5190 STRMOVE(e, e + 1); 5191 if (*e == NUL) 5192 end = TRUE; 5193 5194 /* 5195 * If there is a "set" command, require a terminating ':' and 5196 * ignore the stuff after the ':'. 5197 * "vi:set opt opt opt: foo" -- foo not interpreted 5198 * "vi:opt opt opt: foo" -- foo interpreted 5199 * Accept "se" for compatibility with Elvis. 5200 */ 5201 if (STRNCMP(s, "set ", (size_t)4) == 0 5202 || STRNCMP(s, "se ", (size_t)3) == 0) 5203 { 5204 if (*e != ':') /* no terminating ':'? */ 5205 break; 5206 end = TRUE; 5207 s = vim_strchr(s, ' ') + 1; 5208 } 5209 *e = NUL; /* truncate the set command */ 5210 5211 if (*s != NUL) /* skip over an empty "::" */ 5212 { 5213 #ifdef FEAT_EVAL 5214 save_SID = current_SID; 5215 current_SID = SID_MODELINE; 5216 #endif 5217 retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags); 5218 #ifdef FEAT_EVAL 5219 current_SID = save_SID; 5220 #endif 5221 if (retval == FAIL) /* stop if error found */ 5222 break; 5223 } 5224 s = e + 1; /* advance to next part */ 5225 } 5226 5227 sourcing_lnum = save_sourcing_lnum; 5228 sourcing_name = save_sourcing_name; 5229 5230 vim_free(linecopy); 5231 } 5232 return retval; 5233 } 5234 5235 #if defined(FEAT_VIMINFO) || defined(PROTO) 5236 int 5237 read_viminfo_bufferlist(virp, writing) 5238 vir_T *virp; 5239 int writing; 5240 { 5241 char_u *tab; 5242 linenr_T lnum; 5243 colnr_T col; 5244 buf_T *buf; 5245 char_u *sfname; 5246 char_u *xline; 5247 5248 /* Handle long line and escaped characters. */ 5249 xline = viminfo_readstring(virp, 1, FALSE); 5250 5251 /* don't read in if there are files on the command-line or if writing: */ 5252 if (xline != NULL && !writing && ARGCOUNT == 0 5253 && find_viminfo_parameter('%') != NULL) 5254 { 5255 /* Format is: <fname> Tab <lnum> Tab <col>. 5256 * Watch out for a Tab in the file name, work from the end. */ 5257 lnum = 0; 5258 col = 0; 5259 tab = vim_strrchr(xline, '\t'); 5260 if (tab != NULL) 5261 { 5262 *tab++ = '\0'; 5263 col = (colnr_T)atoi((char *)tab); 5264 tab = vim_strrchr(xline, '\t'); 5265 if (tab != NULL) 5266 { 5267 *tab++ = '\0'; 5268 lnum = atol((char *)tab); 5269 } 5270 } 5271 5272 /* Expand "~/" in the file name at "line + 1" to a full path. 5273 * Then try shortening it by comparing with the current directory */ 5274 expand_env(xline, NameBuff, MAXPATHL); 5275 sfname = shorten_fname1(NameBuff); 5276 5277 buf = buflist_new(NameBuff, sfname, (linenr_T)0, BLN_LISTED); 5278 if (buf != NULL) /* just in case... */ 5279 { 5280 buf->b_last_cursor.lnum = lnum; 5281 buf->b_last_cursor.col = col; 5282 buflist_setfpos(buf, curwin, lnum, col, FALSE); 5283 } 5284 } 5285 vim_free(xline); 5286 5287 return viminfo_readline(virp); 5288 } 5289 5290 void 5291 write_viminfo_bufferlist(fp) 5292 FILE *fp; 5293 { 5294 buf_T *buf; 5295 #ifdef FEAT_WINDOWS 5296 win_T *win; 5297 tabpage_T *tp; 5298 #endif 5299 char_u *line; 5300 int max_buffers; 5301 5302 if (find_viminfo_parameter('%') == NULL) 5303 return; 5304 5305 /* Without a number -1 is returned: do all buffers. */ 5306 max_buffers = get_viminfo_parameter('%'); 5307 5308 /* Allocate room for the file name, lnum and col. */ 5309 #define LINE_BUF_LEN (MAXPATHL + 40) 5310 line = alloc(LINE_BUF_LEN); 5311 if (line == NULL) 5312 return; 5313 5314 #ifdef FEAT_WINDOWS 5315 FOR_ALL_TAB_WINDOWS(tp, win) 5316 set_last_cursor(win); 5317 #else 5318 set_last_cursor(curwin); 5319 #endif 5320 5321 fputs(_("\n# Buffer list:\n"), fp); 5322 for (buf = firstbuf; buf != NULL ; buf = buf->b_next) 5323 { 5324 if (buf->b_fname == NULL 5325 || !buf->b_p_bl 5326 #ifdef FEAT_QUICKFIX 5327 || bt_quickfix(buf) 5328 #endif 5329 || removable(buf->b_ffname)) 5330 continue; 5331 5332 if (max_buffers-- == 0) 5333 break; 5334 putc('%', fp); 5335 home_replace(NULL, buf->b_ffname, line, MAXPATHL, TRUE); 5336 vim_snprintf_add((char *)line, LINE_BUF_LEN, "\t%ld\t%d", 5337 (long)buf->b_last_cursor.lnum, 5338 buf->b_last_cursor.col); 5339 viminfo_writestring(fp, line); 5340 } 5341 vim_free(line); 5342 } 5343 #endif 5344 5345 5346 /* 5347 * Return special buffer name. 5348 * Returns NULL when the buffer has a normal file name. 5349 */ 5350 char_u * 5351 buf_spname(buf) 5352 buf_T *buf; 5353 { 5354 #if defined(FEAT_QUICKFIX) && defined(FEAT_WINDOWS) 5355 if (bt_quickfix(buf)) 5356 { 5357 win_T *win; 5358 tabpage_T *tp; 5359 5360 /* 5361 * For location list window, w_llist_ref points to the location list. 5362 * For quickfix window, w_llist_ref is NULL. 5363 */ 5364 if (find_win_for_buf(buf, &win, &tp) == OK && win->w_llist_ref != NULL) 5365 return (char_u *)_(msg_loclist); 5366 else 5367 return (char_u *)_(msg_qflist); 5368 } 5369 #endif 5370 #ifdef FEAT_QUICKFIX 5371 /* There is no _file_ when 'buftype' is "nofile", b_sfname 5372 * contains the name as specified by the user */ 5373 if (bt_nofile(buf)) 5374 { 5375 if (buf->b_sfname != NULL) 5376 return buf->b_sfname; 5377 return (char_u *)_("[Scratch]"); 5378 } 5379 #endif 5380 if (buf->b_fname == NULL) 5381 return (char_u *)_("[No Name]"); 5382 return NULL; 5383 } 5384 5385 #if (defined(FEAT_QUICKFIX) && defined(FEAT_WINDOWS)) \ 5386 || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \ 5387 || defined(PROTO) 5388 /* 5389 * Find a window for buffer "buf". 5390 * If found OK is returned and "wp" and "tp" are set to the window and tabpage. 5391 * If not found FAIL is returned. 5392 */ 5393 int 5394 find_win_for_buf(buf, wp, tp) 5395 buf_T *buf; 5396 win_T **wp; 5397 tabpage_T **tp; 5398 { 5399 FOR_ALL_TAB_WINDOWS(*tp, *wp) 5400 if ((*wp)->w_buffer == buf) 5401 goto win_found; 5402 return FAIL; 5403 win_found: 5404 return OK; 5405 } 5406 #endif 5407 5408 #if defined(FEAT_SIGNS) || defined(PROTO) 5409 /* 5410 * Insert the sign into the signlist. 5411 */ 5412 static void 5413 insert_sign(buf, prev, next, id, lnum, typenr) 5414 buf_T *buf; /* buffer to store sign in */ 5415 signlist_T *prev; /* previous sign entry */ 5416 signlist_T *next; /* next sign entry */ 5417 int id; /* sign ID */ 5418 linenr_T lnum; /* line number which gets the mark */ 5419 int typenr; /* typenr of sign we are adding */ 5420 { 5421 signlist_T *newsign; 5422 5423 newsign = (signlist_T *)lalloc((long_u)sizeof(signlist_T), FALSE); 5424 if (newsign != NULL) 5425 { 5426 newsign->id = id; 5427 newsign->lnum = lnum; 5428 newsign->typenr = typenr; 5429 newsign->next = next; 5430 #ifdef FEAT_NETBEANS_INTG 5431 newsign->prev = prev; 5432 if (next != NULL) 5433 next->prev = newsign; 5434 #endif 5435 5436 if (prev == NULL) 5437 { 5438 /* When adding first sign need to redraw the windows to create the 5439 * column for signs. */ 5440 if (buf->b_signlist == NULL) 5441 { 5442 redraw_buf_later(buf, NOT_VALID); 5443 changed_cline_bef_curs(); 5444 } 5445 5446 /* first sign in signlist */ 5447 buf->b_signlist = newsign; 5448 } 5449 else 5450 prev->next = newsign; 5451 } 5452 } 5453 5454 /* 5455 * Add the sign into the signlist. Find the right spot to do it though. 5456 */ 5457 void 5458 buf_addsign(buf, id, lnum, typenr) 5459 buf_T *buf; /* buffer to store sign in */ 5460 int id; /* sign ID */ 5461 linenr_T lnum; /* line number which gets the mark */ 5462 int typenr; /* typenr of sign we are adding */ 5463 { 5464 signlist_T *sign; /* a sign in the signlist */ 5465 signlist_T *prev; /* the previous sign */ 5466 5467 prev = NULL; 5468 for (sign = buf->b_signlist; sign != NULL; sign = sign->next) 5469 { 5470 if (lnum == sign->lnum && id == sign->id) 5471 { 5472 sign->typenr = typenr; 5473 return; 5474 } 5475 else if ( 5476 #ifndef FEAT_NETBEANS_INTG /* keep signs sorted by lnum */ 5477 id < 0 && 5478 #endif 5479 lnum < sign->lnum) 5480 { 5481 #ifdef FEAT_NETBEANS_INTG /* insert new sign at head of list for this lnum */ 5482 /* XXX - GRP: Is this because of sign slide problem? Or is it 5483 * really needed? Or is it because we allow multiple signs per 5484 * line? If so, should I add that feature to FEAT_SIGNS? 5485 */ 5486 while (prev != NULL && prev->lnum == lnum) 5487 prev = prev->prev; 5488 if (prev == NULL) 5489 sign = buf->b_signlist; 5490 else 5491 sign = prev->next; 5492 #endif 5493 insert_sign(buf, prev, sign, id, lnum, typenr); 5494 return; 5495 } 5496 prev = sign; 5497 } 5498 #ifdef FEAT_NETBEANS_INTG /* insert new sign at head of list for this lnum */ 5499 /* XXX - GRP: See previous comment */ 5500 while (prev != NULL && prev->lnum == lnum) 5501 prev = prev->prev; 5502 if (prev == NULL) 5503 sign = buf->b_signlist; 5504 else 5505 sign = prev->next; 5506 #endif 5507 insert_sign(buf, prev, sign, id, lnum, typenr); 5508 5509 return; 5510 } 5511 5512 linenr_T 5513 buf_change_sign_type(buf, markId, typenr) 5514 buf_T *buf; /* buffer to store sign in */ 5515 int markId; /* sign ID */ 5516 int typenr; /* typenr of sign we are adding */ 5517 { 5518 signlist_T *sign; /* a sign in the signlist */ 5519 5520 for (sign = buf->b_signlist; sign != NULL; sign = sign->next) 5521 { 5522 if (sign->id == markId) 5523 { 5524 sign->typenr = typenr; 5525 return sign->lnum; 5526 } 5527 } 5528 5529 return (linenr_T)0; 5530 } 5531 5532 int 5533 buf_getsigntype(buf, lnum, type) 5534 buf_T *buf; 5535 linenr_T lnum; 5536 int type; /* SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL */ 5537 { 5538 signlist_T *sign; /* a sign in a b_signlist */ 5539 5540 for (sign = buf->b_signlist; sign != NULL; sign = sign->next) 5541 if (sign->lnum == lnum 5542 && (type == SIGN_ANY 5543 # ifdef FEAT_SIGN_ICONS 5544 || (type == SIGN_ICON 5545 && sign_get_image(sign->typenr) != NULL) 5546 # endif 5547 || (type == SIGN_TEXT 5548 && sign_get_text(sign->typenr) != NULL) 5549 || (type == SIGN_LINEHL 5550 && sign_get_attr(sign->typenr, TRUE) != 0))) 5551 return sign->typenr; 5552 return 0; 5553 } 5554 5555 5556 linenr_T 5557 buf_delsign(buf, id) 5558 buf_T *buf; /* buffer sign is stored in */ 5559 int id; /* sign id */ 5560 { 5561 signlist_T **lastp; /* pointer to pointer to current sign */ 5562 signlist_T *sign; /* a sign in a b_signlist */ 5563 signlist_T *next; /* the next sign in a b_signlist */ 5564 linenr_T lnum; /* line number whose sign was deleted */ 5565 5566 lastp = &buf->b_signlist; 5567 lnum = 0; 5568 for (sign = buf->b_signlist; sign != NULL; sign = next) 5569 { 5570 next = sign->next; 5571 if (sign->id == id) 5572 { 5573 *lastp = next; 5574 #ifdef FEAT_NETBEANS_INTG 5575 if (next != NULL) 5576 next->prev = sign->prev; 5577 #endif 5578 lnum = sign->lnum; 5579 vim_free(sign); 5580 break; 5581 } 5582 else 5583 lastp = &sign->next; 5584 } 5585 5586 /* When deleted the last sign need to redraw the windows to remove the 5587 * sign column. */ 5588 if (buf->b_signlist == NULL) 5589 { 5590 redraw_buf_later(buf, NOT_VALID); 5591 changed_cline_bef_curs(); 5592 } 5593 5594 return lnum; 5595 } 5596 5597 5598 /* 5599 * Find the line number of the sign with the requested id. If the sign does 5600 * not exist, return 0 as the line number. This will still let the correct file 5601 * get loaded. 5602 */ 5603 int 5604 buf_findsign(buf, id) 5605 buf_T *buf; /* buffer to store sign in */ 5606 int id; /* sign ID */ 5607 { 5608 signlist_T *sign; /* a sign in the signlist */ 5609 5610 for (sign = buf->b_signlist; sign != NULL; sign = sign->next) 5611 if (sign->id == id) 5612 return sign->lnum; 5613 5614 return 0; 5615 } 5616 5617 int 5618 buf_findsign_id(buf, lnum) 5619 buf_T *buf; /* buffer whose sign we are searching for */ 5620 linenr_T lnum; /* line number of sign */ 5621 { 5622 signlist_T *sign; /* a sign in the signlist */ 5623 5624 for (sign = buf->b_signlist; sign != NULL; sign = sign->next) 5625 if (sign->lnum == lnum) 5626 return sign->id; 5627 5628 return 0; 5629 } 5630 5631 5632 # if defined(FEAT_NETBEANS_INTG) || defined(PROTO) 5633 /* see if a given type of sign exists on a specific line */ 5634 int 5635 buf_findsigntype_id(buf, lnum, typenr) 5636 buf_T *buf; /* buffer whose sign we are searching for */ 5637 linenr_T lnum; /* line number of sign */ 5638 int typenr; /* sign type number */ 5639 { 5640 signlist_T *sign; /* a sign in the signlist */ 5641 5642 for (sign = buf->b_signlist; sign != NULL; sign = sign->next) 5643 if (sign->lnum == lnum && sign->typenr == typenr) 5644 return sign->id; 5645 5646 return 0; 5647 } 5648 5649 5650 # if defined(FEAT_SIGN_ICONS) || defined(PROTO) 5651 /* return the number of icons on the given line */ 5652 int 5653 buf_signcount(buf, lnum) 5654 buf_T *buf; 5655 linenr_T lnum; 5656 { 5657 signlist_T *sign; /* a sign in the signlist */ 5658 int count = 0; 5659 5660 for (sign = buf->b_signlist; sign != NULL; sign = sign->next) 5661 if (sign->lnum == lnum) 5662 if (sign_get_image(sign->typenr) != NULL) 5663 count++; 5664 5665 return count; 5666 } 5667 # endif /* FEAT_SIGN_ICONS */ 5668 # endif /* FEAT_NETBEANS_INTG */ 5669 5670 5671 /* 5672 * Delete signs in buffer "buf". 5673 */ 5674 void 5675 buf_delete_signs(buf) 5676 buf_T *buf; 5677 { 5678 signlist_T *next; 5679 5680 while (buf->b_signlist != NULL) 5681 { 5682 next = buf->b_signlist->next; 5683 vim_free(buf->b_signlist); 5684 buf->b_signlist = next; 5685 } 5686 } 5687 5688 /* 5689 * Delete all signs in all buffers. 5690 */ 5691 void 5692 buf_delete_all_signs() 5693 { 5694 buf_T *buf; /* buffer we are checking for signs */ 5695 5696 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 5697 if (buf->b_signlist != NULL) 5698 { 5699 /* Need to redraw the windows to remove the sign column. */ 5700 redraw_buf_later(buf, NOT_VALID); 5701 buf_delete_signs(buf); 5702 } 5703 } 5704 5705 /* 5706 * List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. 5707 */ 5708 void 5709 sign_list_placed(rbuf) 5710 buf_T *rbuf; 5711 { 5712 buf_T *buf; 5713 signlist_T *p; 5714 char lbuf[BUFSIZ]; 5715 5716 MSG_PUTS_TITLE(_("\n--- Signs ---")); 5717 msg_putchar('\n'); 5718 if (rbuf == NULL) 5719 buf = firstbuf; 5720 else 5721 buf = rbuf; 5722 while (buf != NULL && !got_int) 5723 { 5724 if (buf->b_signlist != NULL) 5725 { 5726 vim_snprintf(lbuf, BUFSIZ, _("Signs for %s:"), buf->b_fname); 5727 MSG_PUTS_ATTR(lbuf, hl_attr(HLF_D)); 5728 msg_putchar('\n'); 5729 } 5730 for (p = buf->b_signlist; p != NULL && !got_int; p = p->next) 5731 { 5732 vim_snprintf(lbuf, BUFSIZ, _(" line=%ld id=%d name=%s"), 5733 (long)p->lnum, p->id, sign_typenr2name(p->typenr)); 5734 MSG_PUTS(lbuf); 5735 msg_putchar('\n'); 5736 } 5737 if (rbuf != NULL) 5738 break; 5739 buf = buf->b_next; 5740 } 5741 } 5742 5743 /* 5744 * Adjust a placed sign for inserted/deleted lines. 5745 */ 5746 void 5747 sign_mark_adjust(line1, line2, amount, amount_after) 5748 linenr_T line1; 5749 linenr_T line2; 5750 long amount; 5751 long amount_after; 5752 { 5753 signlist_T *sign; /* a sign in a b_signlist */ 5754 5755 for (sign = curbuf->b_signlist; sign != NULL; sign = sign->next) 5756 { 5757 if (sign->lnum >= line1 && sign->lnum <= line2) 5758 { 5759 if (amount == MAXLNUM) 5760 sign->lnum = line1; 5761 else 5762 sign->lnum += amount; 5763 } 5764 else if (sign->lnum > line2) 5765 sign->lnum += amount_after; 5766 } 5767 } 5768 #endif /* FEAT_SIGNS */ 5769 5770 /* 5771 * Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed. 5772 */ 5773 void 5774 set_buflisted(on) 5775 int on; 5776 { 5777 if (on != curbuf->b_p_bl) 5778 { 5779 curbuf->b_p_bl = on; 5780 #ifdef FEAT_AUTOCMD 5781 if (on) 5782 apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, curbuf); 5783 else 5784 apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); 5785 #endif 5786 } 5787 } 5788 5789 /* 5790 * Read the file for "buf" again and check if the contents changed. 5791 * Return TRUE if it changed or this could not be checked. 5792 */ 5793 int 5794 buf_contents_changed(buf) 5795 buf_T *buf; 5796 { 5797 buf_T *newbuf; 5798 int differ = TRUE; 5799 linenr_T lnum; 5800 aco_save_T aco; 5801 exarg_T ea; 5802 5803 /* Allocate a buffer without putting it in the buffer list. */ 5804 newbuf = buflist_new(NULL, NULL, (linenr_T)1, BLN_DUMMY); 5805 if (newbuf == NULL) 5806 return TRUE; 5807 5808 /* Force the 'fileencoding' and 'fileformat' to be equal. */ 5809 if (prep_exarg(&ea, buf) == FAIL) 5810 { 5811 wipe_buffer(newbuf, FALSE); 5812 return TRUE; 5813 } 5814 5815 /* set curwin/curbuf to buf and save a few things */ 5816 aucmd_prepbuf(&aco, newbuf); 5817 5818 if (ml_open(curbuf) == OK 5819 && readfile(buf->b_ffname, buf->b_fname, 5820 (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, 5821 &ea, READ_NEW | READ_DUMMY) == OK) 5822 { 5823 /* compare the two files line by line */ 5824 if (buf->b_ml.ml_line_count == curbuf->b_ml.ml_line_count) 5825 { 5826 differ = FALSE; 5827 for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) 5828 if (STRCMP(ml_get_buf(buf, lnum, FALSE), ml_get(lnum)) != 0) 5829 { 5830 differ = TRUE; 5831 break; 5832 } 5833 } 5834 } 5835 vim_free(ea.cmd); 5836 5837 /* restore curwin/curbuf and a few other things */ 5838 aucmd_restbuf(&aco); 5839 5840 if (curbuf != newbuf) /* safety check */ 5841 wipe_buffer(newbuf, FALSE); 5842 5843 return differ; 5844 } 5845 5846 /* 5847 * Wipe out a buffer and decrement the last buffer number if it was used for 5848 * this buffer. Call this to wipe out a temp buffer that does not contain any 5849 * marks. 5850 */ 5851 void 5852 wipe_buffer(buf, aucmd) 5853 buf_T *buf; 5854 int aucmd UNUSED; /* When TRUE trigger autocommands. */ 5855 { 5856 if (buf->b_fnum == top_file_num - 1) 5857 --top_file_num; 5858 5859 #ifdef FEAT_AUTOCMD 5860 if (!aucmd) /* Don't trigger BufDelete autocommands here. */ 5861 block_autocmds(); 5862 #endif 5863 close_buffer(NULL, buf, DOBUF_WIPE, FALSE); 5864 #ifdef FEAT_AUTOCMD 5865 if (!aucmd) 5866 unblock_autocmds(); 5867 #endif 5868 } 5869