⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 memline.c

📁 VIM文本编辑器
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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.
 */

/* for debugging */
/* #define CHECK(c, s)	if (c) EMSG(s) */
#define CHECK(c, s)

/*
 * memline.c: Contains the functions for appending, deleting and changing the
 * text lines. The memfile functions are used to store the information in blocks
 * of memory, backed up by a file. The structure of the information is a tree.
 * The root of the tree is a pointer block. The leaves of the tree are data
 * blocks. In between may be several layers of pointer blocks, forming branches.
 *
 * Three types of blocks are used:
 * - Block nr 0 contains information for recovery
 * - Pointer blocks contain list of pointers to other blocks.
 * - Data blocks contain the actual text.
 *
 * Block nr 0 contains the block0 structure (see below).
 *
 * Block nr 1 is the first pointer block. It is the root of the tree.
 * Other pointer blocks are branches.
 *
 *  If a line is too big to fit in a single page, the block containing that
 *  line is made big enough to hold the line. It may span several pages.
 *  Otherwise all blocks are one page.
 *
 *  A data block that was filled when starting to edit a file and was not
 *  changed since then, can have a negative block number. This means that it
 *  has not yet been assigned a place in the file. When recovering, the lines
 *  in this data block can be read from the original file. When the block is
 *  changed (lines appended/deleted/changed) or when it is flushed it gets a
 *  positive number. Use mf_trans_del() to get the new number, before calling
 *  mf_get().
 */

#if defined MSDOS  ||  defined WIN32
# include <io.h>
#endif

#include "vim.h"

#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#ifndef UNIX		/* it's in os_unix.h for Unix */
# include <time.h>
#endif

#ifdef SASC
# include <proto/dos.h>	    /* for Open() and Close() */
#endif

typedef struct block0		ZERO_BL;    /* contents of the first block */
typedef struct pointer_block	PTR_BL;	    /* contents of a pointer block */
typedef struct data_block	DATA_BL;    /* contents of a data block */
typedef struct pointer_entry	PTR_EN;	    /* block/line-count pair */

#define DATA_ID	    (('d' << 8) + 'a')	    /* data block id */
#define PTR_ID	    (('p' << 8) + 't')	    /* pointer block id */
#define BLOCK0_ID0  'b'			    /* block 0 id 0 */
#define BLOCK0_ID1  '0'			    /* block 0 id 1 */

/*
 * pointer to a block, used in a pointer block
 */
struct pointer_entry
{
    blocknr_t	pe_bnum;	/* block number */
    linenr_t	pe_line_count;	/* number of lines in this branch */
    linenr_t	pe_old_lnum;	/* lnum for this block (for recovery) */
    int		pe_page_count;	/* number of pages in block pe_bnum */
};

/*
 * A pointer block contains a list of branches in the tree.
 */
struct pointer_block
{
    short_u	pb_id;		/* ID for pointer block: PTR_ID */
    short_u	pb_count;	/* number of pointer in this block */
    short_u	pb_count_max;	/* maximum value for pb_count */
    PTR_EN	pb_pointer[1];	/* list of pointers to blocks (actually longer)
				 * followed by empty space until end of page */
};

/*
 * A data block is a leaf in the tree.
 *
 * The text of the lines is at the end of the block. The text of the first line
 * in the block is put at the end, the text of the second line in front of it,
 * etc. Thus the order of the lines is the opposite of the line number.
 */
struct data_block
{
    short_u	db_id;		/* ID for data block: DATA_ID */
    unsigned	db_free;	/* free space available */
    unsigned	db_txt_start;	/* byte where text starts */
    unsigned	db_txt_end;	/* byte just after data block */
    linenr_t	db_line_count;	/* number of lines in this block */
    unsigned	db_index[1];	/* index for start of line (actually bigger)
				 * followed by empty space upto db_txt_start
				 * followed by the text in the lines until
				 * end of page */
};

