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