4861 lines · 128.2 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. */ /* 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) || defined(_WIN64)# include "vimio.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 #if defined(SASC) || defined(__amigaos4__)# include <proto/dos.h>	    /* for Open() and Close() */#endif #ifdef HAVE_ERRNO_H# include <errno.h>#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_ORG	900	/* what it was in older versions */#define B0_FNAME_SIZE		898#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	0x30313233L#define B0_MAGIC_INT	0x20212223L#define B0_MAGIC_SHORT	0x10111213L#define B0_MAGIC_CHAR	0x55 /* * Block zero holds all info about the swap file. * * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE! It would make all existing * swap files unusable! * * If size of block0 changes anyway, adjust MIN_SWAP_PAGE_SIZE in vim.h!! * * This block is built up of single bytes, to make it portable across * different machines. b0_magic_* is used to check the byte order and size of * variables, because the rest of the swap file 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_ORG]; /* 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 */}; /* * Note: b0_dirty and b0_flags are put at the end of the file name.  For very * long file names in older versions of Vim they are invalid. * The 'fileencoding' comes before b0_flags, with a NUL in front.  But only * when there is room, for very long file names it's omitted. */#define B0_DIRTY	0x55#define b0_dirty	b0_fname[B0_FNAME_SIZE_ORG-1] /* * The b0_flags field is new in Vim 7.0. */#define b0_flags	b0_fname[B0_FNAME_SIZE_ORG-2] /* The lowest two bits contain the fileformat.  Zero means it's not set * (compatible with Vim 6.x), otherwise it's EOL_UNIX + 1, EOL_DOS + 1 or * EOL_MAC + 1. */#define B0_FF_MASK	3 /* Swap file is in directory of edited file.  Used to find the file from * different mount points. */#define B0_SAME_DIR	4 /* The 'fileencoding' is at the end of b0_fname[], with a NUL in front of it. * When empty there is only the NUL. */#define B0_HAS_FENC	8 #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 ml_upd_block0 __ARGS((buf_T *buf, int set_fname));static void set_b0_fname __ARGS((ZERO_BL *, buf_T *buf));static void set_b0_dir_flag __ARGS((ZERO_BL *b0p, buf_T *buf));#ifdef FEAT_MBYTEstatic void add_b0_fenc __ARGS((ZERO_BL *b0p, buf_T *buf));#endifstatic time_t 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_T *, linenr_T, char_u *, colnr_T, int, int));static int ml_delete_int __ARGS((buf_T *, linenr_T, int));static char_u *findswapname __ARGS((buf_T *, char_u **, char_u *));static void ml_flush_line __ARGS((buf_T *));static bhdr_T *ml_new_data __ARGS((memfile_T *, int, int));static bhdr_T *ml_new_ptr __ARGS((memfile_T *));static bhdr_T *ml_find_line __ARGS((buf_T *, linenr_T, int));static int ml_add_stack __ARGS((buf_T *));static void ml_lineadd __ARGS((buf_T *, int));static int b0_magic_wrong __ARGS((ZERO_BL *));#ifdef CHECK_INODEstatic int fnamecmp_ino __ARGS((char_u *, char_u *, long));#endifstatic void long_to_char __ARGS((long, char_u *));static long char_to_long __ARGS((char_u *));#if defined(UNIX) || defined(WIN3264)static char_u *make_percent_swname __ARGS((char_u *dir, char_u *name));#endif#ifdef FEAT_BYTEOFFstatic void ml_updatechunk __ARGS((buf_T *buf, long line, long len, int updtype));#endif /* * Open a new memline for "buf". * * Return FAIL for failure, OK otherwise. */    intml_open(buf)    buf_T	*buf;{    memfile_T	*mfp;    bhdr_T	*hp = NULL;    ZERO_BL	*b0p;    PTR_BL	*pp;    DATA_BL	*dp;     /*     * init fields in memline struct     */    buf->b_ml.ml_stack_size = 0;	/* no stack yet */    buf->b_ml.ml_stack = NULL;	/* no stack yet */    buf->b_ml.ml_stack_top = 0;	/* nothing in the stack */    buf->b_ml.ml_locked = NULL;	/* no cached block */    buf->b_ml.ml_line_lnum = 0;	/* no cached line */#ifdef FEAT_BYTEOFF    buf->b_ml.ml_chunksize = NULL;#endif     /*     * When 'updatecount' is non-zero swap file may be opened later.     */    if (p_uc && buf->b_p_swf)	buf->b_may_swap = TRUE;    else	buf->b_may_swap = FALSE;     /*     * Open the memfile.  No swap file is created yet.     */    mfp = mf_open(NULL, 0);    if (mfp == NULL)	goto error;     buf->b_ml.ml_mfp = mfp;    buf->b_ml.ml_flags = ML_EMPTY;    buf->b_ml.ml_line_count = 1;#ifdef FEAT_LINEBREAK    curwin->w_nrwidth_line_count = 0;#endif #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 != 0)	ml_open_file(buf);#endif /* * fill block0 struct and write page 0 */    if ((hp = mf_new(mfp, FALSE, 1)) == NULL)	goto error;    if (hp->bh_bnum != 0)    {	EMSG(_("E298: 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);    long_to_char((long)mfp->mf_page_size, b0p->b0_page_size); #ifdef FEAT_SPELL    if (!buf->b_spell)#endif    {	b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;	b0p->b0_flags = get_fileformat(buf) + 1;	set_b0_fname(b0p, buf);	(void)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     * and spell buffer though.     * Only works when there's a swapfile, otherwise it's done when the file     * is created.     */    mf_put(mfp, hp, TRUE, FALSE);    if (!buf->b_help && !B_SPELL(buf))	(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(_("E298: 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(_("E298: 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) */    }    buf->b_ml.ml_mfp = NULL;    return FAIL;} /* * ml_setname() is called when the file name of "buf" has been changed. * It may rename the swap file. */    voidml_setname(buf)    buf_T	*buf;{    int		success = FALSE;    memfile_T	*mfp;    char_u	*fname;    char_u	*dirp;#if defined(MSDOS) || defined(MSWIN)    char_u	*p;#endif     mfp = buf->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 != 0)	    ml_open_file(buf);	    /* 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(buf, &dirp, mfp->mf_fname);						    /* alloc's fname */	if (fname == NULL)	    /* no file name found for this dir */	    continue; #if defined(MSDOS) || defined(MSWIN)	/*	 * 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(MSWIN)	    mfp->mf_ffname = NULL;  /* mf_fname is full pathname already */#else	    mf_set_ffname(mfp);#endif	    ml_upd_block0(buf, FALSE);	    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 = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0);	if (mfp->mf_fd < 0)	{	    /* could not (re)open the swap file, what can we do???? */	    EMSG(_("E301: Oops, lost the swap file!!!"));	    return;	}    }    if (!success)	EMSG(_("E302: Could not rename swap file"));} 

Lines 1–500 of 4861.