From da51dc9cf202772f60bd2da975dbef257bd9237c Mon Sep 17 00:00:00 2001 From: Eliseo Martínez Date: Mon, 12 May 2014 02:25:17 +0200 Subject: Introduce nvim namespace: Move files. Move files from src/ to src/nvim/. - src/nvim/ becomes the new root dir for nvim executable sources. - src/libnvim/ is planned to become root dir of the neovim library. --- src/buffer.c | 4657 ---------------------------------------------------------- 1 file changed, 4657 deletions(-) delete mode 100644 src/buffer.c (limited to 'src/buffer.c') diff --git a/src/buffer.c b/src/buffer.c deleted file mode 100644 index 7cd773b55c..0000000000 --- a/src/buffer.c +++ /dev/null @@ -1,4657 +0,0 @@ -/* - * VIM - Vi IMproved by Bram Moolenaar - * - * Do ":help uganda" in Vim to read copying and usage conditions. - * Do ":help credits" in Vim to see a list of people who contributed. - * See README.txt for an overview of the Vim source code. - */ - -/* - * buffer.c: functions for dealing with the buffer structure - */ - -/* - * The buffer list is a double linked list of all buffers. - * Each buffer can be in one of these states: - * never loaded: BF_NEVERLOADED is set, only the file name is valid - * not loaded: b_ml.ml_mfp == NULL, no memfile allocated - * hidden: b_nwindows == 0, loaded but not displayed in a window - * normal: loaded and displayed in a window - * - * Instead of storing file names all over the place, each file name is - * stored in the buffer list. It can be referenced by a number. - * - * The current implementation remembers all file names ever used. - */ - -#include - -#include "vim.h" -#include "buffer.h" -#include "charset.h" -#include "diff.h" -#include "digraph.h" -#include "eval.h" -#include "ex_cmds2.h" -#include "ex_cmds.h" -#include "ex_docmd.h" -#include "ex_eval.h" -#include "ex_getln.h" -#include "fileio.h" -#include "fold.h" -#include "getchar.h" -#include "hashtab.h" -#include "indent.h" -#include "indent_c.h" -#include "main.h" -#include "mark.h" -#include "mbyte.h" -#include "memline.h" -#include "memory.h" -#include "message.h" -#include "misc1.h" -#include "misc2.h" -#include "garray.h" -#include "move.h" -#include "option.h" -#include "os_unix.h" -#include "path.h" -#include "quickfix.h" -#include "regexp.h" -#include "screen.h" -#include "spell.h" -#include "syntax.h" -#include "term.h" -#include "ui.h" -#include "undo.h" -#include "window.h" -#include "os/os.h" - -// Todo(stefan991): remove this macro -#define INVALID_DEVICE_ID UINT64_MAX - -static char_u *buflist_match(regprog_T *prog, buf_T *buf); -# define HAVE_BUFLIST_MATCH -static char_u *fname_match(regprog_T *prog, char_u *name); -static void buflist_setfpos(buf_T *buf, win_T *win, linenr_T lnum, - colnr_T col, int copy_options); -static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer); -static buf_T *buflist_findname_file_info(char_u *ffname, FileInfo *file_info); -static int otherfile_buf(buf_T *buf, char_u *ffname, FileInfo *file_info); -static int buf_same_ino(buf_T *buf, FileInfo *file_info); -static int ti_change(char_u *str, char_u **last); -static int append_arg_number(win_T *wp, char_u *buf, int buflen, int add_file); -static void free_buffer(buf_T *); -static void free_buffer_stuff(buf_T *buf, int free_options); -static void clear_wininfo(buf_T *buf); - -static void insert_sign(buf_T *buf, signlist_T *prev, signlist_T *next, int id, linenr_T lnum, int typenr); - -static char *msg_loclist = N_("[Location List]"); -static char *msg_qflist = N_("[Quickfix List]"); -static char *e_auabort = N_("E855: Autocommands caused command to abort"); - -/* - * Open current buffer, that is: open the memfile and read the file into - * memory. - * Return FAIL for failure, OK otherwise. - */ -int -open_buffer ( - int read_stdin, /* read file from stdin */ - exarg_T *eap, /* for forced 'ff' and 'fenc' or NULL */ - int flags /* extra flags for readfile() */ -) -{ - int retval = OK; - buf_T *old_curbuf; - long old_tw = curbuf->b_p_tw; - - /* - * The 'readonly' flag is only set when BF_NEVERLOADED is being reset. - * When re-entering the same buffer, it should not change, because the - * user may have reset the flag by hand. - */ - if (readonlymode && curbuf->b_ffname != NULL - && (curbuf->b_flags & BF_NEVERLOADED)) - curbuf->b_p_ro = TRUE; - - if (ml_open(curbuf) == FAIL) { - /* - * There MUST be a memfile, otherwise we can't do anything - * If we can't create one for the current buffer, take another buffer - */ - close_buffer(NULL, curbuf, 0, FALSE); - for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next) - if (curbuf->b_ml.ml_mfp != NULL) - break; - /* - * if there is no memfile at all, exit - * This is OK, since there are no changes to lose. - */ - if (curbuf == NULL) { - EMSG(_("E82: Cannot allocate any buffer, exiting...")); - getout(2); - } - EMSG(_("E83: Cannot allocate buffer, using other one...")); - enter_buffer(curbuf); - if (old_tw != curbuf->b_p_tw) - check_colorcolumn(curwin); - return FAIL; - } - - /* The autocommands in readfile() may change the buffer, but only AFTER - * reading the file. */ - old_curbuf = curbuf; - modified_was_set = FALSE; - - /* mark cursor position as being invalid */ - curwin->w_valid = 0; - - if (curbuf->b_ffname != NULL - ) { - retval = readfile(curbuf->b_ffname, curbuf->b_fname, - (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, - flags | READ_NEW); - /* Help buffer is filtered. */ - if (curbuf->b_help) - fix_help_buffer(); - } else if (read_stdin) { - int save_bin = curbuf->b_p_bin; - linenr_T line_count; - - /* - * First read the text in binary mode into the buffer. - * Then read from that same buffer and append at the end. This makes - * it possible to retry when 'fileformat' or 'fileencoding' was - * guessed wrong. - */ - curbuf->b_p_bin = TRUE; - retval = readfile(NULL, NULL, (linenr_T)0, - (linenr_T)0, (linenr_T)MAXLNUM, NULL, - flags | (READ_NEW + READ_STDIN)); - curbuf->b_p_bin = save_bin; - if (retval == OK) { - line_count = curbuf->b_ml.ml_line_count; - retval = readfile(NULL, NULL, (linenr_T)line_count, - (linenr_T)0, (linenr_T)MAXLNUM, eap, - flags | READ_BUFFER); - if (retval == OK) { - /* Delete the binary lines. */ - while (--line_count >= 0) - ml_delete((linenr_T)1, FALSE); - } else { - /* Delete the converted lines. */ - while (curbuf->b_ml.ml_line_count > line_count) - ml_delete(line_count, FALSE); - } - /* Put the cursor on the first line. */ - curwin->w_cursor.lnum = 1; - curwin->w_cursor.col = 0; - - /* Set or reset 'modified' before executing autocommands, so that - * it can be changed there. */ - if (!readonlymode && !bufempty()) - changed(); - else if (retval != FAIL) - unchanged(curbuf, FALSE); - apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE, - curbuf, &retval); - } - } - - /* if first time loading this buffer, init b_chartab[] */ - if (curbuf->b_flags & BF_NEVERLOADED) { - (void)buf_init_chartab(curbuf, FALSE); - parse_cino(curbuf); - } - - /* - * Set/reset the Changed flag first, autocmds may change the buffer. - * Apply the automatic commands, before processing the modelines. - * So the modelines have priority over auto commands. - */ - /* When reading stdin, the buffer contents always needs writing, so set - * the changed flag. Unless in readonly mode: "ls | gview -". - * When interrupted and 'cpoptions' contains 'i' set changed flag. */ - if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - || modified_was_set /* ":set modified" used in autocmd */ - || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - ) - changed(); - else if (retval != FAIL && !read_stdin) - unchanged(curbuf, FALSE); - save_file_ff(curbuf); /* keep this fileformat */ - - /* require "!" to overwrite the file, because it wasn't read completely */ - if (aborting()) - curbuf->b_flags |= BF_READERR; - - /* Need to update automatic folding. Do this before the autocommands, - * they may use the fold info. */ - foldUpdateAll(curwin); - - /* need to set w_topline, unless some autocommand already did that. */ - if (!(curwin->w_valid & VALID_TOPLINE)) { - curwin->w_topline = 1; - curwin->w_topfill = 0; - } - apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval); - - if (retval != FAIL) { - /* - * The autocommands may have changed the current buffer. Apply the - * modelines to the correct buffer, if it still exists and is loaded. - */ - if (buf_valid(old_curbuf) && old_curbuf->b_ml.ml_mfp != NULL) { - aco_save_T aco; - - /* Go to the buffer that was opened. */ - aucmd_prepbuf(&aco, old_curbuf); - do_modelines(0); - curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED); - - apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf, - &retval); - - /* restore curwin/curbuf and a few other things */ - aucmd_restbuf(&aco); - } - } - - return retval; -} - -/* - * Return TRUE if "buf" points to a valid buffer (in the buffer list). - */ -int buf_valid(buf_T *buf) -{ - buf_T *bp; - - for (bp = firstbuf; bp != NULL; bp = bp->b_next) - if (bp == buf) - return TRUE; - return FALSE; -} - -/* - * Close the link to a buffer. - * "action" is used when there is no longer a window for the buffer. - * It can be: - * 0 buffer becomes hidden - * DOBUF_UNLOAD buffer is unloaded - * DOBUF_DELETE buffer is unloaded and removed from buffer list - * DOBUF_WIPE buffer is unloaded and really deleted - * When doing all but the first one on the current buffer, the caller should - * get a new buffer very soon! - * - * The 'bufhidden' option can force freeing and deleting. - * - * When "abort_if_last" is TRUE then do not close the buffer if autocommands - * cause there to be only one window with this buffer. e.g. when ":quit" is - * supposed to close the window but autocommands close all other windows. - */ -void -close_buffer ( - win_T *win, /* if not NULL, set b_last_cursor */ - buf_T *buf, - int action, - int abort_if_last -) -{ - int is_curbuf; - int nwindows; - int unload_buf = (action != 0); - int del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE); - int wipe_buf = (action == DOBUF_WIPE); - - /* - * Force unloading or deleting when 'bufhidden' says so. - * The caller must take care of NOT deleting/freeing when 'bufhidden' is - * "hide" (otherwise we could never free or delete a buffer). - */ - if (buf->b_p_bh[0] == 'd') { /* 'bufhidden' == "delete" */ - del_buf = TRUE; - unload_buf = TRUE; - } else if (buf->b_p_bh[0] == 'w') { /* 'bufhidden' == "wipe" */ - del_buf = TRUE; - unload_buf = TRUE; - wipe_buf = TRUE; - } else if (buf->b_p_bh[0] == 'u') /* 'bufhidden' == "unload" */ - unload_buf = TRUE; - - if (win != NULL) { - /* Set b_last_cursor when closing the last window for the buffer. - * Remember the last cursor position and window options of the buffer. - * This used to be only for the current window, but then options like - * 'foldmethod' may be lost with a ":only" command. */ - if (buf->b_nwindows == 1) - set_last_cursor(win); - buflist_setfpos(buf, win, - win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum, - win->w_cursor.col, TRUE); - } - - /* When the buffer is no longer in a window, trigger BufWinLeave */ - if (buf->b_nwindows == 1) { - buf->b_closing = TRUE; - apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) { - /* Autocommands deleted the buffer. */ -aucmd_abort: - EMSG(_(e_auabort)); - return; - } - buf->b_closing = FALSE; - if (abort_if_last && one_window()) - /* Autocommands made this the only window. */ - goto aucmd_abort; - - /* When the buffer becomes hidden, but is not unloaded, trigger - * BufHidden */ - if (!unload_buf) { - buf->b_closing = TRUE; - apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) - /* Autocommands deleted the buffer. */ - goto aucmd_abort; - buf->b_closing = FALSE; - if (abort_if_last && one_window()) - /* Autocommands made this the only window. */ - goto aucmd_abort; - } - if (aborting()) /* autocmds may abort script processing */ - return; - } - nwindows = buf->b_nwindows; - - /* decrease the link count from windows (unless not in any window) */ - if (buf->b_nwindows > 0) - --buf->b_nwindows; - - /* Return when a window is displaying the buffer or when it's not - * unloaded. */ - if (buf->b_nwindows > 0 || !unload_buf) - return; - - /* Always remove the buffer when there is no file name. */ - if (buf->b_ffname == NULL) - del_buf = TRUE; - - /* - * Free all things allocated for this buffer. - * Also calls the "BufDelete" autocommands when del_buf is TRUE. - */ - /* Remember if we are closing the current buffer. Restore the number of - * windows, so that autocommands in buf_freeall() don't get confused. */ - is_curbuf = (buf == curbuf); - buf->b_nwindows = nwindows; - - buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0)); - if ( - win_valid(win) && - win->w_buffer == buf) - win->w_buffer = NULL; /* make sure we don't use the buffer now */ - - /* Autocommands may have deleted the buffer. */ - if (!buf_valid(buf)) - return; - if (aborting()) /* autocmds may abort script processing */ - return; - - /* Autocommands may have opened or closed windows for this buffer. - * Decrement the count for the close we do here. */ - if (buf->b_nwindows > 0) - --buf->b_nwindows; - - /* - * It's possible that autocommands change curbuf to the one being deleted. - * This might cause the previous curbuf to be deleted unexpectedly. But - * in some cases it's OK to delete the curbuf, because a new one is - * obtained anyway. Therefore only return if curbuf changed to the - * deleted buffer. - */ - if (buf == curbuf && !is_curbuf) - return; - - /* Change directories when the 'acd' option is set. */ - do_autochdir(); - - /* - * Remove the buffer from the list. - */ - if (wipe_buf) { - free(buf->b_ffname); - free(buf->b_sfname); - if (buf->b_prev == NULL) - firstbuf = buf->b_next; - else - buf->b_prev->b_next = buf->b_next; - if (buf->b_next == NULL) - lastbuf = buf->b_prev; - else - buf->b_next->b_prev = buf->b_prev; - free_buffer(buf); - } else { - if (del_buf) { - /* Free all internal variables and reset option values, to make - * ":bdel" compatible with Vim 5.7. */ - free_buffer_stuff(buf, TRUE); - - /* Make it look like a new buffer. */ - buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; - - /* Init the options when loaded again. */ - buf->b_p_initialized = FALSE; - } - buf_clear_file(buf); - if (del_buf) - buf->b_p_bl = FALSE; - } -} - -/* - * Make buffer not contain a file. - */ -void buf_clear_file(buf_T *buf) -{ - buf->b_ml.ml_line_count = 1; - unchanged(buf, TRUE); - buf->b_p_eol = TRUE; - buf->b_start_eol = TRUE; - buf->b_p_bomb = FALSE; - buf->b_start_bomb = FALSE; - buf->b_ml.ml_mfp = NULL; - buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */ -} - -/* - * buf_freeall() - free all things allocated for a buffer that are related to - * the file. flags: - * BFA_DEL buffer is going to be deleted - * BFA_WIPE buffer is going to be wiped out - * BFA_KEEP_UNDO do not free undo information - */ -void buf_freeall(buf_T *buf, int flags) -{ - int is_curbuf = (buf == curbuf); - - buf->b_closing = TRUE; - apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ - return; - if ((flags & BFA_DEL) && buf->b_p_bl) { - apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ - return; - } - if (flags & BFA_WIPE) { - apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname, - FALSE, buf); - if (!buf_valid(buf)) /* autocommands may delete the buffer */ - return; - } - buf->b_closing = FALSE; - if (aborting()) /* autocmds may abort script processing */ - return; - - /* - * It's possible that autocommands change curbuf to the one being deleted. - * This might cause curbuf to be deleted unexpectedly. But in some cases - * it's OK to delete the curbuf, because a new one is obtained anyway. - * Therefore only return if curbuf changed to the deleted buffer. - */ - if (buf == curbuf && !is_curbuf) - return; - diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */ - /* Remove any ownsyntax, unless exiting. */ - if (firstwin != NULL && curwin->w_buffer == buf) - reset_synblock(curwin); - - /* No folds in an empty buffer. */ - { - win_T *win; - tabpage_T *tp; - - FOR_ALL_TAB_WINDOWS(tp, win) - if (win->w_buffer == buf) - clearFolding(win); - } - - ml_close(buf, TRUE); /* close and delete the memline/memfile */ - buf->b_ml.ml_line_count = 0; /* no lines in buffer */ - if ((flags & BFA_KEEP_UNDO) == 0) { - u_blockfree(buf); /* free the memory allocated for undo */ - u_clearall(buf); /* reset all undo information */ - } - syntax_clear(&buf->b_s); /* reset syntax info */ - buf->b_flags &= ~BF_READERR; /* a read error is no longer relevant */ -} - -/* - * Free a buffer structure and the things it contains related to the buffer - * itself (not the file, that must have been done already). - */ -static void free_buffer(buf_T *buf) -{ - free_buffer_stuff(buf, TRUE); - unref_var_dict(buf->b_vars); - aubuflocal_remove(buf); - if (autocmd_busy) { - // Do not free the buffer structure while autocommands are executing, - // it's still needed. Free it when autocmd_busy is reset. - buf->b_next = au_pending_free_buf; - au_pending_free_buf = buf; - } else { - free(buf); - } -} - -/* - * Free stuff in the buffer for ":bdel" and when wiping out the buffer. - */ -static void -free_buffer_stuff ( - buf_T *buf, - int free_options /* free options as well */ -) -{ - if (free_options) { - clear_wininfo(buf); /* including window-local options */ - free_buf_options(buf, TRUE); - ga_clear(&buf->b_s.b_langp); - } - vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */ - hash_init(&buf->b_vars->dv_hashtab); - uc_clear(&buf->b_ucmds); /* clear local user commands */ - buf_delete_signs(buf); /* delete any signs */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */ - map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */ - free(buf->b_start_fenc); - buf->b_start_fenc = NULL; -} - -/* - * Free the b_wininfo list for buffer "buf". - */ -static void clear_wininfo(buf_T *buf) -{ - wininfo_T *wip; - - while (buf->b_wininfo != NULL) { - wip = buf->b_wininfo; - buf->b_wininfo = wip->wi_next; - if (wip->wi_optset) { - clear_winopt(&wip->wi_opt); - deleteFoldRecurse(&wip->wi_folds); - } - free(wip); - } -} - -/* - * Go to another buffer. Handles the result of the ATTENTION dialog. - */ -void goto_buffer(exarg_T *eap, int start, int dir, int count) -{ -# if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION) - buf_T *old_curbuf = curbuf; - - swap_exists_action = SEA_DIALOG; -# endif - (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO, - start, dir, count, eap->forceit); -# if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION) - if (swap_exists_action == SEA_QUIT && *eap->cmd == 's') { - cleanup_T cs; - - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a window. */ - enter_cleanup(&cs); - - /* Quitting means closing the split window, nothing else. */ - win_close(curwin, TRUE); - swap_exists_action = SEA_NONE; - swap_exists_did_quit = TRUE; - - /* Restore the error/interrupt/exception state if not discarded by a - * new aborting error, interrupt, or uncaught exception. */ - leave_cleanup(&cs); - } else - handle_swap_exists(old_curbuf); -# endif -} - -#if defined(HAS_SWAP_EXISTS_ACTION) || defined(PROTO) -/* - * Handle the situation of swap_exists_action being set. - * It is allowed for "old_curbuf" to be NULL or invalid. - */ -void handle_swap_exists(buf_T *old_curbuf) -{ - cleanup_T cs; - long old_tw = curbuf->b_p_tw; - - if (swap_exists_action == SEA_QUIT) { - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a buffer. */ - enter_cleanup(&cs); - - /* User selected Quit at ATTENTION prompt. Go back to previous - * buffer. If that buffer is gone or the same as the current one, - * open a new, empty buffer. */ - swap_exists_action = SEA_NONE; /* don't want it again */ - swap_exists_did_quit = TRUE; - close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE); - if (!buf_valid(old_curbuf) || old_curbuf == curbuf) - old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED); - if (old_curbuf != NULL) { - enter_buffer(old_curbuf); - if (old_tw != curbuf->b_p_tw) - check_colorcolumn(curwin); - } - /* If "old_curbuf" is NULL we are in big trouble here... */ - - /* Restore the error/interrupt/exception state if not discarded by a - * new aborting error, interrupt, or uncaught exception. */ - leave_cleanup(&cs); - } else if (swap_exists_action == SEA_RECOVER) { - /* Reset the error/interrupt/exception state here so that - * aborting() returns FALSE when closing a buffer. */ - enter_cleanup(&cs); - - /* User selected Recover at ATTENTION prompt. */ - msg_scroll = TRUE; - ml_recover(); - MSG_PUTS("\n"); /* don't overwrite the last message */ - cmdline_row = msg_row; - do_modelines(0); - - /* Restore the error/interrupt/exception state if not discarded by a - * new aborting error, interrupt, or uncaught exception. */ - leave_cleanup(&cs); - } - swap_exists_action = SEA_NONE; -} -#endif - -/* - * do_bufdel() - delete or unload buffer(s) - * - * addr_count == 0: ":bdel" - delete current buffer - * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete - * buffer "end_bnr", then any other arguments. - * addr_count == 2: ":N,N bdel" - delete buffers in range - * - * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or - * DOBUF_DEL (":bdel") - * - * Returns error message or NULL - */ -char_u * -do_bufdel ( - int command, - char_u *arg, /* pointer to extra arguments */ - int addr_count, - int start_bnr, /* first buffer number in a range */ - int end_bnr, /* buffer nr or last buffer nr in a range */ - int forceit -) -{ - int do_current = 0; /* delete current buffer? */ - int deleted = 0; /* number of buffers deleted */ - char_u *errormsg = NULL; /* return value */ - int bnr; /* buffer number */ - char_u *p; - - if (addr_count == 0) { - (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit); - } else { - if (addr_count == 2) { - if (*arg) /* both range and argument is not allowed */ - return (char_u *)_(e_trailing); - bnr = start_bnr; - } else /* addr_count == 1 */ - bnr = end_bnr; - - for (; !got_int; ui_breakcheck()) { - /* - * delete the current buffer last, otherwise when the - * current buffer is deleted, the next buffer becomes - * the current one and will be loaded, which may then - * also be deleted, etc. - */ - if (bnr == curbuf->b_fnum) - do_current = bnr; - else if (do_buffer(command, DOBUF_FIRST, FORWARD, (int)bnr, - forceit) == OK) - ++deleted; - - /* - * find next buffer number to delete/unload - */ - if (addr_count == 2) { - if (++bnr > end_bnr) - break; - } else { /* addr_count == 1 */ - arg = skipwhite(arg); - if (*arg == NUL) - break; - if (!VIM_ISDIGIT(*arg)) { - p = skiptowhite_esc(arg); - bnr = buflist_findpat(arg, p, command == DOBUF_WIPE, - FALSE, FALSE); - if (bnr < 0) /* failed */ - break; - arg = p; - } else - bnr = getdigits(&arg); - } - } - if (!got_int && do_current && do_buffer(command, DOBUF_FIRST, - FORWARD, do_current, forceit) == OK) - ++deleted; - - if (deleted == 0) { - if (command == DOBUF_UNLOAD) - STRCPY(IObuff, _("E515: No buffers were unloaded")); - else if (command == DOBUF_DEL) - STRCPY(IObuff, _("E516: No buffers were deleted")); - else - STRCPY(IObuff, _("E517: No buffers were wiped out")); - errormsg = IObuff; - } else if (deleted >= p_report) { - if (command == DOBUF_UNLOAD) { - if (deleted == 1) - MSG(_("1 buffer unloaded")); - else - smsg((char_u *)_("%d buffers unloaded"), deleted); - } else if (command == DOBUF_DEL) { - if (deleted == 1) - MSG(_("1 buffer deleted")); - else - smsg((char_u *)_("%d buffers deleted"), deleted); - } else { - if (deleted == 1) - MSG(_("1 buffer wiped out")); - else - smsg((char_u *)_("%d buffers wiped out"), deleted); - } - } - } - - - return errormsg; -} - - -static int empty_curbuf(int close_others, int forceit, int action); - -/* - * Make the current buffer empty. - * Used when it is wiped out and it's the last buffer. - */ -static int empty_curbuf(int close_others, int forceit, int action) -{ - int retval; - buf_T *buf = curbuf; - - if (action == DOBUF_UNLOAD) { - EMSG(_("E90: Cannot unload last buffer")); - return FAIL; - } - - if (close_others) { - /* Close any other windows on this buffer, then make it empty. */ - close_windows(buf, TRUE); - } - - setpcmark(); - retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, - forceit ? ECMD_FORCEIT : 0, curwin); - - /* - * do_ecmd() may create a new buffer, then we have to delete - * the old one. But do_ecmd() may have done that already, check - * if the buffer still exists. - */ - if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0) - close_buffer(NULL, buf, action, FALSE); - if (!close_others) - need_fileinfo = FALSE; - return retval; -} -/* - * Implementation of the commands for the buffer list. - * - * action == DOBUF_GOTO go to specified buffer - * action == DOBUF_SPLIT split window and go to specified buffer - * action == DOBUF_UNLOAD unload specified buffer(s) - * action == DOBUF_DEL delete specified buffer(s) from buffer list - * action == DOBUF_WIPE delete specified buffer(s) really - * - * start == DOBUF_CURRENT go to "count" buffer from current buffer - * start == DOBUF_FIRST go to "count" buffer from first buffer - * start == DOBUF_LAST go to "count" buffer from last buffer - * start == DOBUF_MOD go to "count" modified buffer from current buffer - * - * Return FAIL or OK. - */ -int -do_buffer ( - int action, - int start, - int dir, /* FORWARD or BACKWARD */ - int count, /* buffer number or number of buffers */ - int forceit /* TRUE for :...! */ -) -{ - buf_T *buf; - buf_T *bp; - int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL - || action == DOBUF_WIPE); - - switch (start) { - case DOBUF_FIRST: buf = firstbuf; break; - case DOBUF_LAST: buf = lastbuf; break; - default: buf = curbuf; break; - } - if (start == DOBUF_MOD) { /* find next modified buffer */ - while (count-- > 0) { - do { - buf = buf->b_next; - if (buf == NULL) - buf = firstbuf; - } while (buf != curbuf && !bufIsChanged(buf)); - } - if (!bufIsChanged(buf)) { - EMSG(_("E84: No modified buffer found")); - return FAIL; - } - } else if (start == DOBUF_FIRST && count) { /* find specified buffer number */ - while (buf != NULL && buf->b_fnum != count) - buf = buf->b_next; - } else { - bp = NULL; - while (count > 0 || (!unload && !buf->b_p_bl && bp != buf)) { - /* remember the buffer where we start, we come back there when all - * buffers are unlisted. */ - if (bp == NULL) - bp = buf; - if (dir == FORWARD) { - buf = buf->b_next; - if (buf == NULL) - buf = firstbuf; - } else { - buf = buf->b_prev; - if (buf == NULL) - buf = lastbuf; - } - /* don't count unlisted buffers */ - if (unload || buf->b_p_bl) { - --count; - bp = NULL; /* use this buffer as new starting point */ - } - if (bp == buf) { - /* back where we started, didn't find anything. */ - EMSG(_("E85: There is no listed buffer")); - return FAIL; - } - } - } - - if (buf == NULL) { /* could not find it */ - if (start == DOBUF_FIRST) { - /* don't warn when deleting */ - if (!unload) - EMSGN(_("E86: Buffer %" PRId64 " does not exist"), count); - } else if (dir == FORWARD) - EMSG(_("E87: Cannot go beyond last buffer")); - else - EMSG(_("E88: Cannot go before first buffer")); - return FAIL; - } - - - /* - * delete buffer buf from memory and/or the list - */ - if (unload) { - int forward; - - /* When unloading or deleting a buffer that's already unloaded and - * unlisted: fail silently. */ - if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl) - return FAIL; - - if (!forceit && bufIsChanged(buf)) { - if ((p_confirm || cmdmod.confirm) && p_write) { - dialog_changed(buf, FALSE); - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! It's not changed - * now. */ - return FAIL; - /* If it's still changed fail silently, the dialog already - * mentioned why it fails. */ - if (bufIsChanged(buf)) - return FAIL; - } else { - EMSGN(_("E89: No write since last change for buffer %" PRId64 - " (add ! to override)"), - buf->b_fnum); - return FAIL; - } - } - - /* - * If deleting the last (listed) buffer, make it empty. - * The last (listed) buffer cannot be unloaded. - */ - for (bp = firstbuf; bp != NULL; bp = bp->b_next) - if (bp->b_p_bl && bp != buf) - break; - if (bp == NULL && buf == curbuf) - return empty_curbuf(TRUE, forceit, action); - - /* - * If the deleted buffer is the current one, close the current window - * (unless it's the only window). Repeat this so long as we end up in - * a window with this buffer. - */ - while (buf == curbuf - && !(curwin->w_closing || curwin->w_buffer->b_closing) - && (firstwin != lastwin || first_tabpage->tp_next != NULL)) { - if (win_close(curwin, FALSE) == FAIL) - break; - } - - /* - * If the buffer to be deleted is not the current one, delete it here. - */ - if (buf != curbuf) { - close_windows(buf, FALSE); - if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0) - close_buffer(NULL, buf, action, FALSE); - return OK; - } - - /* - * Deleting the current buffer: Need to find another buffer to go to. - * There should be another, otherwise it would have been handled - * above. However, autocommands may have deleted all buffers. - * First use au_new_curbuf, if it is valid. - * Then prefer the buffer we most recently visited. - * Else try to find one that is loaded, after the current buffer, - * then before the current buffer. - * Finally use any buffer. - */ - buf = NULL; /* selected buffer */ - bp = NULL; /* used when no loaded buffer found */ - if (au_new_curbuf != NULL && buf_valid(au_new_curbuf)) - buf = au_new_curbuf; - else if (curwin->w_jumplistlen > 0) { - int jumpidx; - - jumpidx = curwin->w_jumplistidx - 1; - if (jumpidx < 0) - jumpidx = curwin->w_jumplistlen - 1; - - forward = jumpidx; - while (jumpidx != curwin->w_jumplistidx) { - buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum); - if (buf != NULL) { - if (buf == curbuf || !buf->b_p_bl) - buf = NULL; /* skip current and unlisted bufs */ - else if (buf->b_ml.ml_mfp == NULL) { - /* skip unloaded buf, but may keep it for later */ - if (bp == NULL) - bp = buf; - buf = NULL; - } - } - if (buf != NULL) /* found a valid buffer: stop searching */ - break; - /* advance to older entry in jump list */ - if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen) - break; - if (--jumpidx < 0) - jumpidx = curwin->w_jumplistlen - 1; - if (jumpidx == forward) /* List exhausted for sure */ - break; - } - } - - if (buf == NULL) { /* No previous buffer, Try 2'nd approach */ - forward = TRUE; - buf = curbuf->b_next; - for (;; ) { - if (buf == NULL) { - if (!forward) /* tried both directions */ - break; - buf = curbuf->b_prev; - forward = FALSE; - continue; - } - /* in non-help buffer, try to skip help buffers, and vv */ - if (buf->b_help == curbuf->b_help && buf->b_p_bl) { - if (buf->b_ml.ml_mfp != NULL) /* found loaded buffer */ - break; - if (bp == NULL) /* remember unloaded buf for later */ - bp = buf; - } - if (forward) - buf = buf->b_next; - else - buf = buf->b_prev; - } - } - if (buf == NULL) /* No loaded buffer, use unloaded one */ - buf = bp; - if (buf == NULL) { /* No loaded buffer, find listed one */ - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_p_bl && buf != curbuf) - break; - } - if (buf == NULL) { /* Still no buffer, just take one */ - if (curbuf->b_next != NULL) - buf = curbuf->b_next; - else - buf = curbuf->b_prev; - } - } - - if (buf == NULL) { - /* Autocommands must have wiped out all other buffers. Only option - * now is to make the current buffer empty. */ - return empty_curbuf(FALSE, forceit, action); - } - - /* - * make buf current buffer - */ - if (action == DOBUF_SPLIT) { /* split window first */ - /* If 'switchbuf' contains "useopen": jump to first window containing - * "buf" if one exists */ - if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf)) - return OK; - /* If 'switchbuf' contains "usetab": jump to first window in any tab - * page containing "buf" if one exists */ - if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf)) - return OK; - if (win_split(0, 0) == FAIL) - return FAIL; - } - - /* go to current buffer - nothing to do */ - if (buf == curbuf) - return OK; - - /* - * Check if the current buffer may be abandoned. - */ - if (action == DOBUF_GOTO && !can_abandon(curbuf, forceit)) { - if ((p_confirm || cmdmod.confirm) && p_write) { - dialog_changed(curbuf, FALSE); - if (!buf_valid(buf)) - /* Autocommand deleted buffer, oops! */ - return FAIL; - } - if (bufIsChanged(curbuf)) { - EMSG(_(e_nowrtmsg)); - return FAIL; - } - } - - /* Go to the other buffer. */ - set_curbuf(buf, action); - -#if defined(FEAT_LISTCMDS) \ - && (defined(FEAT_SCROLLBIND) || defined(FEAT_CURSORBIND)) - if (action == DOBUF_SPLIT) { - RESET_BINDING(curwin); /* reset 'scrollbind' and 'cursorbind' */ - } -#endif - - if (aborting()) /* autocmds may abort script processing */ - return FAIL; - - return OK; -} - - -/* - * Set current buffer to "buf". Executes autocommands and closes current - * buffer. "action" tells how to close the current buffer: - * DOBUF_GOTO free or hide it - * DOBUF_SPLIT nothing - * DOBUF_UNLOAD unload it - * DOBUF_DEL delete it - * DOBUF_WIPE wipe it out - */ -void set_curbuf(buf_T *buf, int action) -{ - buf_T *prevbuf; - int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL - || action == DOBUF_WIPE); - long old_tw = curbuf->b_p_tw; - - setpcmark(); - if (!cmdmod.keepalt) - curwin->w_alt_fnum = curbuf->b_fnum; /* remember alternate file */ - buflist_altfpos(curwin); /* remember curpos */ - - /* Don't restart Select mode after switching to another buffer. */ - VIsual_reselect = FALSE; - - /* close_windows() or apply_autocmds() may change curbuf */ - prevbuf = curbuf; - - apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf); - if (buf_valid(prevbuf) && !aborting()) { - if (prevbuf == curwin->w_buffer) - reset_synblock(curwin); - if (unload) - close_windows(prevbuf, FALSE); - if (buf_valid(prevbuf) && !aborting()) { - win_T *previouswin = curwin; - if (prevbuf == curbuf) - u_sync(FALSE); - close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf, - unload ? action : (action == DOBUF_GOTO - && !P_HID(prevbuf) - && !bufIsChanged( - prevbuf)) ? DOBUF_UNLOAD : 0, FALSE); - if (curwin != previouswin && win_valid(previouswin)) - /* autocommands changed curwin, Grr! */ - curwin = previouswin; - } - } - /* An autocommand may have deleted "buf", already entered it (e.g., when - * it did ":bunload") or aborted the script processing! - * If curwin->w_buffer is null, enter_buffer() will make it valid again */ - if ((buf_valid(buf) && buf != curbuf - && !aborting() - ) || curwin->w_buffer == NULL - ) { - enter_buffer(buf); - if (old_tw != curbuf->b_p_tw) - check_colorcolumn(curwin); - } -} - -/* - * Enter a new current buffer. - * Old curbuf must have been abandoned already! This also means "curbuf" may - * be pointing to freed memory. - */ -void enter_buffer(buf_T *buf) -{ - /* Copy buffer and window local option values. Not for a help buffer. */ - buf_copy_options(buf, BCO_ENTER | BCO_NOHELP); - if (!buf->b_help) - get_winopts(buf); - else - /* Remove all folds in the window. */ - clearFolding(curwin); - foldUpdateAll(curwin); /* update folds (later). */ - - /* Get the buffer in the current window. */ - curwin->w_buffer = buf; - curbuf = buf; - ++curbuf->b_nwindows; - - if (curwin->w_p_diff) - diff_buf_add(curbuf); - - curwin->w_s = &(buf->b_s); - - /* Cursor on first line by default. */ - curwin->w_cursor.lnum = 1; - curwin->w_cursor.col = 0; - curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; - curwin->w_topline_was_set = FALSE; - - /* mark cursor position as being invalid */ - curwin->w_valid = 0; - - /* Make sure the buffer is loaded. */ - if (curbuf->b_ml.ml_mfp == NULL) { /* need to load the file */ - /* If there is no filetype, allow for detecting one. Esp. useful for - * ":ball" used in a autocommand. If there already is a filetype we - * might prefer to keep it. */ - if (*curbuf->b_p_ft == NUL) - did_filetype = FALSE; - - open_buffer(FALSE, NULL, 0); - } else { - if (!msg_silent) - need_fileinfo = TRUE; /* display file info after redraw */ - (void)buf_check_timestamp(curbuf, FALSE); /* check if file changed */ - curwin->w_topline = 1; - curwin->w_topfill = 0; - apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); - apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf); - } - - /* If autocommands did not change the cursor position, restore cursor lnum - * and possibly cursor col. */ - if (curwin->w_cursor.lnum == 1 && inindent(0)) - buflist_getfpos(); - - check_arg_idx(curwin); /* check for valid arg_idx */ - maketitle(); - /* when autocmds didn't change it */ - if (curwin->w_topline == 1 && !curwin->w_topline_was_set) - scroll_cursor_halfway(FALSE); /* redisplay at correct position */ - - - /* Change directories when the 'acd' option is set. */ - do_autochdir(); - - if (curbuf->b_kmap_state & KEYMAP_INIT) - (void)keymap_init(); - /* May need to set the spell language. Can only do this after the buffer - * has been properly setup. */ - if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL) - (void)did_set_spelllang(curwin); - - redraw_later(NOT_VALID); -} - -/* - * Change to the directory of the current buffer. - */ -void do_autochdir(void) -{ - if (p_acd) { - if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK) { - shorten_fnames(TRUE); - } - } -} - -/* - * functions for dealing with the buffer list - */ - -/* - * Add a file name to the buffer list. Return a pointer to the buffer. - * If the same file name already exists return a pointer to that buffer. - * If it does not exist, or if fname == NULL, a new entry is created. - * If (flags & BLN_CURBUF) is TRUE, may use current buffer. - * If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list. - * If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer. - * This is the ONLY way to create a new buffer. - */ -static int top_file_num = 1; /* highest file number */ - -buf_T * -buflist_new ( - char_u *ffname, /* full path of fname or relative */ - char_u *sfname, /* short fname or NULL */ - linenr_T lnum, /* preferred cursor line */ - int flags /* BLN_ defines */ -) -{ - buf_T *buf; - - fname_expand(curbuf, &ffname, &sfname); /* will allocate ffname */ - - /* - * If file name already exists in the list, update the entry. - */ - /* We can use inode numbers when the file exists. Works better - * for hard links. */ - FileInfo file_info; - if (sfname == NULL || !os_get_file_info((char *)sfname, &file_info)) { - file_info.stat.st_dev = INVALID_DEVICE_ID; - } - if (ffname != NULL && !(flags & BLN_DUMMY) - && (buf = buflist_findname_file_info(ffname, &file_info)) != NULL) { - free(ffname); - if (lnum != 0) - buflist_setfpos(buf, curwin, lnum, (colnr_T)0, FALSE); - /* copy the options now, if 'cpo' doesn't have 's' and not done - * already */ - buf_copy_options(buf, 0); - if ((flags & BLN_LISTED) && !buf->b_p_bl) { - buf->b_p_bl = TRUE; - if (!(flags & BLN_DUMMY)) { - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { - return NULL; - } - } - } - return buf; - } - - /* - * If the current buffer has no name and no contents, use the current - * buffer. Otherwise: Need to allocate a new buffer structure. - * - * This is the ONLY place where a new buffer structure is allocated! - * (A spell file buffer is allocated in spell.c, but that's not a normal - * buffer.) - */ - buf = NULL; - if ((flags & BLN_CURBUF) - && curbuf != NULL - && curbuf->b_ffname == NULL - && curbuf->b_nwindows <= 1 - && (curbuf->b_ml.ml_mfp == NULL || bufempty())) { - buf = curbuf; - /* It's like this buffer is deleted. Watch out for autocommands that - * change curbuf! If that happens, allocate a new buffer anyway. */ - if (curbuf->b_p_bl) - apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf); - if (buf == curbuf) - apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf); - if (aborting()) /* autocmds may abort script processing */ - return NULL; - if (buf == curbuf) { - /* Make sure 'bufhidden' and 'buftype' are empty */ - clear_string_option(&buf->b_p_bh); - clear_string_option(&buf->b_p_bt); - } - } - if (buf != curbuf || curbuf == NULL) { - buf = xcalloc(1, sizeof(buf_T)); - /* init b: variables */ - buf->b_vars = dict_alloc(); - init_var_dict(buf->b_vars, &buf->b_bufvar, VAR_SCOPE); - } - - if (ffname != NULL) { - buf->b_ffname = ffname; - buf->b_sfname = vim_strsave(sfname); - } - - clear_wininfo(buf); - buf->b_wininfo = xcalloc(1, sizeof(wininfo_T)); - - if (ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL)) { - free(buf->b_ffname); - buf->b_ffname = NULL; - free(buf->b_sfname); - buf->b_sfname = NULL; - if (buf != curbuf) - free_buffer(buf); - return NULL; - } - - if (buf == curbuf) { - /* free all things allocated for this buffer */ - buf_freeall(buf, 0); - if (buf != curbuf) /* autocommands deleted the buffer! */ - return NULL; - if (aborting()) /* autocmds may abort script processing */ - return NULL; - /* buf->b_nwindows = 0; why was this here? */ - free_buffer_stuff(buf, FALSE); /* delete local variables et al. */ - - /* Init the options. */ - buf->b_p_initialized = FALSE; - buf_copy_options(buf, BCO_ENTER); - - /* need to reload lmaps and set b:keymap_name */ - curbuf->b_kmap_state |= KEYMAP_INIT; - } else { - /* - * put new buffer at the end of the buffer list - */ - buf->b_next = NULL; - if (firstbuf == NULL) { /* buffer list is empty */ - buf->b_prev = NULL; - firstbuf = buf; - } else { /* append new buffer at end of list */ - lastbuf->b_next = buf; - buf->b_prev = lastbuf; - } - lastbuf = buf; - - buf->b_fnum = top_file_num++; - if (top_file_num < 0) { /* wrap around (may cause duplicates) */ - EMSG(_("W14: Warning: List of file names overflow")); - if (emsg_silent == 0) { - out_flush(); - ui_delay(3000L, TRUE); /* make sure it is noticed */ - } - top_file_num = 1; - } - - /* - * Always copy the options from the current buffer. - */ - buf_copy_options(buf, BCO_ALWAYS); - } - - buf->b_wininfo->wi_fpos.lnum = lnum; - buf->b_wininfo->wi_win = curwin; - - hash_init(&buf->b_s.b_keywtab); - hash_init(&buf->b_s.b_keywtab_ic); - - buf->b_fname = buf->b_sfname; - if (file_info.stat.st_dev == INVALID_DEVICE_ID) - buf->b_dev_valid = FALSE; - else { - buf->b_dev_valid = TRUE; - buf->b_dev = file_info.stat.st_dev; - buf->b_ino = file_info.stat.st_ino; - } - buf->b_u_synced = TRUE; - buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; - if (flags & BLN_DUMMY) - buf->b_flags |= BF_DUMMY; - buf_clear_file(buf); - clrallmarks(buf); /* clear marks */ - fmarks_check_names(buf); /* check file marks for this file */ - buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */ - if (!(flags & BLN_DUMMY)) { - apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { - return NULL; - } - if (flags & BLN_LISTED) { - apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf); - if (!buf_valid(buf)) { - return NULL; - } - } - if (aborting()) /* autocmds may abort script processing */ - return NULL; - } - - return buf; -} - -/* - * Free the memory for the options of a buffer. - * If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and - * 'fileencoding'. - */ -void free_buf_options(buf_T *buf, int free_p_ff) -{ - if (free_p_ff) { - clear_string_option(&buf->b_p_fenc); - clear_string_option(&buf->b_p_ff); - clear_string_option(&buf->b_p_bh); - clear_string_option(&buf->b_p_bt); - } - clear_string_option(&buf->b_p_def); - clear_string_option(&buf->b_p_inc); - clear_string_option(&buf->b_p_inex); - clear_string_option(&buf->b_p_inde); - clear_string_option(&buf->b_p_indk); - clear_string_option(&buf->b_p_cm); - clear_string_option(&buf->b_p_fex); - clear_string_option(&buf->b_p_key); - clear_string_option(&buf->b_p_kp); - clear_string_option(&buf->b_p_mps); - clear_string_option(&buf->b_p_fo); - clear_string_option(&buf->b_p_flp); - clear_string_option(&buf->b_p_isk); - clear_string_option(&buf->b_p_keymap); - ga_clear(&buf->b_kmap_ga); - clear_string_option(&buf->b_p_com); - clear_string_option(&buf->b_p_cms); - clear_string_option(&buf->b_p_nf); - clear_string_option(&buf->b_p_syn); - clear_string_option(&buf->b_s.b_p_spc); - clear_string_option(&buf->b_s.b_p_spf); - vim_regfree(buf->b_s.b_cap_prog); - buf->b_s.b_cap_prog = NULL; - clear_string_option(&buf->b_s.b_p_spl); - clear_string_option(&buf->b_p_sua); - clear_string_option(&buf->b_p_ft); - clear_string_option(&buf->b_p_cink); - clear_string_option(&buf->b_p_cino); - clear_string_option(&buf->b_p_cinw); - clear_string_option(&buf->b_p_cpt); - clear_string_option(&buf->b_p_cfu); - clear_string_option(&buf->b_p_ofu); - clear_string_option(&buf->b_p_gp); - clear_string_option(&buf->b_p_mp); - clear_string_option(&buf->b_p_efm); - clear_string_option(&buf->b_p_ep); - clear_string_option(&buf->b_p_path); - clear_string_option(&buf->b_p_tags); - clear_string_option(&buf->b_p_dict); - clear_string_option(&buf->b_p_tsr); - clear_string_option(&buf->b_p_qe); - buf->b_p_ar = -1; - buf->b_p_ul = NO_LOCAL_UNDOLEVEL; -} - -/* - * get alternate file n - * set linenr to lnum or altfpos.lnum if lnum == 0 - * also set cursor column to altfpos.col if 'startofline' is not set. - * if (options & GETF_SETMARK) call setpcmark() - * if (options & GETF_ALT) we are jumping to an alternate file. - * if (options & GETF_SWITCH) respect 'switchbuf' settings when jumping - * - * return FAIL for failure, OK for success - */ -int buflist_getfile(int n, linenr_T lnum, int options, int forceit) -{ - buf_T *buf; - win_T *wp = NULL; - pos_T *fpos; - colnr_T col; - - buf = buflist_findnr(n); - if (buf == NULL) { - if ((options & GETF_ALT) && n == 0) - EMSG(_(e_noalt)); - else - EMSGN(_("E92: Buffer %" PRId64 " not found"), n); - return FAIL; - } - - /* if alternate file is the current buffer, nothing to do */ - if (buf == curbuf) - return OK; - - if (text_locked()) { - text_locked_msg(); - return FAIL; - } - if (curbuf_locked()) - return FAIL; - - /* altfpos may be changed by getfile(), get it now */ - if (lnum == 0) { - fpos = buflist_findfpos(buf); - lnum = fpos->lnum; - col = fpos->col; - } else - col = 0; - - if (options & GETF_SWITCH) { - /* If 'switchbuf' contains "useopen": jump to first window containing - * "buf" if one exists */ - if (swb_flags & SWB_USEOPEN) - wp = buf_jump_open_win(buf); - /* If 'switchbuf' contains "usetab": jump to first window in any tab - * page containing "buf" if one exists */ - if (wp == NULL && (swb_flags & SWB_USETAB)) - wp = buf_jump_open_tab(buf); - /* If 'switchbuf' contains "split" or "newtab" and the current buffer - * isn't empty: open new window */ - if (wp == NULL && (swb_flags & (SWB_SPLIT | SWB_NEWTAB)) && !bufempty()) { - if (swb_flags & SWB_NEWTAB) /* Open in a new tab */ - tabpage_new(); - else if (win_split(0, 0) == FAIL) /* Open in a new window */ - return FAIL; - RESET_BINDING(curwin); - } - } - - ++RedrawingDisabled; - if (getfile(buf->b_fnum, NULL, NULL, (options & GETF_SETMARK), - lnum, forceit) <= 0) { - --RedrawingDisabled; - - /* cursor is at to BOL and w_cursor.lnum is checked due to getfile() */ - if (!p_sol && col != 0) { - curwin->w_cursor.col = col; - check_cursor_col(); - curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; - } - return OK; - } - --RedrawingDisabled; - return FAIL; -} - -/* - * go to the last know line number for the current buffer - */ -void buflist_getfpos(void) -{ - pos_T *fpos; - - fpos = buflist_findfpos(curbuf); - - curwin->w_cursor.lnum = fpos->lnum; - check_cursor_lnum(); - - if (p_sol) - curwin->w_cursor.col = 0; - else { - curwin->w_cursor.col = fpos->col; - check_cursor_col(); - curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; - } -} - -/* - * Find file in buffer list by name (it has to be for the current window). - * Returns NULL if not found. - */ -buf_T *buflist_findname_exp(char_u *fname) -{ - char_u *ffname; - buf_T *buf = NULL; - - /* First make the name into a full path name */ - ffname = FullName_save(fname, -#ifdef UNIX - TRUE /* force expansion, get rid of symbolic links */ -#else - FALSE -#endif - ); - if (ffname != NULL) { - buf = buflist_findname(ffname); - free(ffname); - } - return buf; -} - -/* - * Find file in buffer list by name (it has to be for the current window). - * "ffname" must have a full path. - * Skips dummy buffers. - * Returns NULL if not found. - */ -buf_T *buflist_findname(char_u *ffname) -{ - FileInfo file_info; - if (!os_get_file_info((char *)ffname, &file_info)) { - file_info.stat.st_dev = INVALID_DEVICE_ID; - } - return buflist_findname_file_info(ffname, &file_info); -} - -/* - * Same as buflist_findname(), but pass the FileInfo structure to avoid - * getting it twice for the same file. - * Returns NULL if not found. - */ -static buf_T *buflist_findname_file_info(char_u *ffname, FileInfo *file_info) -{ - buf_T *buf; - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if ((buf->b_flags & BF_DUMMY) == 0 - && !otherfile_buf(buf, ffname, file_info)) { - return buf; - } - } - return NULL; -} - -/* - * Find file in buffer list by a regexp pattern. - * Return fnum of the found buffer. - * Return < 0 for error. - */ -int -buflist_findpat ( - char_u *pattern, - char_u *pattern_end, /* pointer to first char after pattern */ - int unlisted, /* find unlisted buffers */ - int diffmode, /* find diff-mode buffers only */ - int curtab_only /* find buffers in current tab only */ -) -{ - buf_T *buf; - regprog_T *prog; - int match = -1; - int find_listed; - char_u *pat; - char_u *patend; - int attempt; - char_u *p; - int toggledollar; - - if (pattern_end == pattern + 1 && (*pattern == '%' || *pattern == '#')) { - if (*pattern == '%') - match = curbuf->b_fnum; - else - match = curwin->w_alt_fnum; - if (diffmode && !diff_mode_buf(buflist_findnr(match))) - match = -1; - } - /* - * Try four ways of matching a listed buffer: - * attempt == 0: without '^' or '$' (at any position) - * attempt == 1: with '^' at start (only at position 0) - * attempt == 2: with '$' at end (only match at end) - * attempt == 3: with '^' at start and '$' at end (only full match) - * Repeat this for finding an unlisted buffer if there was no matching - * listed buffer. - */ - else { - pat = file_pat_to_reg_pat(pattern, pattern_end, NULL, FALSE); - if (pat == NULL) - return -1; - patend = pat + STRLEN(pat) - 1; - toggledollar = (patend > pat && *patend == '$'); - - /* First try finding a listed buffer. If not found and "unlisted" - * is TRUE, try finding an unlisted buffer. */ - find_listed = TRUE; - for (;; ) { - for (attempt = 0; attempt <= 3; ++attempt) { - /* may add '^' and '$' */ - if (toggledollar) - *patend = (attempt < 2) ? NUL : '$'; /* add/remove '$' */ - p = pat; - if (*p == '^' && !(attempt & 1)) /* add/remove '^' */ - ++p; - prog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); - if (prog == NULL) { - free(pat); - return -1; - } - - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_p_bl == find_listed - && (!diffmode || diff_mode_buf(buf)) - && buflist_match(prog, buf) != NULL) { - if (curtab_only) { - /* Ignore the match if the buffer is not open in - * the current tab. */ - win_T *wp; - - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_buffer == buf) - break; - if (wp == NULL) - continue; - } - if (match >= 0) { /* already found a match */ - match = -2; - break; - } - match = buf->b_fnum; /* remember first match */ - } - - vim_regfree(prog); - if (match >= 0) /* found one match */ - break; - } - - /* Only search for unlisted buffers if there was no match with - * a listed buffer. */ - if (!unlisted || !find_listed || match != -1) - break; - find_listed = FALSE; - } - - free(pat); - } - - if (match == -2) - EMSG2(_("E93: More than one match for %s"), pattern); - else if (match < 0) - EMSG2(_("E94: No matching buffer for %s"), pattern); - return match; -} - - -/* - * Find all buffer names that match. - * For command line expansion of ":buf" and ":sbuf". - * Return OK if matches found, FAIL otherwise. - */ -int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) -{ - int count = 0; - buf_T *buf; - int round; - char_u *p; - int attempt; - regprog_T *prog; - char_u *patc; - - *num_file = 0; /* return values in case of FAIL */ - *file = NULL; - - /* Make a copy of "pat" and change "^" to "\(^\|[\/]\)". */ - if (*pat == '^') { - patc = xmalloc(STRLEN(pat) + 11); - STRCPY(patc, "\\(^\\|[\\/]\\)"); - STRCPY(patc + 11, pat + 1); - } else - patc = pat; - - /* - * attempt == 0: try match with '\<', match at start of word - * attempt == 1: try match without '\<', match anywhere - */ - for (attempt = 0; attempt <= 1; ++attempt) { - if (attempt > 0 && patc == pat) - break; /* there was no anchor, no need to try again */ - prog = vim_regcomp(patc + attempt * 11, RE_MAGIC); - if (prog == NULL) { - if (patc != pat) - free(patc); - return FAIL; - } - - /* - * round == 1: Count the matches. - * round == 2: Build the array to keep the matches. - */ - for (round = 1; round <= 2; ++round) { - count = 0; - for (buf = firstbuf; buf != NULL; buf = buf->b_next) { - if (!buf->b_p_bl) /* skip unlisted buffers */ - continue; - p = buflist_match(prog, buf); - if (p != NULL) { - if (round == 1) - ++count; - else { - if (options & WILD_HOME_REPLACE) - p = home_replace_save(buf, p); - else - p = vim_strsave(p); - (*file)[count++] = p; - } - } - } - if (count == 0) /* no match found, break here */ - break; - if (round == 1) { - *file = xmalloc(count * sizeof(**file)); - } - } - vim_regfree(prog); - if (count) /* match(es) found, break here */ - break; - } - - if (patc != pat) - free(patc); - - *num_file = count; - return count == 0 ? FAIL : OK; -} - - -#ifdef HAVE_BUFLIST_MATCH -/* - * Check for a match on the file name for buffer "buf" with regprog "prog". - */ -static char_u *buflist_match(regprog_T *prog, buf_T *buf) -{ - char_u *match; - - /* First try the short file name, then the long file name. */ - match = fname_match(prog, buf->b_sfname); - if (match == NULL) - match = fname_match(prog, buf->b_ffname); - - return match; -} - -/* - * Try matching the regexp in "prog" with file name "name". - * Return "name" when there is a match, NULL when not. - */ -static char_u *fname_match(regprog_T *prog, char_u *name) -{ - char_u *match = NULL; - char_u *p; - regmatch_T regmatch; - - if (name != NULL) { - regmatch.regprog = prog; - regmatch.rm_ic = p_fic; /* ignore case when 'fileignorecase' is set */ - if (vim_regexec(®match, name, (colnr_T)0)) - match = name; - else { - /* Replace $(HOME) with '~' and try matching again. */ - p = home_replace_save(NULL, name); - if (p != NULL && vim_regexec(®match, p, (colnr_T)0)) - match = name; - free(p); - } - } - - return match; -} -#endif - -/* - * find file in buffer list by number - */ -buf_T *buflist_findnr(int nr) -{ - buf_T *buf; - - if (nr == 0) - nr = curwin->w_alt_fnum; - for (buf = firstbuf; buf != NULL; buf = buf->b_next) - if (buf->b_fnum == nr) - return buf; - return NULL; -} - -/* - * Get name of file 'n' in the buffer list. - * When the file has no name an empty string is returned. - * home_replace() is used to shorten the file name (used for marks). - * Returns a pointer to allocated memory, of NULL when failed. - */ -char_u * -buflist_nr2name ( - int n, - int fullname, - int helptail /* for help buffers return tail only */ -) -{ - buf_T *buf; - - buf = buflist_findnr(n); - if (buf == NULL) - return NULL; - return home_replace_save(helptail ? buf : NULL, - fullname ? buf->b_ffname : buf->b_fname); -} - -/* - * Set the "lnum" and "col" for the buffer "buf" and the current window. - * When "copy_options" is TRUE save the local window option values. - * When "lnum" is 0 only do the options. - */ -static void buflist_setfpos(buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options) -{ - wininfo_T *wip; - - for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) - if (wip->wi_win == win) - break; - if (wip == NULL) { - /* allocate a new entry */ - wip = xcalloc(1, sizeof(wininfo_T)); - wip->wi_win = win; - if (lnum == 0) /* set lnum even when it's 0 */ - lnum = 1; - } else { - /* remove the entry from the list */ - if (wip->wi_prev) - wip->wi_prev->wi_next = wip->wi_next; - else - buf->b_wininfo = wip->wi_next; - if (wip->wi_next) - wip->wi_next->wi_prev = wip->wi_prev; - if (copy_options && wip->wi_optset) { - clear_winopt(&wip->wi_opt); - deleteFoldRecurse(&wip->wi_folds); - } - } - if (lnum != 0) { - wip->wi_fpos.lnum = lnum; - wip->wi_fpos.col = col; - } - if (copy_options) { - /* Save the window-specific option values. */ - copy_winopt(&win->w_onebuf_opt, &wip->wi_opt); - wip->wi_fold_manual = win->w_fold_manual; - cloneFoldGrowArray(&win->w_folds, &wip->wi_folds); - wip->wi_optset = TRUE; - } - - /* insert the entry in front of the list */ - wip->wi_next = buf->b_wininfo; - buf->b_wininfo = wip; - wip->wi_prev = NULL; - if (wip->wi_next) - wip->wi_next->wi_prev = wip; - - return; -} - -static int wininfo_other_tab_diff(wininfo_T *wip); - -/* - * Return TRUE when "wip" has 'diff' set and the diff is only for another tab - * page. That's because a diff is local to a tab page. - */ -static int wininfo_other_tab_diff(wininfo_T *wip) -{ - win_T *wp; - - if (wip->wi_opt.wo_diff) { - for (wp = firstwin; wp != NULL; wp = wp->w_next) - /* return FALSE when it's a window in the current tab page, thus - * the buffer was in diff mode here */ - if (wip->wi_win == wp) - return FALSE; - return TRUE; - } - return FALSE; -} - -/* - * Find info for the current window in buffer "buf". - * If not found, return the info for the most recently used window. - * When "skip_diff_buffer" is TRUE avoid windows with 'diff' set that is in - * another tab page. - * Returns NULL when there isn't any info. - */ -static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer) -{ - wininfo_T *wip; - - for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) - if (wip->wi_win == curwin - && (!skip_diff_buffer || !winin