/*
 * The low bits of db_index hold the actual index. The topmost bit is
 * used for the global command to be able to mark a line.
 * This method is not clean, but otherwise there would be at least one extra
 * byte used for each line.
 * The mark has to be in this place to keep it with the correct line when other
 * lines are inserted or deleted.
 */
#define DB_MARKED	((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
#define DB_INDEX_MASK	(~DB_MARKED)

#define INDEX_SIZE  (sizeof(unsigned))	    /* size of one db_index entry */
#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE)  /* size of data block header */

#define B0_FNAME_SIZE	900
#define B0_UNAME_SIZE	40
#define B0_HNAME_SIZE	40
/*
 * Restrict the numbers to 32 bits, otherwise most compilers will complain.
 * This won't detect a 64 bit machine that only swaps a byte in the top 32
 * bits, but that is crazy anyway.
 */
#define B0_MAGIC_LONG	0x30313233
#define B0_MAGIC_INT	0x20212223
#define B0_MAGIC_SHORT	0x10111213
#define B0_MAGIC_CHAR	0x55

/*
 * Block zero holds all info about the swap file.
 *
 * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE, it makes all existing swap
 * files unusable!
 *
 * If size of block0 changes anyway, adjust minimal block size
 * in mf_open()!!
 *
 * This block is built up of single bytes, to make it portable accros
 * different machines. b0_magic_* is used to check the byte order and size of
 * variables, because the rest of the swap is not portable.
 */
struct block0
{
    char_u	b0_id[2];	/* id for block 0: BLOCK0_ID0 and BLOCK0_ID1 */
    char_u	b0_version[10];	/* Vim version string */
    char_u	b0_page_size[4];/* number of bytes per page */
    char_u	b0_mtime[4];	/* last modification time of file */
    char_u	b0_ino[4];	/* inode of b0_fname */
    char_u	b0_pid[4];	/* process id of creator (or 0) */
    char_u	b0_uname[B0_UNAME_SIZE]; /* name of user (uid if no name) */
    char_u	b0_hname[B0_HNAME_SIZE]; /* host name (if it has a name) */
    char_u	b0_fname[B0_FNAME_SIZE]; /* name of file being edited */
    long	b0_magic_long;	/* check for byte order of long */
    int		b0_magic_int;	/* check for byte order of int */
    short	b0_magic_short;	/* check for byte order of short */
    char_u	b0_magic_char;	/* check for last char */
};

#define STACK_INCR	5	/* nr of entries added to ml_stack at a time */

/*
 * The line number where the first mark may be is remembered.
 * If it is 0 there are no marks at all.
 * (always used for the current buffer only, no buffer change possible while
 * executing a global command).
 */
static linenr_t	lowest_marked = 0;

/*
 * arguments for ml_find_line()
 */
#define ML_DELETE	0x11	    /* delete line */
#define ML_INSERT	0x12	    /* insert line */
#define ML_FIND		0x13	    /* just find the line */
#define ML_FLUSH	0x02	    /* flush locked block */
#define ML_SIMPLE(x)	(x & 0x10)  /* DEL, INS or FIND */

static void set_b0_fname __ARGS((ZERO_BL *, BUF *buf));
static void swapfile_info __ARGS((char_u *));
static int recov_file_names __ARGS((char_u **, char_u *, int prepend_dot));
static int ml_append_int __ARGS((BUF *, linenr_t, char_u *, colnr_t, int, int));
static int ml_delete_int __ARGS((BUF *, linenr_t, int));
static char_u *findswapname __ARGS((BUF *, char_u **, char_u *));
static void ml_flush_line __ARGS((BUF *));
static BHDR *ml_new_data __ARGS((MEMFILE *, int, int));
static BHDR *ml_new_ptr __ARGS((MEMFILE *));
static BHDR *ml_find_line __ARGS((BUF *, linenr_t, int));
static int ml_add_stack __ARGS((BUF *));
static char_u *makeswapname __ARGS((BUF *, char_u *));
static void ml_lineadd __ARGS((BUF *, int));
static int b0_magic_wrong __ARGS((ZERO_BL *));
#ifdef CHECK_INODE
static int fnamecmp_ino __ARGS((char_u *, char_u *, long));
#endif
static void long_to_char __ARGS((long, char_u *));
static long char_to_long __ARGS((char_u *));

