5517 lines · 129.4 KB · C
RawDownload
/* vi:set ts=8 sts=4 sw=4: * * 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 "vim.h" #if defined(FEAT_CMDL_COMPL) || defined(FEAT_LISTCMDS) || defined(FEAT_EVAL) || defined(FEAT_PERL)static char_u	*buflist_match __ARGS((regprog_T *prog, buf_T *buf));# define HAVE_BUFLIST_MATCHstatic char_u	*fname_match __ARGS((regprog_T *prog, char_u *name));#endifstatic void	buflist_setfpos __ARGS((buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options));static wininfo_T *find_wininfo __ARGS((buf_T *buf));#ifdef UNIXstatic buf_T	*buflist_findname_stat __ARGS((char_u *ffname, struct stat *st));static int	otherfile_buf __ARGS((buf_T *buf, char_u *ffname, struct stat *stp));static int	buf_same_ino __ARGS((buf_T *buf, struct stat *stp));#elsestatic int	otherfile_buf __ARGS((buf_T *buf, char_u *ffname));#endif#ifdef FEAT_TITLEstatic int	ti_change __ARGS((char_u *str, char_u **last));#endifstatic void	free_buffer __ARGS((buf_T *));static void	free_buffer_stuff __ARGS((buf_T *buf, int free_options));static void	clear_wininfo __ARGS((buf_T *buf)); #ifdef UNIX# define dev_T dev_t#else# define dev_T unsigned#endif #if defined(FEAT_SIGNS)static void insert_sign __ARGS((buf_T *buf, signlist_T *prev, signlist_T *next, int id, linenr_T lnum, int typenr));static void buf_delete_signs __ARGS((buf_T *buf));#endif /* * Open current buffer, that is: open the memfile and read the file into memory * return FAIL for failure, OK otherwise */    intopen_buffer(read_stdin, eap)    int		read_stdin;	    /* read file from stdin */    exarg_T	*eap;		    /* for forced 'ff' and 'fenc' or NULL */{    int		retval = OK;#ifdef FEAT_AUTOCMD    buf_T	*old_curbuf;#endif     /*     * 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);	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 loose.	 */	if (curbuf == NULL)	{	    EMSG(_("E82: Cannot allocate any buffer, exiting..."));	    getout(2);	}	EMSG(_("E83: Cannot allocate buffer, using other one..."));	enter_buffer(curbuf);	return FAIL;    } #ifdef FEAT_AUTOCMD    /* The autocommands in readfile() may change the buffer, but only AFTER     * reading the file. */    old_curbuf = curbuf;    modified_was_set = FALSE;#endif     /* mark cursor position as being invalid */    changed_line_abv_curs();     if (curbuf->b_ffname != NULL#ifdef FEAT_NETBEANS_INTG	    && netbeansReadFile#endif       )    {#ifdef FEAT_NETBEANS_INTG	int oldFire = netbeansFireChanges; 	netbeansFireChanges = 0;#endif	retval = readfile(curbuf->b_ffname, curbuf->b_fname,		  (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, READ_NEW);#ifdef FEAT_NETBEANS_INTG	netbeansFireChanges = oldFire;#endif	/* 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, 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, 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;#ifdef FEAT_AUTOCMD# ifdef FEAT_EVAL	    apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE,							curbuf, &retval);# else	    apply_autocmds(EVENT_STDINREADPOST, NULL, NULL, FALSE, curbuf);# endif#endif	}    }     /* if first time loading this buffer, init b_chartab[] */    if (curbuf->b_flags & BF_NEVERLOADED)	(void)buf_init_chartab(curbuf, FALSE);     /*     * 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 ((read_stdin && !readonlymode && !bufempty())#ifdef FEAT_AUTOCMD		|| modified_was_set	/* ":set modified" used in autocmd */# ifdef FEAT_EVAL		|| (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)# endif#endif		|| (got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL))	changed();    else if (retval != FAIL)	unchanged(curbuf, FALSE);    save_file_ff(curbuf);		/* keep this fileformat */     /* require "!" to overwrite the file, because it wasn't read completely */#ifdef FEAT_EVAL    if (aborting())#else    if (got_int)#endif	curbuf->b_flags |= BF_READERR; #ifdef FEAT_FOLDING    /* Need to update automatic folding.  Do this before the autocommands,     * they may use the fold info. */    foldUpdateAll(curwin);#endif #ifdef FEAT_AUTOCMD    /* need to set w_topline, unless some autocommand already did that. */    if (!(curwin->w_valid & VALID_TOPLINE))    {	curwin->w_topline = 1;# ifdef FEAT_DIFF	curwin->w_topfill = 0;# endif    }# ifdef FEAT_EVAL    apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval);# else    apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);# endif#endif     if (retval != FAIL)    {#ifdef FEAT_AUTOCMD	/*	 * 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);#endif	    do_modelines(0);	    curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED); #ifdef FEAT_AUTOCMD# ifdef FEAT_EVAL	    apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf,								    &retval);# else	    apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);# endif 	    /* restore curwin/curbuf and a few other things */	    aucmd_restbuf(&aco);	}#endif    }     return retval;} /* * Return TRUE if "buf" points to a valid buffer (in the buffer list). */    intbuf_valid(buf)    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. */    voidclose_buffer(win, buf, action)    win_T	*win;		/* if not NULL, set b_last_cursor */    buf_T	*buf;    int		action;{#ifdef FEAT_AUTOCMD    int		is_curbuf;    int		nwindows = buf->b_nwindows;#endif    int		unload_buf = (action != 0);    int		del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);    int		wipe_buf = (action == DOBUF_WIPE); #ifdef FEAT_QUICKFIX    /*     * 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;#endif     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);    } #ifdef FEAT_AUTOCMD    /* When the buffer is no longer in a window, trigger BufWinLeave */    if (buf->b_nwindows == 1)    {	apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,								  FALSE, buf);	if (!buf_valid(buf))	    /* autocommands may delete the buffer */	    return; 	/* When the buffer becomes hidden, but is not unloaded, trigger	 * BufHidden */	if (!unload_buf)	{	    apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,								  FALSE, buf);	    if (!buf_valid(buf))	/* autocmds may delete the buffer */		return;	}# ifdef FEAT_EVAL	if (aborting())	    /* autocmds may abort script processing */	    return;# endif    }    nwindows = buf->b_nwindows;#endif     /* 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)    {#if 0	    /* why was this here? */	if (buf == curbuf)	    u_sync();	    /* sync undo before going to another buffer */#endif	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.     */#ifdef FEAT_AUTOCMD    /* 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;#endif     buf_freeall(buf, del_buf, wipe_buf); #ifdef FEAT_AUTOCMD    /* Autocommands may have deleted the buffer. */    if (!buf_valid(buf))	return;# ifdef FEAT_EVAL    if (aborting())	    /* autocmds may abort script processing */	return;# endif     /* 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;#endif #ifdef FEAT_NETBEANS_INTG    if (usingNetbeans)	netbeans_file_closed(buf);#endif    /* Change directories when the 'acd' option is set. */    DO_AUTOCHDIR     /*     * Remove the buffer from the list.     */    if (wipe_buf)    {#ifdef FEAT_SUN_WORKSHOP	if (usingSunWorkShop)	    workshop_file_closed_lineno((char *)buf->b_ffname,			(int)buf->b_last_cursor.lnum);#endif	vim_free(buf->b_ffname);	vim_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. */    voidbuf_clear_file(buf)    buf_T	*buf;{    buf->b_ml.ml_line_count = 1;    unchanged(buf, TRUE);#ifndef SHORT_FNAME    buf->b_shortname = FALSE;#endif    buf->b_p_eol = TRUE;    buf->b_start_eol = TRUE;#ifdef FEAT_MBYTE    buf->b_p_bomb = FALSE;#endif    buf->b_ml.ml_mfp = NULL;    buf->b_ml.ml_flags = ML_EMPTY;		/* empty buffer */

Lines 1–500 of 5517.