/*
 * open a new memline for 'curbuf'
 *
 * return FAIL for failure, OK otherwise
 */
    int
ml_open()
{
    MEMFILE	*mfp;
    BHDR	*hp = NULL;
    ZERO_BL	*b0p;
    PTR_BL	*pp;
    DATA_BL	*dp;

/*
 * init fields in memline struct
 */
    curbuf->b_ml.ml_stack_size = 0;	/* no stack yet */
    curbuf->b_ml.ml_stack = NULL;	/* no stack yet */
    curbuf->b_ml.ml_stack_top = 0;	/* nothing in the stack */
    curbuf->b_ml.ml_locked = NULL;	/* no cached block */
    curbuf->b_ml.ml_line_lnum = 0;	/* no cached line */

/*
 * When 'updatecount' is non-zero, flag that a swap file may be opened later.
 */
    if (p_uc && curbuf->b_p_swf)
	curbuf->b_may_swap = TRUE;
    else
	curbuf->b_may_swap = FALSE;

/*
 * Open the memfile.  No swap file is created yet.
 */
    mfp = mf_open(NULL, TRUE);
    if (mfp == NULL)
	goto error;

#if defined(MSDOS) && !defined(DJGPP)
    /* for 16 bit MS-DOS create a swapfile now, because we run out of
     * memory very quickly */
    if (p_uc)
	ml_open_file(curbuf);
#endif

    curbuf->b_ml.ml_mfp = mfp;
    curbuf->b_ml.ml_flags = ML_EMPTY;
    curbuf->b_ml.ml_line_count = 1;

/*
 * fill block0 struct and write page 0
 */
    if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
	goto error;
    if (hp->bh_bnum != 0)
    {
	EMSG("didn't get block nr 0?");
	goto error;
    }
    b0p = (ZERO_BL *)(hp->bh_data);

    b0p->b0_id[0] = BLOCK0_ID0;
    b0p->b0_id[1] = BLOCK0_ID1;
    b0p->b0_magic_long = (long)B0_MAGIC_LONG;
    b0p->b0_magic_int = (int)B0_MAGIC_INT;
    b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
    b0p->b0_magic_char = B0_MAGIC_CHAR;

    STRNCPY(b0p->b0_version, "VIM ", 4);
    STRNCPY(b0p->b0_version + 4, Version, 6);
    set_b0_fname(b0p, curbuf);
    long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
    (void)mch_get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
    b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
    mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
    b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
    long_to_char(mch_get_pid(), b0p->b0_pid);

    /*
     * Always sync block number 0 to disk, so we can check the file name in
     * the swap file in findswapname(). Don't do this for help files though.
     * Only works when there's a swapfile, otherwise it's done when the file
     * is created.
     */
    mf_put(mfp, hp, TRUE, FALSE);
    if (!curbuf->b_help)
	(void)mf_sync(mfp, 0);

/*
 * fill in root pointer block and write page 1
 */
    if ((hp = ml_new_ptr(mfp)) == NULL)
	goto error;
    if (hp->bh_bnum != 1)
    {
	EMSG("didn't get block nr 1?");
	goto error;
    }
    pp = (PTR_BL *)(hp->bh_data);
    pp->pb_count = 1;
    pp->pb_pointer[0].pe_bnum = 2;
    pp->pb_pointer[0].pe_page_count = 1;
    pp->pb_pointer[0].pe_old_lnum = 1;
    pp->pb_pointer[0].pe_line_count = 1;    /* line count after insertion */
    mf_put(mfp, hp, TRUE, FALSE);

/*
 * allocate first data block and create an empty line 1.
 */
    if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
	goto error;
    if (hp->bh_bnum != 2)
    {
	EMSG("didn't get block nr 2?");
	goto error;
    }

    dp = (DATA_BL *)(hp->bh_data);
    dp->db_index[0] = --dp->db_txt_start;	/* at end of block */
    dp->db_free -= 1 + INDEX_SIZE;
    dp->db_line_count = 1;
    *((char_u *)dp + dp->db_txt_start) = NUL;	/* emtpy line */

    return OK;

error:
    if (mfp != NULL)
    {
	if (hp)
	    mf_put(mfp, hp, FALSE, FALSE);
	mf_close(mfp, TRUE);	    /* will also free(mfp->mf_fname) */
    }
    curbuf->b_ml.ml_mfp = NULL;
    return FAIL;
}

/*
 * ml_setname() is called when the file name has been changed.
 * It may rename the swap file.
 */
    void
ml_setname()
{
    int		success = FALSE;
    MEMFILE	*mfp;
    char_u	*fname;
    char_u	*dirp;
#if defined(MSDOS) || defined(WIN32)
    char_u	*p;
#endif

    mfp = curbuf->b_ml.ml_mfp;
    if (mfp->mf_fd < 0)		    /* there is no swap file yet */
    {
	/*
	 * When 'updatecount' is 0 and 'noswapfile' there is no swap file.
	 * For help files we will make a swap file now.
	 */
	if (p_uc)
	    ml_open_file(curbuf);	/* create a swap file */
	return;
    }

/*
 * Try all directories in the 'directory' option.
 */
    dirp = p_dir;
    for (;;)
    {
	if (*dirp == NUL)	    /* tried all directories, fail */
	    break;
	fname = findswapname(curbuf, &dirp, mfp->mf_fname); /* alloc's fname */
	if (fname == NULL)	    /* no file name found for this dir */
	    continue;

#if defined(MSDOS) || defined(WIN32)
	/*
	 * Set full pathname for swap file now, because a ":!cd dir" may
	 * change directory without us knowing it.
	 */
	p = FullName_save(fname, FALSE);
	vim_free(fname);
	fname = p;
	if (fname == NULL)
	    continue;
#endif
	    /* if the file name is the same we don't have to do anything */
	if (fnamecmp(fname, mfp->mf_fname) == 0)
	{
	    vim_free(fname);
	    success = TRUE;
	    break;
	}
	    /* need to close the swap file before renaming */
	if (mfp->mf_fd >= 0)
	{
	    close(mfp->mf_fd);
	    mfp->mf_fd = -1;
	}

	    /* try to rename the swap file */
	if (vim_rename(mfp->mf_fname, fname) == 0)
	{
	    success = TRUE;
	    vim_free(mfp->mf_fname);
	    mfp->mf_fname = fname;
	    vim_free(mfp->mf_ffname);
#if defined(MSDOS) || defined(WIN32)
	    mfp->mf_ffname = NULL;  /* mf_fname is full pathname already */
#else
	    mf_set_ffname(mfp);
#endif
	    break;
	}
	vim_free(fname);	    /* this fname didn't work, try another */
    }

    if (mfp->mf_fd == -1)	    /* need to (re)open the swap file */
    {
	mfp->mf_fd = open((char *)mfp->mf_fname, O_RDWR | O_EXTRA
#ifndef macintosh
		, 0
#endif
		);
	if (mfp->mf_fd < 0)
	{
	    /* could not (re)open the swap file, what can we do???? */
	    EMSG("Oops, lost the swap file!!!");
	    return;
	}
    }
    if (!success)
	EMSG("Could not rename swap file");
}

/*
 * Open a file for the memfile for all buffers that are not readonly or have
 * been modified.
 * Used when 'updatecount' changes from zero to non-zero.
 */
    void
ml_open_files()
{
    BUF		*buf;

    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
	if (!buf->b_p_ro || buf->b_changed)
	    ml_open_file(buf);
}

/*
 * Open a swap file for an existing memfile, if there is no swap file yet.
 * If we are unable to find a file name, mf_fname will be NULL
 * and the memfile will be in memory only (no recovery possible).
 */
    void
ml_open_file(buf)
    BUF		*buf;
{
    MEMFILE	*mfp;
    char_u	*fname;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -