xref: /vim-8.2.3635/src/memfile.c (revision f9e3e09f)
1edf3f97aSBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2071d4279SBram Moolenaar  *
3071d4279SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4071d4279SBram Moolenaar  *
5071d4279SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6071d4279SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7071d4279SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8071d4279SBram Moolenaar  */
9071d4279SBram Moolenaar 
10071d4279SBram Moolenaar /*
11071d4279SBram Moolenaar  * memfile.c: Contains the functions for handling blocks of memory which can
12071d4279SBram Moolenaar  * be stored in a file. This is the implementation of a sort of virtual memory.
13071d4279SBram Moolenaar  *
14071d4279SBram Moolenaar  * A memfile consists of a sequence of blocks. The blocks numbered from 0
15071d4279SBram Moolenaar  * upwards have been assigned a place in the actual file. The block number
16071d4279SBram Moolenaar  * is equal to the page number in the file. The
17071d4279SBram Moolenaar  * blocks with negative numbers are currently in memory only. They can be
18071d4279SBram Moolenaar  * assigned a place in the file when too much memory is being used. At that
19071d4279SBram Moolenaar  * moment they get a new, positive, number. A list is used for translation of
20071d4279SBram Moolenaar  * negative to positive numbers.
21071d4279SBram Moolenaar  *
22071d4279SBram Moolenaar  * The size of a block is a multiple of a page size, normally the page size of
23071d4279SBram Moolenaar  * the device the file is on. Most blocks are 1 page long. A Block of multiple
24071d4279SBram Moolenaar  * pages is used for a line that does not fit in a single page.
25071d4279SBram Moolenaar  *
26071d4279SBram Moolenaar  * Each block can be in memory and/or in a file. The block stays in memory
27071d4279SBram Moolenaar  * as long as it is locked. If it is no longer locked it can be swapped out to
28071d4279SBram Moolenaar  * the file. It is only written to the file if it has been changed.
29071d4279SBram Moolenaar  *
30071d4279SBram Moolenaar  * Under normal operation the file is created when opening the memory file and
31071d4279SBram Moolenaar  * deleted when closing the memory file. Only with recovery an existing memory
32071d4279SBram Moolenaar  * file is opened.
33071d4279SBram Moolenaar  */
34071d4279SBram Moolenaar 
35071d4279SBram Moolenaar #include "vim.h"
36071d4279SBram Moolenaar 
37071d4279SBram Moolenaar /*
38071d4279SBram Moolenaar  * Some systems have the page size in statfs.f_bsize, some in stat.st_blksize
39071d4279SBram Moolenaar  */
40071d4279SBram Moolenaar #ifdef HAVE_ST_BLKSIZE
41071d4279SBram Moolenaar # define STATFS stat
42071d4279SBram Moolenaar # define F_BSIZE st_blksize
43071d4279SBram Moolenaar # define fstatfs(fd, buf, len, nul) mch_fstat((fd), (buf))
44071d4279SBram Moolenaar #else
45071d4279SBram Moolenaar # ifdef HAVE_SYS_STATFS_H
46071d4279SBram Moolenaar #  include <sys/statfs.h>
47071d4279SBram Moolenaar #  define STATFS statfs
48071d4279SBram Moolenaar #  define F_BSIZE f_bsize
49071d4279SBram Moolenaar #  ifdef __MINT__		/* do we still need this? */
50071d4279SBram Moolenaar #   define fstatfs(fd, buf, len, nul) mch_fstat((fd), (buf))
51071d4279SBram Moolenaar #  endif
52071d4279SBram Moolenaar # endif
53071d4279SBram Moolenaar #endif
54071d4279SBram Moolenaar 
55071d4279SBram Moolenaar /*
56071d4279SBram Moolenaar  * for Amiga Dos 2.0x we use Flush
57071d4279SBram Moolenaar  */
58071d4279SBram Moolenaar #ifdef AMIGA
59071d4279SBram Moolenaar # ifdef FEAT_ARP
60071d4279SBram Moolenaar extern int dos2;			/* this is in os_amiga.c */
61071d4279SBram Moolenaar # endif
62071d4279SBram Moolenaar # ifdef SASC
63071d4279SBram Moolenaar #  include <proto/dos.h>
64071d4279SBram Moolenaar #  include <ios1.h>			/* for chkufb() */
65071d4279SBram Moolenaar # endif
66071d4279SBram Moolenaar #endif
67071d4279SBram Moolenaar 
68071d4279SBram Moolenaar #define MEMFILE_PAGE_SIZE 4096		/* default page size */
69071d4279SBram Moolenaar 
70071d4279SBram Moolenaar static long_u	total_mem_used = 0;	/* total memory used for memfiles */
71071d4279SBram Moolenaar 
7292b8b2d3SBram Moolenaar static void mf_ins_hash(memfile_T *, bhdr_T *);
7392b8b2d3SBram Moolenaar static void mf_rem_hash(memfile_T *, bhdr_T *);
7492b8b2d3SBram Moolenaar static bhdr_T *mf_find_hash(memfile_T *, blocknr_T);
7592b8b2d3SBram Moolenaar static void mf_ins_used(memfile_T *, bhdr_T *);
7692b8b2d3SBram Moolenaar static void mf_rem_used(memfile_T *, bhdr_T *);
7792b8b2d3SBram Moolenaar static bhdr_T *mf_release(memfile_T *, int);
7892b8b2d3SBram Moolenaar static bhdr_T *mf_alloc_bhdr(memfile_T *, int);
7992b8b2d3SBram Moolenaar static void mf_free_bhdr(bhdr_T *);
8092b8b2d3SBram Moolenaar static void mf_ins_free(memfile_T *, bhdr_T *);
8192b8b2d3SBram Moolenaar static bhdr_T *mf_rem_free(memfile_T *);
8292b8b2d3SBram Moolenaar static int  mf_read(memfile_T *, bhdr_T *);
8392b8b2d3SBram Moolenaar static int  mf_write(memfile_T *, bhdr_T *);
848767f52fSBram Moolenaar static int  mf_write_block(memfile_T *mfp, bhdr_T *hp, off_T offset, unsigned size);
8592b8b2d3SBram Moolenaar static int  mf_trans_add(memfile_T *, bhdr_T *);
8692b8b2d3SBram Moolenaar static void mf_do_open(memfile_T *, char_u *, int);
8792b8b2d3SBram Moolenaar static void mf_hash_init(mf_hashtab_T *);
8892b8b2d3SBram Moolenaar static void mf_hash_free(mf_hashtab_T *);
8992b8b2d3SBram Moolenaar static void mf_hash_free_all(mf_hashtab_T *);
9092b8b2d3SBram Moolenaar static mf_hashitem_T *mf_hash_find(mf_hashtab_T *, blocknr_T);
9192b8b2d3SBram Moolenaar static void mf_hash_add_item(mf_hashtab_T *, mf_hashitem_T *);
9292b8b2d3SBram Moolenaar static void mf_hash_rem_item(mf_hashtab_T *, mf_hashitem_T *);
9392b8b2d3SBram Moolenaar static int mf_hash_grow(mf_hashtab_T *);
94071d4279SBram Moolenaar 
95071d4279SBram Moolenaar /*
96071d4279SBram Moolenaar  * The functions for using a memfile:
97071d4279SBram Moolenaar  *
98071d4279SBram Moolenaar  * mf_open()	    open a new or existing memfile
99071d4279SBram Moolenaar  * mf_open_file()   open a swap file for an existing memfile
100071d4279SBram Moolenaar  * mf_close()	    close (and delete) a memfile
101071d4279SBram Moolenaar  * mf_new()	    create a new block in a memfile and lock it
102071d4279SBram Moolenaar  * mf_get()	    get an existing block and lock it
103071d4279SBram Moolenaar  * mf_put()	    unlock a block, may be marked for writing
104071d4279SBram Moolenaar  * mf_free()	    remove a block
105071d4279SBram Moolenaar  * mf_sync()	    sync changed parts of memfile to disk
106071d4279SBram Moolenaar  * mf_release_all() release as much memory as possible
107071d4279SBram Moolenaar  * mf_trans_del()   may translate negative to positive block number
108071d4279SBram Moolenaar  * mf_fullname()    make file name full path (use before first :cd)
109071d4279SBram Moolenaar  */
110071d4279SBram Moolenaar 
111071d4279SBram Moolenaar /*
112071d4279SBram Moolenaar  * Open an existing or new memory block file.
113071d4279SBram Moolenaar  *
114071d4279SBram Moolenaar  *  fname:	name of file to use (NULL means no file at all)
115071d4279SBram Moolenaar  *		Note: fname must have been allocated, it is not copied!
116071d4279SBram Moolenaar  *			If opening the file fails, fname is freed.
117071d4279SBram Moolenaar  *  flags:	flags for open() call
118071d4279SBram Moolenaar  *
119071d4279SBram Moolenaar  *  If fname != NULL and file cannot be opened, fail.
120071d4279SBram Moolenaar  *
121071d4279SBram Moolenaar  * return value: identifier for this memory block file.
122071d4279SBram Moolenaar  */
123071d4279SBram Moolenaar     memfile_T *
12452ea13daSBram Moolenaar mf_open(char_u *fname, int flags)
125071d4279SBram Moolenaar {
126071d4279SBram Moolenaar     memfile_T		*mfp;
1278767f52fSBram Moolenaar     off_T		size;
128a03dbed9SBram Moolenaar #if defined(STATFS) && defined(UNIX) && !defined(__QNX__) && !defined(__minix)
129071d4279SBram Moolenaar # define USE_FSTATFS
130071d4279SBram Moolenaar     struct STATFS	stf;
131071d4279SBram Moolenaar #endif
132071d4279SBram Moolenaar 
133071d4279SBram Moolenaar     if ((mfp = (memfile_T *)alloc((unsigned)sizeof(memfile_T))) == NULL)
134071d4279SBram Moolenaar 	return NULL;
135071d4279SBram Moolenaar 
136071d4279SBram Moolenaar     if (fname == NULL)	    /* no file for this memfile, use memory only */
137071d4279SBram Moolenaar     {
138071d4279SBram Moolenaar 	mfp->mf_fname = NULL;
139071d4279SBram Moolenaar 	mfp->mf_ffname = NULL;
140071d4279SBram Moolenaar 	mfp->mf_fd = -1;
141071d4279SBram Moolenaar     }
142071d4279SBram Moolenaar     else
143071d4279SBram Moolenaar     {
144071d4279SBram Moolenaar 	mf_do_open(mfp, fname, flags);	/* try to open the file */
145071d4279SBram Moolenaar 
146071d4279SBram Moolenaar 	/* if the file cannot be opened, return here */
147071d4279SBram Moolenaar 	if (mfp->mf_fd < 0)
148071d4279SBram Moolenaar 	{
149071d4279SBram Moolenaar 	    vim_free(mfp);
150071d4279SBram Moolenaar 	    return NULL;
151071d4279SBram Moolenaar 	}
152071d4279SBram Moolenaar     }
153071d4279SBram Moolenaar 
154071d4279SBram Moolenaar     mfp->mf_free_first = NULL;		/* free list is empty */
155071d4279SBram Moolenaar     mfp->mf_used_first = NULL;		/* used list is empty */
156071d4279SBram Moolenaar     mfp->mf_used_last = NULL;
157071d4279SBram Moolenaar     mfp->mf_dirty = FALSE;
158071d4279SBram Moolenaar     mfp->mf_used_count = 0;
159b05b10a3SBram Moolenaar     mf_hash_init(&mfp->mf_hash);
160b05b10a3SBram Moolenaar     mf_hash_init(&mfp->mf_trans);
161071d4279SBram Moolenaar     mfp->mf_page_size = MEMFILE_PAGE_SIZE;
162a8ffcbbfSBram Moolenaar #ifdef FEAT_CRYPT
163a8ffcbbfSBram Moolenaar     mfp->mf_old_key = NULL;
164a8ffcbbfSBram Moolenaar #endif
165071d4279SBram Moolenaar 
166071d4279SBram Moolenaar #ifdef USE_FSTATFS
167071d4279SBram Moolenaar     /*
168071d4279SBram Moolenaar      * Try to set the page size equal to the block size of the device.
169071d4279SBram Moolenaar      * Speeds up I/O a lot.
170071d4279SBram Moolenaar      * When recovering, the actual block size will be retrieved from block 0
171071d4279SBram Moolenaar      * in ml_recover().  The size used here may be wrong, therefore
172071d4279SBram Moolenaar      * mf_blocknr_max must be rounded up.
173071d4279SBram Moolenaar      */
174071d4279SBram Moolenaar     if (mfp->mf_fd >= 0
175071d4279SBram Moolenaar 	    && fstatfs(mfp->mf_fd, &stf, sizeof(struct statfs), 0) == 0
176071d4279SBram Moolenaar 	    && stf.F_BSIZE >= MIN_SWAP_PAGE_SIZE
177071d4279SBram Moolenaar 	    && stf.F_BSIZE <= MAX_SWAP_PAGE_SIZE)
178071d4279SBram Moolenaar 	mfp->mf_page_size = stf.F_BSIZE;
179071d4279SBram Moolenaar #endif
180071d4279SBram Moolenaar 
181071d4279SBram Moolenaar     if (mfp->mf_fd < 0 || (flags & (O_TRUNC|O_EXCL))
1828767f52fSBram Moolenaar 		  || (size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
183071d4279SBram Moolenaar 	mfp->mf_blocknr_max = 0;	/* no file or empty file */
184071d4279SBram Moolenaar     else
185071d4279SBram Moolenaar 	mfp->mf_blocknr_max = (blocknr_T)((size + mfp->mf_page_size - 1)
186071d4279SBram Moolenaar 							 / mfp->mf_page_size);
187071d4279SBram Moolenaar     mfp->mf_blocknr_min = -1;
188071d4279SBram Moolenaar     mfp->mf_neg_count = 0;
189071d4279SBram Moolenaar     mfp->mf_infile_count = mfp->mf_blocknr_max;
190fe265ff5SBram Moolenaar 
191fe265ff5SBram Moolenaar     /*
192fe265ff5SBram Moolenaar      * Compute maximum number of pages ('maxmem' is in Kbyte):
193fe265ff5SBram Moolenaar      *	'mammem' * 1Kbyte / page-size-in-bytes.
194fe265ff5SBram Moolenaar      * Avoid overflow by first reducing page size as much as possible.
195fe265ff5SBram Moolenaar      */
196fe265ff5SBram Moolenaar     {
197fe265ff5SBram Moolenaar 	int	    shift = 10;
198fe265ff5SBram Moolenaar 	unsigned    page_size = mfp->mf_page_size;
199fe265ff5SBram Moolenaar 
200fe265ff5SBram Moolenaar 	while (shift > 0 && (page_size & 1) == 0)
201fe265ff5SBram Moolenaar 	{
202fe265ff5SBram Moolenaar 	    page_size = page_size >> 1;
203fe265ff5SBram Moolenaar 	    --shift;
204fe265ff5SBram Moolenaar 	}
205fe265ff5SBram Moolenaar 	mfp->mf_used_count_max = (p_mm << shift) / page_size;
206fe265ff5SBram Moolenaar 	if (mfp->mf_used_count_max < 10)
207fe265ff5SBram Moolenaar 	    mfp->mf_used_count_max = 10;
208fe265ff5SBram Moolenaar     }
209071d4279SBram Moolenaar 
210071d4279SBram Moolenaar     return mfp;
211071d4279SBram Moolenaar }
212071d4279SBram Moolenaar 
213071d4279SBram Moolenaar /*
214071d4279SBram Moolenaar  * Open a file for an existing memfile.  Used when updatecount set from 0 to
215071d4279SBram Moolenaar  * some value.
216071d4279SBram Moolenaar  * If the file already exists, this fails.
217071d4279SBram Moolenaar  * "fname" is the name of file to use (NULL means no file at all)
218071d4279SBram Moolenaar  * Note: "fname" must have been allocated, it is not copied!  If opening the
219071d4279SBram Moolenaar  * file fails, "fname" is freed.
220071d4279SBram Moolenaar  *
221071d4279SBram Moolenaar  * return value: FAIL if file could not be opened, OK otherwise
222071d4279SBram Moolenaar  */
223071d4279SBram Moolenaar     int
22452ea13daSBram Moolenaar mf_open_file(memfile_T *mfp, char_u *fname)
225071d4279SBram Moolenaar {
226071d4279SBram Moolenaar     mf_do_open(mfp, fname, O_RDWR|O_CREAT|O_EXCL); /* try to open the file */
227071d4279SBram Moolenaar 
228071d4279SBram Moolenaar     if (mfp->mf_fd < 0)
229071d4279SBram Moolenaar 	return FAIL;
230071d4279SBram Moolenaar 
231071d4279SBram Moolenaar     mfp->mf_dirty = TRUE;
232071d4279SBram Moolenaar     return OK;
233071d4279SBram Moolenaar }
234071d4279SBram Moolenaar 
235071d4279SBram Moolenaar /*
236945e2dbbSBram Moolenaar  * Close a memory file and delete the associated file if 'del_file' is TRUE.
237071d4279SBram Moolenaar  */
238071d4279SBram Moolenaar     void
23952ea13daSBram Moolenaar mf_close(memfile_T *mfp, int del_file)
240071d4279SBram Moolenaar {
241071d4279SBram Moolenaar     bhdr_T	*hp, *nextp;
242071d4279SBram Moolenaar 
243071d4279SBram Moolenaar     if (mfp == NULL)		    /* safety check */
244071d4279SBram Moolenaar 	return;
245071d4279SBram Moolenaar     if (mfp->mf_fd >= 0)
246071d4279SBram Moolenaar     {
247071d4279SBram Moolenaar 	if (close(mfp->mf_fd) < 0)
248*f9e3e09fSBram Moolenaar 	    emsg(_(e_swapclose));
249071d4279SBram Moolenaar     }
250071d4279SBram Moolenaar     if (del_file && mfp->mf_fname != NULL)
251071d4279SBram Moolenaar 	mch_remove(mfp->mf_fname);
252071d4279SBram Moolenaar 					    /* free entries in used list */
253071d4279SBram Moolenaar     for (hp = mfp->mf_used_first; hp != NULL; hp = nextp)
254071d4279SBram Moolenaar     {
255071d4279SBram Moolenaar 	total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
256071d4279SBram Moolenaar 	nextp = hp->bh_next;
257071d4279SBram Moolenaar 	mf_free_bhdr(hp);
258071d4279SBram Moolenaar     }
259071d4279SBram Moolenaar     while (mfp->mf_free_first != NULL)	    /* free entries in free list */
260071d4279SBram Moolenaar 	vim_free(mf_rem_free(mfp));
261b05b10a3SBram Moolenaar     mf_hash_free(&mfp->mf_hash);
262b05b10a3SBram Moolenaar     mf_hash_free_all(&mfp->mf_trans);	    /* free hashtable and its items */
263071d4279SBram Moolenaar     vim_free(mfp->mf_fname);
264071d4279SBram Moolenaar     vim_free(mfp->mf_ffname);
265071d4279SBram Moolenaar     vim_free(mfp);
266071d4279SBram Moolenaar }
267071d4279SBram Moolenaar 
268071d4279SBram Moolenaar /*
269071d4279SBram Moolenaar  * Close the swap file for a memfile.  Used when 'swapfile' is reset.
270071d4279SBram Moolenaar  */
271071d4279SBram Moolenaar     void
27252ea13daSBram Moolenaar mf_close_file(
27352ea13daSBram Moolenaar     buf_T	*buf,
27452ea13daSBram Moolenaar     int		getlines)	/* get all lines into memory? */
275071d4279SBram Moolenaar {
276071d4279SBram Moolenaar     memfile_T	*mfp;
277071d4279SBram Moolenaar     linenr_T	lnum;
278071d4279SBram Moolenaar 
279071d4279SBram Moolenaar     mfp = buf->b_ml.ml_mfp;
280071d4279SBram Moolenaar     if (mfp == NULL || mfp->mf_fd < 0)		/* nothing to close */
281071d4279SBram Moolenaar 	return;
282071d4279SBram Moolenaar 
283071d4279SBram Moolenaar     if (getlines)
284071d4279SBram Moolenaar     {
285071d4279SBram Moolenaar 	/* get all blocks in memory by accessing all lines (clumsy!) */
28647b8b15bSBram Moolenaar 	mf_dont_release = TRUE;
287071d4279SBram Moolenaar 	for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum)
288071d4279SBram Moolenaar 	    (void)ml_get_buf(buf, lnum, FALSE);
28947b8b15bSBram Moolenaar 	mf_dont_release = FALSE;
290071d4279SBram Moolenaar 	/* TODO: should check if all blocks are really in core */
291071d4279SBram Moolenaar     }
292071d4279SBram Moolenaar 
293071d4279SBram Moolenaar     if (close(mfp->mf_fd) < 0)			/* close the file */
294*f9e3e09fSBram Moolenaar 	emsg(_(e_swapclose));
295071d4279SBram Moolenaar     mfp->mf_fd = -1;
296071d4279SBram Moolenaar 
297071d4279SBram Moolenaar     if (mfp->mf_fname != NULL)
298071d4279SBram Moolenaar     {
299071d4279SBram Moolenaar 	mch_remove(mfp->mf_fname);		/* delete the swap file */
300d23a8236SBram Moolenaar 	VIM_CLEAR(mfp->mf_fname);
301d23a8236SBram Moolenaar 	VIM_CLEAR(mfp->mf_ffname);
302071d4279SBram Moolenaar     }
303071d4279SBram Moolenaar }
304071d4279SBram Moolenaar 
305071d4279SBram Moolenaar /*
306071d4279SBram Moolenaar  * Set new size for a memfile.  Used when block 0 of a swapfile has been read
307071d4279SBram Moolenaar  * and the size it indicates differs from what was guessed.
308071d4279SBram Moolenaar  */
309071d4279SBram Moolenaar     void
31052ea13daSBram Moolenaar mf_new_page_size(memfile_T *mfp, unsigned new_size)
311071d4279SBram Moolenaar {
312071d4279SBram Moolenaar     /* Correct the memory used for block 0 to the new size, because it will be
313071d4279SBram Moolenaar      * freed with that size later on. */
314071d4279SBram Moolenaar     total_mem_used += new_size - mfp->mf_page_size;
315071d4279SBram Moolenaar     mfp->mf_page_size = new_size;
316071d4279SBram Moolenaar }
317071d4279SBram Moolenaar 
318071d4279SBram Moolenaar /*
319071d4279SBram Moolenaar  * get a new block
320071d4279SBram Moolenaar  *
321071d4279SBram Moolenaar  *   negative: TRUE if negative block number desired (data block)
322071d4279SBram Moolenaar  */
323071d4279SBram Moolenaar     bhdr_T *
32452ea13daSBram Moolenaar mf_new(memfile_T *mfp, int negative, int page_count)
325071d4279SBram Moolenaar {
326071d4279SBram Moolenaar     bhdr_T	*hp;	/* new bhdr_T */
327071d4279SBram Moolenaar     bhdr_T	*freep;	/* first block in free list */
328071d4279SBram Moolenaar     char_u	*p;
329071d4279SBram Moolenaar 
330071d4279SBram Moolenaar     /*
331071d4279SBram Moolenaar      * If we reached the maximum size for the used memory blocks, release one
332071d4279SBram Moolenaar      * If a bhdr_T is returned, use it and adjust the page_count if necessary.
333071d4279SBram Moolenaar      */
334071d4279SBram Moolenaar     hp = mf_release(mfp, page_count);
335071d4279SBram Moolenaar 
336071d4279SBram Moolenaar /*
337071d4279SBram Moolenaar  * Decide on the number to use:
338071d4279SBram Moolenaar  * If there is a free block, use its number.
339071d4279SBram Moolenaar  * Otherwise use mf_block_min for a negative number, mf_block_max for
340071d4279SBram Moolenaar  * a positive number.
341071d4279SBram Moolenaar  */
342071d4279SBram Moolenaar     freep = mfp->mf_free_first;
343071d4279SBram Moolenaar     if (!negative && freep != NULL && freep->bh_page_count >= page_count)
344071d4279SBram Moolenaar     {
345071d4279SBram Moolenaar 	/*
346071d4279SBram Moolenaar 	 * If the block in the free list has more pages, take only the number
347071d4279SBram Moolenaar 	 * of pages needed and allocate a new bhdr_T with data
348071d4279SBram Moolenaar 	 *
34917e79197SBram Moolenaar 	 * If the number of pages matches and mf_release() did not return a
35017e79197SBram Moolenaar 	 * bhdr_T, use the bhdr_T from the free list and allocate the data
351071d4279SBram Moolenaar 	 *
35217e79197SBram Moolenaar 	 * If the number of pages matches and mf_release() returned a bhdr_T,
353071d4279SBram Moolenaar 	 * just use the number and free the bhdr_T from the free list
354071d4279SBram Moolenaar 	 */
355071d4279SBram Moolenaar 	if (freep->bh_page_count > page_count)
356071d4279SBram Moolenaar 	{
357071d4279SBram Moolenaar 	    if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
358071d4279SBram Moolenaar 		return NULL;
359071d4279SBram Moolenaar 	    hp->bh_bnum = freep->bh_bnum;
360071d4279SBram Moolenaar 	    freep->bh_bnum += page_count;
361071d4279SBram Moolenaar 	    freep->bh_page_count -= page_count;
362071d4279SBram Moolenaar 	}
363071d4279SBram Moolenaar 	else if (hp == NULL)	    /* need to allocate memory for this block */
364071d4279SBram Moolenaar 	{
365071d4279SBram Moolenaar 	    if ((p = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
366071d4279SBram Moolenaar 		return NULL;
367071d4279SBram Moolenaar 	    hp = mf_rem_free(mfp);
368071d4279SBram Moolenaar 	    hp->bh_data = p;
369071d4279SBram Moolenaar 	}
370071d4279SBram Moolenaar 	else		    /* use the number, remove entry from free list */
371071d4279SBram Moolenaar 	{
372071d4279SBram Moolenaar 	    freep = mf_rem_free(mfp);
373071d4279SBram Moolenaar 	    hp->bh_bnum = freep->bh_bnum;
374071d4279SBram Moolenaar 	    vim_free(freep);
375071d4279SBram Moolenaar 	}
376071d4279SBram Moolenaar     }
377071d4279SBram Moolenaar     else	/* get a new number */
378071d4279SBram Moolenaar     {
379071d4279SBram Moolenaar 	if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
380071d4279SBram Moolenaar 	    return NULL;
381071d4279SBram Moolenaar 	if (negative)
382071d4279SBram Moolenaar 	{
383071d4279SBram Moolenaar 	    hp->bh_bnum = mfp->mf_blocknr_min--;
384071d4279SBram Moolenaar 	    mfp->mf_neg_count++;
385071d4279SBram Moolenaar 	}
386071d4279SBram Moolenaar 	else
387071d4279SBram Moolenaar 	{
388071d4279SBram Moolenaar 	    hp->bh_bnum = mfp->mf_blocknr_max;
389071d4279SBram Moolenaar 	    mfp->mf_blocknr_max += page_count;
390071d4279SBram Moolenaar 	}
391071d4279SBram Moolenaar     }
392071d4279SBram Moolenaar     hp->bh_flags = BH_LOCKED | BH_DIRTY;	/* new block is always dirty */
393071d4279SBram Moolenaar     mfp->mf_dirty = TRUE;
394071d4279SBram Moolenaar     hp->bh_page_count = page_count;
395071d4279SBram Moolenaar     mf_ins_used(mfp, hp);
396071d4279SBram Moolenaar     mf_ins_hash(mfp, hp);
397071d4279SBram Moolenaar 
398071d4279SBram Moolenaar     /*
399071d4279SBram Moolenaar      * Init the data to all zero, to avoid reading uninitialized data.
400071d4279SBram Moolenaar      * This also avoids that the passwd file ends up in the swap file!
401071d4279SBram Moolenaar      */
402945e2dbbSBram Moolenaar     (void)vim_memset((char *)(hp->bh_data), 0,
403945e2dbbSBram Moolenaar 				      (size_t)mfp->mf_page_size * page_count);
404071d4279SBram Moolenaar 
405071d4279SBram Moolenaar     return hp;
406071d4279SBram Moolenaar }
407071d4279SBram Moolenaar 
408071d4279SBram Moolenaar /*
409a8ffcbbfSBram Moolenaar  * Get existing block "nr" with "page_count" pages.
410071d4279SBram Moolenaar  *
411071d4279SBram Moolenaar  * Note: The caller should first check a negative nr with mf_trans_del()
412071d4279SBram Moolenaar  */
413071d4279SBram Moolenaar     bhdr_T *
41452ea13daSBram Moolenaar mf_get(memfile_T *mfp, blocknr_T nr, int page_count)
415071d4279SBram Moolenaar {
416071d4279SBram Moolenaar     bhdr_T    *hp;
417071d4279SBram Moolenaar 						/* doesn't exist */
418071d4279SBram Moolenaar     if (nr >= mfp->mf_blocknr_max || nr <= mfp->mf_blocknr_min)
419071d4279SBram Moolenaar 	return NULL;
420071d4279SBram Moolenaar 
421071d4279SBram Moolenaar     /*
422071d4279SBram Moolenaar      * see if it is in the cache
423071d4279SBram Moolenaar      */
424071d4279SBram Moolenaar     hp = mf_find_hash(mfp, nr);
425071d4279SBram Moolenaar     if (hp == NULL)	/* not in the hash list */
426071d4279SBram Moolenaar     {
427071d4279SBram Moolenaar 	if (nr < 0 || nr >= mfp->mf_infile_count)   /* can't be in the file */
428071d4279SBram Moolenaar 	    return NULL;
429071d4279SBram Moolenaar 
430071d4279SBram Moolenaar 	/* could check here if the block is in the free list */
431071d4279SBram Moolenaar 
432071d4279SBram Moolenaar 	/*
433071d4279SBram Moolenaar 	 * Check if we need to flush an existing block.
434071d4279SBram Moolenaar 	 * If so, use that block.
435071d4279SBram Moolenaar 	 * If not, allocate a new block.
436071d4279SBram Moolenaar 	 */
437071d4279SBram Moolenaar 	hp = mf_release(mfp, page_count);
438071d4279SBram Moolenaar 	if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
439071d4279SBram Moolenaar 	    return NULL;
440071d4279SBram Moolenaar 
441071d4279SBram Moolenaar 	hp->bh_bnum = nr;
442071d4279SBram Moolenaar 	hp->bh_flags = 0;
443071d4279SBram Moolenaar 	hp->bh_page_count = page_count;
444071d4279SBram Moolenaar 	if (mf_read(mfp, hp) == FAIL)	    /* cannot read the block! */
445071d4279SBram Moolenaar 	{
446071d4279SBram Moolenaar 	    mf_free_bhdr(hp);
447071d4279SBram Moolenaar 	    return NULL;
448071d4279SBram Moolenaar 	}
449071d4279SBram Moolenaar     }
450071d4279SBram Moolenaar     else
451071d4279SBram Moolenaar     {
452071d4279SBram Moolenaar 	mf_rem_used(mfp, hp);	/* remove from list, insert in front below */
453071d4279SBram Moolenaar 	mf_rem_hash(mfp, hp);
454071d4279SBram Moolenaar     }
455071d4279SBram Moolenaar 
456071d4279SBram Moolenaar     hp->bh_flags |= BH_LOCKED;
457071d4279SBram Moolenaar     mf_ins_used(mfp, hp);	/* put in front of used list */
458071d4279SBram Moolenaar     mf_ins_hash(mfp, hp);	/* put in front of hash list */
459071d4279SBram Moolenaar 
460071d4279SBram Moolenaar     return hp;
461071d4279SBram Moolenaar }
462071d4279SBram Moolenaar 
463071d4279SBram Moolenaar /*
464071d4279SBram Moolenaar  * release the block *hp
465071d4279SBram Moolenaar  *
466071d4279SBram Moolenaar  *   dirty: Block must be written to file later
467071d4279SBram Moolenaar  *   infile: Block should be in file (needed for recovery)
468071d4279SBram Moolenaar  *
469071d4279SBram Moolenaar  *  no return value, function cannot fail
470071d4279SBram Moolenaar  */
471071d4279SBram Moolenaar     void
47252ea13daSBram Moolenaar mf_put(
47352ea13daSBram Moolenaar     memfile_T	*mfp,
47452ea13daSBram Moolenaar     bhdr_T	*hp,
47552ea13daSBram Moolenaar     int		dirty,
47652ea13daSBram Moolenaar     int		infile)
477071d4279SBram Moolenaar {
478071d4279SBram Moolenaar     int		flags;
479071d4279SBram Moolenaar 
480071d4279SBram Moolenaar     flags = hp->bh_flags;
481071d4279SBram Moolenaar 
482071d4279SBram Moolenaar     if ((flags & BH_LOCKED) == 0)
483*f9e3e09fSBram Moolenaar 	iemsg(_("E293: block was not locked"));
484071d4279SBram Moolenaar     flags &= ~BH_LOCKED;
485071d4279SBram Moolenaar     if (dirty)
486071d4279SBram Moolenaar     {
487071d4279SBram Moolenaar 	flags |= BH_DIRTY;
488071d4279SBram Moolenaar 	mfp->mf_dirty = TRUE;
489071d4279SBram Moolenaar     }
490071d4279SBram Moolenaar     hp->bh_flags = flags;
491071d4279SBram Moolenaar     if (infile)
492071d4279SBram Moolenaar 	mf_trans_add(mfp, hp);	    /* may translate negative in positive nr */
493071d4279SBram Moolenaar }
494071d4279SBram Moolenaar 
495071d4279SBram Moolenaar /*
496071d4279SBram Moolenaar  * block *hp is no longer in used, may put it in the free list of memfile *mfp
497071d4279SBram Moolenaar  */
498071d4279SBram Moolenaar     void
49952ea13daSBram Moolenaar mf_free(memfile_T *mfp, bhdr_T *hp)
500071d4279SBram Moolenaar {
501071d4279SBram Moolenaar     vim_free(hp->bh_data);	/* free the memory */
502071d4279SBram Moolenaar     mf_rem_hash(mfp, hp);	/* get *hp out of the hash list */
503071d4279SBram Moolenaar     mf_rem_used(mfp, hp);	/* get *hp out of the used list */
504071d4279SBram Moolenaar     if (hp->bh_bnum < 0)
505071d4279SBram Moolenaar     {
506071d4279SBram Moolenaar 	vim_free(hp);		/* don't want negative numbers in free list */
507071d4279SBram Moolenaar 	mfp->mf_neg_count--;
508071d4279SBram Moolenaar     }
509071d4279SBram Moolenaar     else
510071d4279SBram Moolenaar 	mf_ins_free(mfp, hp);	/* put *hp in the free list */
511071d4279SBram Moolenaar }
512071d4279SBram Moolenaar 
51389f3727bSBram Moolenaar #if defined(__MORPHOS__) && defined(__libnix__)
514071d4279SBram Moolenaar /* function is missing in MorphOS libnix version */
515071d4279SBram Moolenaar extern unsigned long *__stdfiledes;
516071d4279SBram Moolenaar 
517071d4279SBram Moolenaar     static unsigned long
518071d4279SBram Moolenaar fdtofh(int filedescriptor)
519071d4279SBram Moolenaar {
520071d4279SBram Moolenaar     return __stdfiledes[filedescriptor];
521071d4279SBram Moolenaar }
522071d4279SBram Moolenaar #endif
523071d4279SBram Moolenaar 
524071d4279SBram Moolenaar /*
525071d4279SBram Moolenaar  * Sync the memory file *mfp to disk.
526071d4279SBram Moolenaar  * Flags:
527071d4279SBram Moolenaar  *  MFS_ALL	If not given, blocks with negative numbers are not synced,
528071d4279SBram Moolenaar  *		even when they are dirty!
529071d4279SBram Moolenaar  *  MFS_STOP	Stop syncing when a character becomes available, but sync at
530071d4279SBram Moolenaar  *		least one block.
531071d4279SBram Moolenaar  *  MFS_FLUSH	Make sure buffers are flushed to disk, so they will survive a
532071d4279SBram Moolenaar  *		system crash.
533071d4279SBram Moolenaar  *  MFS_ZERO	Only write block 0.
534071d4279SBram Moolenaar  *
535071d4279SBram Moolenaar  * Return FAIL for failure, OK otherwise
536071d4279SBram Moolenaar  */
537071d4279SBram Moolenaar     int
53852ea13daSBram Moolenaar mf_sync(memfile_T *mfp, int flags)
539071d4279SBram Moolenaar {
540071d4279SBram Moolenaar     int		status;
541071d4279SBram Moolenaar     bhdr_T	*hp;
542071d4279SBram Moolenaar     int		got_int_save = got_int;
543071d4279SBram Moolenaar 
544071d4279SBram Moolenaar     if (mfp->mf_fd < 0)	    /* there is no file, nothing to do */
545071d4279SBram Moolenaar     {
546071d4279SBram Moolenaar 	mfp->mf_dirty = FALSE;
547071d4279SBram Moolenaar 	return FAIL;
548071d4279SBram Moolenaar     }
549071d4279SBram Moolenaar 
550071d4279SBram Moolenaar     /* Only a CTRL-C while writing will break us here, not one typed
551071d4279SBram Moolenaar      * previously. */
552071d4279SBram Moolenaar     got_int = FALSE;
553071d4279SBram Moolenaar 
554071d4279SBram Moolenaar     /*
555071d4279SBram Moolenaar      * sync from last to first (may reduce the probability of an inconsistent
556071d4279SBram Moolenaar      * file) If a write fails, it is very likely caused by a full filesystem.
557071d4279SBram Moolenaar      * Then we only try to write blocks within the existing file. If that also
558071d4279SBram Moolenaar      * fails then we give up.
559071d4279SBram Moolenaar      */
560071d4279SBram Moolenaar     status = OK;
561071d4279SBram Moolenaar     for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
562071d4279SBram Moolenaar 	if (((flags & MFS_ALL) || hp->bh_bnum >= 0)
563071d4279SBram Moolenaar 		&& (hp->bh_flags & BH_DIRTY)
564071d4279SBram Moolenaar 		&& (status == OK || (hp->bh_bnum >= 0
565071d4279SBram Moolenaar 		    && hp->bh_bnum < mfp->mf_infile_count)))
566071d4279SBram Moolenaar 	{
567071d4279SBram Moolenaar 	    if ((flags & MFS_ZERO) && hp->bh_bnum != 0)
568071d4279SBram Moolenaar 		continue;
569071d4279SBram Moolenaar 	    if (mf_write(mfp, hp) == FAIL)
570071d4279SBram Moolenaar 	    {
571071d4279SBram Moolenaar 		if (status == FAIL)	/* double error: quit syncing */
572071d4279SBram Moolenaar 		    break;
573071d4279SBram Moolenaar 		status = FAIL;
574071d4279SBram Moolenaar 	    }
575071d4279SBram Moolenaar 	    if (flags & MFS_STOP)
576071d4279SBram Moolenaar 	    {
577071d4279SBram Moolenaar 		/* Stop when char available now. */
578071d4279SBram Moolenaar 		if (ui_char_avail())
579071d4279SBram Moolenaar 		    break;
580071d4279SBram Moolenaar 	    }
581071d4279SBram Moolenaar 	    else
582071d4279SBram Moolenaar 		ui_breakcheck();
583071d4279SBram Moolenaar 	    if (got_int)
584071d4279SBram Moolenaar 		break;
585071d4279SBram Moolenaar 	}
586071d4279SBram Moolenaar 
587071d4279SBram Moolenaar     /*
588071d4279SBram Moolenaar      * If the whole list is flushed, the memfile is not dirty anymore.
589071d4279SBram Moolenaar      * In case of an error this flag is also set, to avoid trying all the time.
590071d4279SBram Moolenaar      */
591071d4279SBram Moolenaar     if (hp == NULL || status == FAIL)
592071d4279SBram Moolenaar 	mfp->mf_dirty = FALSE;
593071d4279SBram Moolenaar 
594071d4279SBram Moolenaar     if ((flags & MFS_FLUSH) && *p_sws != NUL)
595071d4279SBram Moolenaar     {
596071d4279SBram Moolenaar #if defined(UNIX)
597071d4279SBram Moolenaar # ifdef HAVE_FSYNC
598071d4279SBram Moolenaar 	/*
599071d4279SBram Moolenaar 	 * most Unixes have the very useful fsync() function, just what we need.
600071d4279SBram Moolenaar 	 */
601071d4279SBram Moolenaar 	if (STRCMP(p_sws, "fsync") == 0)
602071d4279SBram Moolenaar 	{
603071d4279SBram Moolenaar 	    if (fsync(mfp->mf_fd))
604071d4279SBram Moolenaar 		status = FAIL;
605071d4279SBram Moolenaar 	}
606071d4279SBram Moolenaar 	else
607071d4279SBram Moolenaar # endif
608071d4279SBram Moolenaar 	    /* OpenNT is strictly POSIX (Benzinger) */
609071d4279SBram Moolenaar 	    /* Tandem/Himalaya NSK-OSS doesn't have sync() */
610ba17ed67SBram Moolenaar 	    /* No sync() on Stratus VOS */
611ba17ed67SBram Moolenaar # if defined(__OPENNT) || defined(__TANDEM) || defined(__VOS__)
612071d4279SBram Moolenaar 	    fflush(NULL);
613071d4279SBram Moolenaar # else
614071d4279SBram Moolenaar 	    sync();
615071d4279SBram Moolenaar # endif
616071d4279SBram Moolenaar #endif
617071d4279SBram Moolenaar #ifdef VMS
618071d4279SBram Moolenaar 	if (STRCMP(p_sws, "fsync") == 0)
619071d4279SBram Moolenaar 	{
620071d4279SBram Moolenaar 	    if (fsync(mfp->mf_fd))
621071d4279SBram Moolenaar 		status = FAIL;
622071d4279SBram Moolenaar 	}
623071d4279SBram Moolenaar #endif
6240bd40517SBram Moolenaar #ifdef WIN32
6250bd40517SBram Moolenaar 	if (_commit(mfp->mf_fd))
6260bd40517SBram Moolenaar 	    status = FAIL;
627071d4279SBram Moolenaar #endif
628071d4279SBram Moolenaar #ifdef AMIGA
6295a6404cfSBram Moolenaar # if defined(__AROS__) || defined(__amigaos4__)
630071d4279SBram Moolenaar 	if (fsync(mfp->mf_fd) != 0)
631071d4279SBram Moolenaar 	    status = FAIL;
632071d4279SBram Moolenaar # else
633071d4279SBram Moolenaar 	/*
634071d4279SBram Moolenaar 	 * Flush() only exists for AmigaDos 2.0.
635071d4279SBram Moolenaar 	 * For 1.3 it should be done with close() + open(), but then the risk
636071d4279SBram Moolenaar 	 * is that the open() may fail and lose the file....
637071d4279SBram Moolenaar 	 */
638071d4279SBram Moolenaar #  ifdef FEAT_ARP
639071d4279SBram Moolenaar 	if (dos2)
640071d4279SBram Moolenaar #  endif
641071d4279SBram Moolenaar #  ifdef SASC
642071d4279SBram Moolenaar 	{
643071d4279SBram Moolenaar 	    struct UFB *fp = chkufb(mfp->mf_fd);
644071d4279SBram Moolenaar 
645071d4279SBram Moolenaar 	    if (fp != NULL)
646071d4279SBram Moolenaar 		Flush(fp->ufbfh);
647071d4279SBram Moolenaar 	}
648071d4279SBram Moolenaar #  else
649071d4279SBram Moolenaar #   if defined(_DCC) || defined(__GNUC__) || defined(__MORPHOS__)
650071d4279SBram Moolenaar 	{
65189f3727bSBram Moolenaar #    if defined(__GNUC__) && !defined(__MORPHOS__) && defined(__libnix__)
652071d4279SBram Moolenaar 	    /* Have function (in libnix at least),
653071d4279SBram Moolenaar 	     * but ain't got no prototype anywhere. */
654071d4279SBram Moolenaar 	    extern unsigned long fdtofh(int filedescriptor);
655071d4279SBram Moolenaar #    endif
65689f3727bSBram Moolenaar #    if !defined(__libnix__)
65789f3727bSBram Moolenaar 	    fflush(NULL);
65889f3727bSBram Moolenaar #    else
659071d4279SBram Moolenaar 	    BPTR fh = (BPTR)fdtofh(mfp->mf_fd);
660071d4279SBram Moolenaar 
661071d4279SBram Moolenaar 	    if (fh != 0)
662071d4279SBram Moolenaar 		Flush(fh);
66389f3727bSBram Moolenaar #    endif
664071d4279SBram Moolenaar 	}
665071d4279SBram Moolenaar #   else /* assume Manx */
666071d4279SBram Moolenaar 	    Flush(_devtab[mfp->mf_fd].fd);
667071d4279SBram Moolenaar #   endif
668071d4279SBram Moolenaar #  endif
669071d4279SBram Moolenaar # endif
670071d4279SBram Moolenaar #endif /* AMIGA */
671071d4279SBram Moolenaar     }
672071d4279SBram Moolenaar 
673071d4279SBram Moolenaar     got_int |= got_int_save;
674071d4279SBram Moolenaar 
675071d4279SBram Moolenaar     return status;
676071d4279SBram Moolenaar }
677071d4279SBram Moolenaar 
678071d4279SBram Moolenaar /*
679c32840f2SBram Moolenaar  * For all blocks in memory file *mfp that have a positive block number set
680c32840f2SBram Moolenaar  * the dirty flag.  These are blocks that need to be written to a newly
681c32840f2SBram Moolenaar  * created swapfile.
682c32840f2SBram Moolenaar  */
683c32840f2SBram Moolenaar     void
68452ea13daSBram Moolenaar mf_set_dirty(memfile_T *mfp)
685c32840f2SBram Moolenaar {
686c32840f2SBram Moolenaar     bhdr_T	*hp;
687c32840f2SBram Moolenaar 
688c32840f2SBram Moolenaar     for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
689c32840f2SBram Moolenaar 	if (hp->bh_bnum > 0)
690c32840f2SBram Moolenaar 	    hp->bh_flags |= BH_DIRTY;
691c32840f2SBram Moolenaar     mfp->mf_dirty = TRUE;
692c32840f2SBram Moolenaar }
693c32840f2SBram Moolenaar 
694c32840f2SBram Moolenaar /*
695071d4279SBram Moolenaar  * insert block *hp in front of hashlist of memfile *mfp
696071d4279SBram Moolenaar  */
697071d4279SBram Moolenaar     static void
69852ea13daSBram Moolenaar mf_ins_hash(memfile_T *mfp, bhdr_T *hp)
699071d4279SBram Moolenaar {
700b05b10a3SBram Moolenaar     mf_hash_add_item(&mfp->mf_hash, (mf_hashitem_T *)hp);
701071d4279SBram Moolenaar }
702071d4279SBram Moolenaar 
703071d4279SBram Moolenaar /*
704071d4279SBram Moolenaar  * remove block *hp from hashlist of memfile list *mfp
705071d4279SBram Moolenaar  */
706071d4279SBram Moolenaar     static void
70752ea13daSBram Moolenaar mf_rem_hash(memfile_T *mfp, bhdr_T *hp)
708071d4279SBram Moolenaar {
709b05b10a3SBram Moolenaar     mf_hash_rem_item(&mfp->mf_hash, (mf_hashitem_T *)hp);
710071d4279SBram Moolenaar }
711071d4279SBram Moolenaar 
712071d4279SBram Moolenaar /*
713071d4279SBram Moolenaar  * look in hash lists of memfile *mfp for block header with number 'nr'
714071d4279SBram Moolenaar  */
715071d4279SBram Moolenaar     static bhdr_T *
71652ea13daSBram Moolenaar mf_find_hash(memfile_T *mfp, blocknr_T nr)
717071d4279SBram Moolenaar {
718b05b10a3SBram Moolenaar     return (bhdr_T *)mf_hash_find(&mfp->mf_hash, nr);
719071d4279SBram Moolenaar }
720071d4279SBram Moolenaar 
721071d4279SBram Moolenaar /*
722071d4279SBram Moolenaar  * insert block *hp in front of used list of memfile *mfp
723071d4279SBram Moolenaar  */
724071d4279SBram Moolenaar     static void
72552ea13daSBram Moolenaar mf_ins_used(memfile_T *mfp, bhdr_T *hp)
726071d4279SBram Moolenaar {
727071d4279SBram Moolenaar     hp->bh_next = mfp->mf_used_first;
728071d4279SBram Moolenaar     mfp->mf_used_first = hp;
729071d4279SBram Moolenaar     hp->bh_prev = NULL;
730071d4279SBram Moolenaar     if (hp->bh_next == NULL)	    /* list was empty, adjust last pointer */
731071d4279SBram Moolenaar 	mfp->mf_used_last = hp;
732071d4279SBram Moolenaar     else
733071d4279SBram Moolenaar 	hp->bh_next->bh_prev = hp;
734071d4279SBram Moolenaar     mfp->mf_used_count += hp->bh_page_count;
735071d4279SBram Moolenaar     total_mem_used += hp->bh_page_count * mfp->mf_page_size;
736071d4279SBram Moolenaar }
737071d4279SBram Moolenaar 
738071d4279SBram Moolenaar /*
739071d4279SBram Moolenaar  * remove block *hp from used list of memfile *mfp
740071d4279SBram Moolenaar  */
741071d4279SBram Moolenaar     static void
74252ea13daSBram Moolenaar mf_rem_used(memfile_T *mfp, bhdr_T *hp)
743071d4279SBram Moolenaar {
744071d4279SBram Moolenaar     if (hp->bh_next == NULL)	    /* last block in used list */
745071d4279SBram Moolenaar 	mfp->mf_used_last = hp->bh_prev;
746071d4279SBram Moolenaar     else
747071d4279SBram Moolenaar 	hp->bh_next->bh_prev = hp->bh_prev;
748071d4279SBram Moolenaar     if (hp->bh_prev == NULL)	    /* first block in used list */
749071d4279SBram Moolenaar 	mfp->mf_used_first = hp->bh_next;
750071d4279SBram Moolenaar     else
751071d4279SBram Moolenaar 	hp->bh_prev->bh_next = hp->bh_next;
752071d4279SBram Moolenaar     mfp->mf_used_count -= hp->bh_page_count;
753071d4279SBram Moolenaar     total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
754071d4279SBram Moolenaar }
755071d4279SBram Moolenaar 
756071d4279SBram Moolenaar /*
757071d4279SBram Moolenaar  * Release the least recently used block from the used list if the number
758071d4279SBram Moolenaar  * of used memory blocks gets to big.
759071d4279SBram Moolenaar  *
760071d4279SBram Moolenaar  * Return the block header to the caller, including the memory block, so
761071d4279SBram Moolenaar  * it can be re-used. Make sure the page_count is right.
762bc56336bSBram Moolenaar  *
763bc56336bSBram Moolenaar  * Returns NULL if no block is released.
764071d4279SBram Moolenaar  */
765071d4279SBram Moolenaar     static bhdr_T *
76652ea13daSBram Moolenaar mf_release(memfile_T *mfp, int page_count)
767071d4279SBram Moolenaar {
768071d4279SBram Moolenaar     bhdr_T	*hp;
769071d4279SBram Moolenaar     int		need_release;
770071d4279SBram Moolenaar     buf_T	*buf;
771071d4279SBram Moolenaar 
772071d4279SBram Moolenaar     /* don't release while in mf_close_file() */
77347b8b15bSBram Moolenaar     if (mf_dont_release)
774071d4279SBram Moolenaar 	return NULL;
775071d4279SBram Moolenaar 
776071d4279SBram Moolenaar     /*
777071d4279SBram Moolenaar      * Need to release a block if the number of blocks for this memfile is
778071d4279SBram Moolenaar      * higher than the maximum or total memory used is over 'maxmemtot'
779071d4279SBram Moolenaar      */
780071d4279SBram Moolenaar     need_release = ((mfp->mf_used_count >= mfp->mf_used_count_max)
781071d4279SBram Moolenaar 				  || (total_mem_used >> 10) >= (long_u)p_mmt);
782071d4279SBram Moolenaar 
783071d4279SBram Moolenaar     /*
784071d4279SBram Moolenaar      * Try to create a swap file if the amount of memory used is getting too
785071d4279SBram Moolenaar      * high.
786071d4279SBram Moolenaar      */
787071d4279SBram Moolenaar     if (mfp->mf_fd < 0 && need_release && p_uc)
788071d4279SBram Moolenaar     {
789071d4279SBram Moolenaar 	/* find for which buffer this memfile is */
79029323590SBram Moolenaar 	FOR_ALL_BUFFERS(buf)
791071d4279SBram Moolenaar 	    if (buf->b_ml.ml_mfp == mfp)
792071d4279SBram Moolenaar 		break;
793071d4279SBram Moolenaar 	if (buf != NULL && buf->b_may_swap)
794071d4279SBram Moolenaar 	    ml_open_file(buf);
795071d4279SBram Moolenaar     }
796071d4279SBram Moolenaar 
797071d4279SBram Moolenaar     /*
798071d4279SBram Moolenaar      * don't release a block if
799071d4279SBram Moolenaar      *	there is no file for this memfile
800071d4279SBram Moolenaar      * or
801071d4279SBram Moolenaar      *	the number of blocks for this memfile is lower than the maximum
802071d4279SBram Moolenaar      *	  and
803071d4279SBram Moolenaar      *	total memory used is not up to 'maxmemtot'
804071d4279SBram Moolenaar      */
805071d4279SBram Moolenaar     if (mfp->mf_fd < 0 || !need_release)
806071d4279SBram Moolenaar 	return NULL;
807071d4279SBram Moolenaar 
808071d4279SBram Moolenaar     for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
809071d4279SBram Moolenaar 	if (!(hp->bh_flags & BH_LOCKED))
810071d4279SBram Moolenaar 	    break;
811071d4279SBram Moolenaar     if (hp == NULL)	/* not a single one that can be released */
812071d4279SBram Moolenaar 	return NULL;
813071d4279SBram Moolenaar 
814071d4279SBram Moolenaar     /*
815071d4279SBram Moolenaar      * If the block is dirty, write it.
816071d4279SBram Moolenaar      * If the write fails we don't free it.
817071d4279SBram Moolenaar      */
818071d4279SBram Moolenaar     if ((hp->bh_flags & BH_DIRTY) && mf_write(mfp, hp) == FAIL)
819071d4279SBram Moolenaar 	return NULL;
820071d4279SBram Moolenaar 
821071d4279SBram Moolenaar     mf_rem_used(mfp, hp);
822071d4279SBram Moolenaar     mf_rem_hash(mfp, hp);
823071d4279SBram Moolenaar 
824071d4279SBram Moolenaar     /*
825071d4279SBram Moolenaar      * If a bhdr_T is returned, make sure that the page_count of bh_data is
826071d4279SBram Moolenaar      * right
827071d4279SBram Moolenaar      */
828071d4279SBram Moolenaar     if (hp->bh_page_count != page_count)
829071d4279SBram Moolenaar     {
830071d4279SBram Moolenaar 	vim_free(hp->bh_data);
831071d4279SBram Moolenaar 	if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
832071d4279SBram Moolenaar 	{
833071d4279SBram Moolenaar 	    vim_free(hp);
834071d4279SBram Moolenaar 	    return NULL;
835071d4279SBram Moolenaar 	}
836071d4279SBram Moolenaar 	hp->bh_page_count = page_count;
837071d4279SBram Moolenaar     }
838071d4279SBram Moolenaar     return hp;
839071d4279SBram Moolenaar }
840071d4279SBram Moolenaar 
841071d4279SBram Moolenaar /*
842071d4279SBram Moolenaar  * release as many blocks as possible
843071d4279SBram Moolenaar  * Used in case of out of memory
844071d4279SBram Moolenaar  *
845071d4279SBram Moolenaar  * return TRUE if any memory was released
846071d4279SBram Moolenaar  */
847071d4279SBram Moolenaar     int
84852ea13daSBram Moolenaar mf_release_all(void)
849071d4279SBram Moolenaar {
850071d4279SBram Moolenaar     buf_T	*buf;
851071d4279SBram Moolenaar     memfile_T	*mfp;
852071d4279SBram Moolenaar     bhdr_T	*hp;
853071d4279SBram Moolenaar     int		retval = FALSE;
854071d4279SBram Moolenaar 
85529323590SBram Moolenaar     FOR_ALL_BUFFERS(buf)
856071d4279SBram Moolenaar     {
857071d4279SBram Moolenaar 	mfp = buf->b_ml.ml_mfp;
858071d4279SBram Moolenaar 	if (mfp != NULL)
859071d4279SBram Moolenaar 	{
860071d4279SBram Moolenaar 	    /* If no swap file yet, may open one */
861071d4279SBram Moolenaar 	    if (mfp->mf_fd < 0 && buf->b_may_swap)
862071d4279SBram Moolenaar 		ml_open_file(buf);
863071d4279SBram Moolenaar 
864071d4279SBram Moolenaar 	    /* only if there is a swapfile */
865071d4279SBram Moolenaar 	    if (mfp->mf_fd >= 0)
866071d4279SBram Moolenaar 	    {
867071d4279SBram Moolenaar 		for (hp = mfp->mf_used_last; hp != NULL; )
868071d4279SBram Moolenaar 		{
869071d4279SBram Moolenaar 		    if (!(hp->bh_flags & BH_LOCKED)
870071d4279SBram Moolenaar 			    && (!(hp->bh_flags & BH_DIRTY)
871071d4279SBram Moolenaar 				|| mf_write(mfp, hp) != FAIL))
872071d4279SBram Moolenaar 		    {
873071d4279SBram Moolenaar 			mf_rem_used(mfp, hp);
874071d4279SBram Moolenaar 			mf_rem_hash(mfp, hp);
875071d4279SBram Moolenaar 			mf_free_bhdr(hp);
876071d4279SBram Moolenaar 			hp = mfp->mf_used_last;	/* re-start, list was changed */
877071d4279SBram Moolenaar 			retval = TRUE;
878071d4279SBram Moolenaar 		    }
879071d4279SBram Moolenaar 		    else
880071d4279SBram Moolenaar 			hp = hp->bh_prev;
881071d4279SBram Moolenaar 		}
882071d4279SBram Moolenaar 	    }
883071d4279SBram Moolenaar 	}
884071d4279SBram Moolenaar     }
885071d4279SBram Moolenaar     return retval;
886071d4279SBram Moolenaar }
887071d4279SBram Moolenaar 
888071d4279SBram Moolenaar /*
889071d4279SBram Moolenaar  * Allocate a block header and a block of memory for it
890071d4279SBram Moolenaar  */
891071d4279SBram Moolenaar     static bhdr_T *
89252ea13daSBram Moolenaar mf_alloc_bhdr(memfile_T *mfp, int page_count)
893071d4279SBram Moolenaar {
894071d4279SBram Moolenaar     bhdr_T	*hp;
895071d4279SBram Moolenaar 
896071d4279SBram Moolenaar     if ((hp = (bhdr_T *)alloc((unsigned)sizeof(bhdr_T))) != NULL)
897071d4279SBram Moolenaar     {
898071d4279SBram Moolenaar 	if ((hp->bh_data = (char_u *)alloc(mfp->mf_page_size * page_count))
899071d4279SBram Moolenaar 								      == NULL)
900071d4279SBram Moolenaar 	{
901071d4279SBram Moolenaar 	    vim_free(hp);	    /* not enough memory */
902071d4279SBram Moolenaar 	    return NULL;
903071d4279SBram Moolenaar 	}
904071d4279SBram Moolenaar 	hp->bh_page_count = page_count;
905071d4279SBram Moolenaar     }
906071d4279SBram Moolenaar     return hp;
907071d4279SBram Moolenaar }
908071d4279SBram Moolenaar 
909071d4279SBram Moolenaar /*
910071d4279SBram Moolenaar  * Free a block header and the block of memory for it
911071d4279SBram Moolenaar  */
912071d4279SBram Moolenaar     static void
91352ea13daSBram Moolenaar mf_free_bhdr(bhdr_T *hp)
914071d4279SBram Moolenaar {
915071d4279SBram Moolenaar     vim_free(hp->bh_data);
916071d4279SBram Moolenaar     vim_free(hp);
917071d4279SBram Moolenaar }
918071d4279SBram Moolenaar 
919071d4279SBram Moolenaar /*
920071d4279SBram Moolenaar  * insert entry *hp in the free list
921071d4279SBram Moolenaar  */
922071d4279SBram Moolenaar     static void
92352ea13daSBram Moolenaar mf_ins_free(memfile_T *mfp, bhdr_T *hp)
924071d4279SBram Moolenaar {
925071d4279SBram Moolenaar     hp->bh_next = mfp->mf_free_first;
926071d4279SBram Moolenaar     mfp->mf_free_first = hp;
927071d4279SBram Moolenaar }
928071d4279SBram Moolenaar 
929071d4279SBram Moolenaar /*
930071d4279SBram Moolenaar  * remove the first entry from the free list and return a pointer to it
931071d4279SBram Moolenaar  * Note: caller must check that mfp->mf_free_first is not NULL!
932071d4279SBram Moolenaar  */
933071d4279SBram Moolenaar     static bhdr_T *
93452ea13daSBram Moolenaar mf_rem_free(memfile_T *mfp)
935071d4279SBram Moolenaar {
936071d4279SBram Moolenaar     bhdr_T	*hp;
937071d4279SBram Moolenaar 
938071d4279SBram Moolenaar     hp = mfp->mf_free_first;
939071d4279SBram Moolenaar     mfp->mf_free_first = hp->bh_next;
940071d4279SBram Moolenaar     return hp;
941071d4279SBram Moolenaar }
942071d4279SBram Moolenaar 
943071d4279SBram Moolenaar /*
944071d4279SBram Moolenaar  * read a block from disk
945071d4279SBram Moolenaar  *
946071d4279SBram Moolenaar  * Return FAIL for failure, OK otherwise
947071d4279SBram Moolenaar  */
948071d4279SBram Moolenaar     static int
94952ea13daSBram Moolenaar mf_read(memfile_T *mfp, bhdr_T *hp)
950071d4279SBram Moolenaar {
9518767f52fSBram Moolenaar     off_T	offset;
952071d4279SBram Moolenaar     unsigned	page_size;
953071d4279SBram Moolenaar     unsigned	size;
954071d4279SBram Moolenaar 
955071d4279SBram Moolenaar     if (mfp->mf_fd < 0)	    /* there is no file, can't read */
956071d4279SBram Moolenaar 	return FAIL;
957071d4279SBram Moolenaar 
958071d4279SBram Moolenaar     page_size = mfp->mf_page_size;
9598767f52fSBram Moolenaar     offset = (off_T)page_size * hp->bh_bnum;
960071d4279SBram Moolenaar     size = page_size * hp->bh_page_count;
9618767f52fSBram Moolenaar     if (vim_lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
962071d4279SBram Moolenaar     {
9633f2d9814SBram Moolenaar 	PERROR(_("E294: Seek error in swap file read"));
964071d4279SBram Moolenaar 	return FAIL;
965071d4279SBram Moolenaar     }
966540fc6fbSBram Moolenaar     if ((unsigned)read_eintr(mfp->mf_fd, hp->bh_data, size) != size)
967071d4279SBram Moolenaar     {
9683f2d9814SBram Moolenaar 	PERROR(_("E295: Read error in swap file"));
969071d4279SBram Moolenaar 	return FAIL;
970071d4279SBram Moolenaar     }
971a8ffcbbfSBram Moolenaar 
972a8ffcbbfSBram Moolenaar #ifdef FEAT_CRYPT
9734a8c2cfcSBram Moolenaar     /* Decrypt if 'key' is set and this is a data block. And when changing the
9744a8c2cfcSBram Moolenaar      * key. */
9754a8c2cfcSBram Moolenaar     if (*mfp->mf_buffer->b_p_key != NUL || mfp->mf_old_key != NULL)
976a8ffcbbfSBram Moolenaar 	ml_decrypt_data(mfp, hp->bh_data, offset, size);
977a8ffcbbfSBram Moolenaar #endif
978a8ffcbbfSBram Moolenaar 
979071d4279SBram Moolenaar     return OK;
980071d4279SBram Moolenaar }
981071d4279SBram Moolenaar 
982071d4279SBram Moolenaar /*
983071d4279SBram Moolenaar  * write a block to disk
984071d4279SBram Moolenaar  *
985071d4279SBram Moolenaar  * Return FAIL for failure, OK otherwise
986071d4279SBram Moolenaar  */
987071d4279SBram Moolenaar     static int
98852ea13daSBram Moolenaar mf_write(memfile_T *mfp, bhdr_T *hp)
989071d4279SBram Moolenaar {
9908767f52fSBram Moolenaar     off_T	offset;	    /* offset in the file */
991071d4279SBram Moolenaar     blocknr_T	nr;	    /* block nr which is being written */
992071d4279SBram Moolenaar     bhdr_T	*hp2;
993071d4279SBram Moolenaar     unsigned	page_size;  /* number of bytes in a page */
994071d4279SBram Moolenaar     unsigned	page_count; /* number of pages written */
995071d4279SBram Moolenaar     unsigned	size;	    /* number of bytes written */
996071d4279SBram Moolenaar 
997071d4279SBram Moolenaar     if (mfp->mf_fd < 0)	    /* there is no file, can't write */
998071d4279SBram Moolenaar 	return FAIL;
999071d4279SBram Moolenaar 
1000071d4279SBram Moolenaar     if (hp->bh_bnum < 0)	/* must assign file block number */
1001071d4279SBram Moolenaar 	if (mf_trans_add(mfp, hp) == FAIL)
1002071d4279SBram Moolenaar 	    return FAIL;
1003071d4279SBram Moolenaar 
1004071d4279SBram Moolenaar     page_size = mfp->mf_page_size;
1005071d4279SBram Moolenaar 
1006071d4279SBram Moolenaar     /*
1007071d4279SBram Moolenaar      * We don't want gaps in the file. Write the blocks in front of *hp
1008071d4279SBram Moolenaar      * to extend the file.
1009071d4279SBram Moolenaar      * If block 'mf_infile_count' is not in the hash list, it has been
1010071d4279SBram Moolenaar      * freed. Fill the space in the file with data from the current block.
1011071d4279SBram Moolenaar      */
1012071d4279SBram Moolenaar     for (;;)
1013071d4279SBram Moolenaar     {
1014071d4279SBram Moolenaar 	nr = hp->bh_bnum;
1015071d4279SBram Moolenaar 	if (nr > mfp->mf_infile_count)		/* beyond end of file */
1016071d4279SBram Moolenaar 	{
1017071d4279SBram Moolenaar 	    nr = mfp->mf_infile_count;
101884a05accSBram Moolenaar 	    hp2 = mf_find_hash(mfp, nr);	/* NULL caught below */
1019071d4279SBram Moolenaar 	}
1020071d4279SBram Moolenaar 	else
1021071d4279SBram Moolenaar 	    hp2 = hp;
1022071d4279SBram Moolenaar 
10238767f52fSBram Moolenaar 	offset = (off_T)page_size * nr;
10248767f52fSBram Moolenaar 	if (vim_lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
1025071d4279SBram Moolenaar 	{
10263f2d9814SBram Moolenaar 	    PERROR(_("E296: Seek error in swap file write"));
1027071d4279SBram Moolenaar 	    return FAIL;
1028071d4279SBram Moolenaar 	}
1029071d4279SBram Moolenaar 	if (hp2 == NULL)	    /* freed block, fill with dummy data */
1030071d4279SBram Moolenaar 	    page_count = 1;
1031071d4279SBram Moolenaar 	else
1032071d4279SBram Moolenaar 	    page_count = hp2->bh_page_count;
1033071d4279SBram Moolenaar 	size = page_size * page_count;
1034a8ffcbbfSBram Moolenaar 	if (mf_write_block(mfp, hp2 == NULL ? hp : hp2, offset, size) == FAIL)
1035071d4279SBram Moolenaar 	{
1036071d4279SBram Moolenaar 	    /*
1037071d4279SBram Moolenaar 	     * Avoid repeating the error message, this mostly happens when the
103849325942SBram Moolenaar 	     * disk is full. We give the message again only after a successful
1039071d4279SBram Moolenaar 	     * write or when hitting a key. We keep on trying, in case some
1040071d4279SBram Moolenaar 	     * space becomes available.
1041071d4279SBram Moolenaar 	     */
1042071d4279SBram Moolenaar 	    if (!did_swapwrite_msg)
1043*f9e3e09fSBram Moolenaar 		emsg(_("E297: Write error in swap file"));
1044071d4279SBram Moolenaar 	    did_swapwrite_msg = TRUE;
1045071d4279SBram Moolenaar 	    return FAIL;
1046071d4279SBram Moolenaar 	}
1047071d4279SBram Moolenaar 	did_swapwrite_msg = FALSE;
1048071d4279SBram Moolenaar 	if (hp2 != NULL)		    /* written a non-dummy block */
1049071d4279SBram Moolenaar 	    hp2->bh_flags &= ~BH_DIRTY;
1050071d4279SBram Moolenaar 					    /* appended to the file */
1051071d4279SBram Moolenaar 	if (nr + (blocknr_T)page_count > mfp->mf_infile_count)
1052071d4279SBram Moolenaar 	    mfp->mf_infile_count = nr + page_count;
1053071d4279SBram Moolenaar 	if (nr == hp->bh_bnum)		    /* written the desired block */
1054071d4279SBram Moolenaar 	    break;
1055071d4279SBram Moolenaar     }
1056071d4279SBram Moolenaar     return OK;
1057071d4279SBram Moolenaar }
1058071d4279SBram Moolenaar 
1059071d4279SBram Moolenaar /*
1060a8ffcbbfSBram Moolenaar  * Write block "hp" with data size "size" to file "mfp->mf_fd".
1061a8ffcbbfSBram Moolenaar  * Takes care of encryption.
1062a8ffcbbfSBram Moolenaar  * Return FAIL or OK.
1063a8ffcbbfSBram Moolenaar  */
1064a8ffcbbfSBram Moolenaar     static int
106552ea13daSBram Moolenaar mf_write_block(
106652ea13daSBram Moolenaar     memfile_T	*mfp,
106752ea13daSBram Moolenaar     bhdr_T	*hp,
10688767f52fSBram Moolenaar     off_T	offset UNUSED,
106952ea13daSBram Moolenaar     unsigned	size)
1070a8ffcbbfSBram Moolenaar {
1071a8ffcbbfSBram Moolenaar     char_u	*data = hp->bh_data;
1072a8ffcbbfSBram Moolenaar     int		result = OK;
1073a8ffcbbfSBram Moolenaar 
1074a8ffcbbfSBram Moolenaar #ifdef FEAT_CRYPT
1075a8ffcbbfSBram Moolenaar     /* Encrypt if 'key' is set and this is a data block. */
1076a8ffcbbfSBram Moolenaar     if (*mfp->mf_buffer->b_p_key != NUL)
1077a8ffcbbfSBram Moolenaar     {
1078a8ffcbbfSBram Moolenaar 	data = ml_encrypt_data(mfp, data, offset, size);
1079a8ffcbbfSBram Moolenaar 	if (data == NULL)
1080a8ffcbbfSBram Moolenaar 	    return FAIL;
1081a8ffcbbfSBram Moolenaar     }
1082a8ffcbbfSBram Moolenaar #endif
1083a8ffcbbfSBram Moolenaar 
1084540fc6fbSBram Moolenaar     if ((unsigned)write_eintr(mfp->mf_fd, data, size) != size)
1085a8ffcbbfSBram Moolenaar 	result = FAIL;
1086a8ffcbbfSBram Moolenaar 
1087a8ffcbbfSBram Moolenaar #ifdef FEAT_CRYPT
1088a8ffcbbfSBram Moolenaar     if (data != hp->bh_data)
1089a8ffcbbfSBram Moolenaar 	vim_free(data);
1090a8ffcbbfSBram Moolenaar #endif
1091a8ffcbbfSBram Moolenaar 
1092a8ffcbbfSBram Moolenaar     return result;
1093a8ffcbbfSBram Moolenaar }
1094a8ffcbbfSBram Moolenaar 
1095a8ffcbbfSBram Moolenaar /*
1096071d4279SBram Moolenaar  * Make block number for *hp positive and add it to the translation list
1097071d4279SBram Moolenaar  *
1098071d4279SBram Moolenaar  * Return FAIL for failure, OK otherwise
1099071d4279SBram Moolenaar  */
1100071d4279SBram Moolenaar     static int
110152ea13daSBram Moolenaar mf_trans_add(memfile_T *mfp, bhdr_T *hp)
1102071d4279SBram Moolenaar {
1103071d4279SBram Moolenaar     bhdr_T	*freep;
1104071d4279SBram Moolenaar     blocknr_T	new_bnum;
1105071d4279SBram Moolenaar     NR_TRANS	*np;
1106071d4279SBram Moolenaar     int		page_count;
1107071d4279SBram Moolenaar 
1108071d4279SBram Moolenaar     if (hp->bh_bnum >= 0)		    /* it's already positive */
1109071d4279SBram Moolenaar 	return OK;
1110071d4279SBram Moolenaar 
1111071d4279SBram Moolenaar     if ((np = (NR_TRANS *)alloc((unsigned)sizeof(NR_TRANS))) == NULL)
1112071d4279SBram Moolenaar 	return FAIL;
1113071d4279SBram Moolenaar 
1114071d4279SBram Moolenaar /*
1115a8ffcbbfSBram Moolenaar  * Get a new number for the block.
1116071d4279SBram Moolenaar  * If the first item in the free list has sufficient pages, use its number
1117071d4279SBram Moolenaar  * Otherwise use mf_blocknr_max.
1118071d4279SBram Moolenaar  */
1119071d4279SBram Moolenaar     freep = mfp->mf_free_first;
1120071d4279SBram Moolenaar     page_count = hp->bh_page_count;
1121071d4279SBram Moolenaar     if (freep != NULL && freep->bh_page_count >= page_count)
1122071d4279SBram Moolenaar     {
1123071d4279SBram Moolenaar 	new_bnum = freep->bh_bnum;
1124071d4279SBram Moolenaar 	/*
112584a05accSBram Moolenaar 	 * If the page count of the free block was larger, reduce it.
1126071d4279SBram Moolenaar 	 * If the page count matches, remove the block from the free list
1127071d4279SBram Moolenaar 	 */
1128071d4279SBram Moolenaar 	if (freep->bh_page_count > page_count)
1129071d4279SBram Moolenaar 	{
1130071d4279SBram Moolenaar 	    freep->bh_bnum += page_count;
1131071d4279SBram Moolenaar 	    freep->bh_page_count -= page_count;
1132071d4279SBram Moolenaar 	}
1133071d4279SBram Moolenaar 	else
1134071d4279SBram Moolenaar 	{
1135071d4279SBram Moolenaar 	    freep = mf_rem_free(mfp);
1136071d4279SBram Moolenaar 	    vim_free(freep);
1137071d4279SBram Moolenaar 	}
1138071d4279SBram Moolenaar     }
1139071d4279SBram Moolenaar     else
1140071d4279SBram Moolenaar     {
1141071d4279SBram Moolenaar 	new_bnum = mfp->mf_blocknr_max;
1142071d4279SBram Moolenaar 	mfp->mf_blocknr_max += page_count;
1143071d4279SBram Moolenaar     }
1144071d4279SBram Moolenaar 
1145071d4279SBram Moolenaar     np->nt_old_bnum = hp->bh_bnum;	    /* adjust number */
1146071d4279SBram Moolenaar     np->nt_new_bnum = new_bnum;
1147071d4279SBram Moolenaar 
1148071d4279SBram Moolenaar     mf_rem_hash(mfp, hp);		    /* remove from old hash list */
1149071d4279SBram Moolenaar     hp->bh_bnum = new_bnum;
1150071d4279SBram Moolenaar     mf_ins_hash(mfp, hp);		    /* insert in new hash list */
1151071d4279SBram Moolenaar 
1152b05b10a3SBram Moolenaar     /* Insert "np" into "mf_trans" hashtable with key "np->nt_old_bnum" */
1153b05b10a3SBram Moolenaar     mf_hash_add_item(&mfp->mf_trans, (mf_hashitem_T *)np);
1154071d4279SBram Moolenaar 
1155071d4279SBram Moolenaar     return OK;
1156071d4279SBram Moolenaar }
1157071d4279SBram Moolenaar 
1158071d4279SBram Moolenaar /*
1159bc56336bSBram Moolenaar  * Lookup a translation from the trans lists and delete the entry.
1160071d4279SBram Moolenaar  *
1161071d4279SBram Moolenaar  * Return the positive new number when found, the old number when not found
1162071d4279SBram Moolenaar  */
1163071d4279SBram Moolenaar     blocknr_T
116452ea13daSBram Moolenaar mf_trans_del(memfile_T *mfp, blocknr_T old_nr)
1165071d4279SBram Moolenaar {
1166071d4279SBram Moolenaar     NR_TRANS	*np;
1167071d4279SBram Moolenaar     blocknr_T	new_bnum;
1168071d4279SBram Moolenaar 
1169b05b10a3SBram Moolenaar     np = (NR_TRANS *)mf_hash_find(&mfp->mf_trans, old_nr);
1170b05b10a3SBram Moolenaar 
1171071d4279SBram Moolenaar     if (np == NULL)		/* not found */
1172071d4279SBram Moolenaar 	return old_nr;
1173071d4279SBram Moolenaar 
1174071d4279SBram Moolenaar     mfp->mf_neg_count--;
1175071d4279SBram Moolenaar     new_bnum = np->nt_new_bnum;
1176b05b10a3SBram Moolenaar 
1177b05b10a3SBram Moolenaar     /* remove entry from the trans list */
1178b05b10a3SBram Moolenaar     mf_hash_rem_item(&mfp->mf_trans, (mf_hashitem_T *)np);
1179b05b10a3SBram Moolenaar 
1180071d4279SBram Moolenaar     vim_free(np);
1181071d4279SBram Moolenaar 
1182071d4279SBram Moolenaar     return new_bnum;
1183071d4279SBram Moolenaar }
1184071d4279SBram Moolenaar 
1185071d4279SBram Moolenaar /*
1186071d4279SBram Moolenaar  * Set mfp->mf_ffname according to mfp->mf_fname and some other things.
1187071d4279SBram Moolenaar  * Only called when creating or renaming the swapfile.	Either way it's a new
1188071d4279SBram Moolenaar  * name so we must work out the full path name.
1189071d4279SBram Moolenaar  */
1190071d4279SBram Moolenaar     void
119152ea13daSBram Moolenaar mf_set_ffname(memfile_T *mfp)
1192071d4279SBram Moolenaar {
1193071d4279SBram Moolenaar     mfp->mf_ffname = FullName_save(mfp->mf_fname, FALSE);
1194071d4279SBram Moolenaar }
1195071d4279SBram Moolenaar 
1196071d4279SBram Moolenaar /*
1197071d4279SBram Moolenaar  * Make the name of the file used for the memfile a full path.
1198071d4279SBram Moolenaar  * Used before doing a :cd
1199071d4279SBram Moolenaar  */
1200071d4279SBram Moolenaar     void
120152ea13daSBram Moolenaar mf_fullname(memfile_T *mfp)
1202071d4279SBram Moolenaar {
1203071d4279SBram Moolenaar     if (mfp != NULL && mfp->mf_fname != NULL && mfp->mf_ffname != NULL)
1204071d4279SBram Moolenaar     {
1205071d4279SBram Moolenaar 	vim_free(mfp->mf_fname);
1206071d4279SBram Moolenaar 	mfp->mf_fname = mfp->mf_ffname;
1207071d4279SBram Moolenaar 	mfp->mf_ffname = NULL;
1208071d4279SBram Moolenaar     }
1209071d4279SBram Moolenaar }
1210071d4279SBram Moolenaar 
1211071d4279SBram Moolenaar /*
1212071d4279SBram Moolenaar  * return TRUE if there are any translations pending for 'mfp'
1213071d4279SBram Moolenaar  */
1214071d4279SBram Moolenaar     int
121552ea13daSBram Moolenaar mf_need_trans(memfile_T *mfp)
1216071d4279SBram Moolenaar {
1217071d4279SBram Moolenaar     return (mfp->mf_fname != NULL && mfp->mf_neg_count > 0);
1218071d4279SBram Moolenaar }
1219071d4279SBram Moolenaar 
1220071d4279SBram Moolenaar /*
1221071d4279SBram Moolenaar  * Open a swap file for a memfile.
1222071d4279SBram Moolenaar  * The "fname" must be in allocated memory, and is consumed (also when an
1223071d4279SBram Moolenaar  * error occurs).
1224071d4279SBram Moolenaar  */
1225071d4279SBram Moolenaar     static void
122652ea13daSBram Moolenaar mf_do_open(
122752ea13daSBram Moolenaar     memfile_T	*mfp,
122852ea13daSBram Moolenaar     char_u	*fname,
122952ea13daSBram Moolenaar     int		flags)		/* flags for open() */
1230071d4279SBram Moolenaar {
1231071d4279SBram Moolenaar #ifdef HAVE_LSTAT
12328767f52fSBram Moolenaar     stat_T	sb;
1233071d4279SBram Moolenaar #endif
1234071d4279SBram Moolenaar 
1235071d4279SBram Moolenaar     mfp->mf_fname = fname;
1236071d4279SBram Moolenaar 
1237071d4279SBram Moolenaar     /*
1238071d4279SBram Moolenaar      * Get the full path name before the open, because this is
1239071d4279SBram Moolenaar      * not possible after the open on the Amiga.
1240071d4279SBram Moolenaar      * fname cannot be NameBuff, because it must have been allocated.
1241071d4279SBram Moolenaar      */
1242071d4279SBram Moolenaar     mf_set_ffname(mfp);
124348e330afSBram Moolenaar #if defined(MSWIN)
1244071d4279SBram Moolenaar     /*
124584a05accSBram Moolenaar      * A ":!cd e:xxx" may change the directory without us knowing, use the
1246071d4279SBram Moolenaar      * full pathname always.  Careful: This frees fname!
1247071d4279SBram Moolenaar      */
1248071d4279SBram Moolenaar     mf_fullname(mfp);
1249071d4279SBram Moolenaar #endif
1250071d4279SBram Moolenaar 
1251071d4279SBram Moolenaar #ifdef HAVE_LSTAT
1252071d4279SBram Moolenaar     /*
1253071d4279SBram Moolenaar      * Extra security check: When creating a swap file it really shouldn't
1254071d4279SBram Moolenaar      * exist yet.  If there is a symbolic link, this is most likely an attack.
1255071d4279SBram Moolenaar      */
1256071d4279SBram Moolenaar     if ((flags & O_CREAT) && mch_lstat((char *)mfp->mf_fname, &sb) >= 0)
1257071d4279SBram Moolenaar     {
1258071d4279SBram Moolenaar 	mfp->mf_fd = -1;
1259*f9e3e09fSBram Moolenaar 	emsg(_("E300: Swap file already exists (symlink attack?)"));
1260071d4279SBram Moolenaar     }
1261071d4279SBram Moolenaar     else
1262071d4279SBram Moolenaar #endif
1263071d4279SBram Moolenaar     {
1264071d4279SBram Moolenaar 	/*
1265071d4279SBram Moolenaar 	 * try to open the file
1266071d4279SBram Moolenaar 	 */
1267a5792f58SBram Moolenaar 	flags |= O_EXTRA | O_NOFOLLOW;
1268071d4279SBram Moolenaar #ifdef WIN32
1269071d4279SBram Moolenaar 	/* Prevent handle inheritance that cause problems with Cscope
1270071d4279SBram Moolenaar 	 * (swap file may not be deleted if cscope connection was open after
1271071d4279SBram Moolenaar 	 * the file) */
1272071d4279SBram Moolenaar 	flags |= O_NOINHERIT;
1273071d4279SBram Moolenaar #endif
1274071d4279SBram Moolenaar 	mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags);
1275071d4279SBram Moolenaar     }
1276071d4279SBram Moolenaar 
1277071d4279SBram Moolenaar     /*
1278071d4279SBram Moolenaar      * If the file cannot be opened, use memory only
1279071d4279SBram Moolenaar      */
1280071d4279SBram Moolenaar     if (mfp->mf_fd < 0)
1281071d4279SBram Moolenaar     {
1282d23a8236SBram Moolenaar 	VIM_CLEAR(mfp->mf_fname);
1283d23a8236SBram Moolenaar 	VIM_CLEAR(mfp->mf_ffname);
1284071d4279SBram Moolenaar     }
1285071d4279SBram Moolenaar     else
1286588ebeb7SBram Moolenaar     {
1287f05da219SBram Moolenaar #ifdef HAVE_FD_CLOEXEC
1288f05da219SBram Moolenaar 	int fdflags = fcntl(mfp->mf_fd, F_GETFD);
1289f05da219SBram Moolenaar 	if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
1290fbc4b4dbSBram Moolenaar 	    (void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
1291f05da219SBram Moolenaar #endif
12925bd32f47SBram Moolenaar #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
1293588ebeb7SBram Moolenaar 	mch_copy_sec(fname, mfp->mf_fname);
1294588ebeb7SBram Moolenaar #endif
1295071d4279SBram Moolenaar 	mch_hide(mfp->mf_fname);    /* try setting the 'hidden' flag */
1296071d4279SBram Moolenaar     }
1297588ebeb7SBram Moolenaar }
1298b05b10a3SBram Moolenaar 
1299b05b10a3SBram Moolenaar /*
1300b05b10a3SBram Moolenaar  * Implementation of mf_hashtab_T follows.
1301b05b10a3SBram Moolenaar  */
1302b05b10a3SBram Moolenaar 
1303b05b10a3SBram Moolenaar /*
1304b05b10a3SBram Moolenaar  * The number of buckets in the hashtable is increased by a factor of
1305b05b10a3SBram Moolenaar  * MHT_GROWTH_FACTOR when the average number of items per bucket
1306b05b10a3SBram Moolenaar  * exceeds 2 ^ MHT_LOG_LOAD_FACTOR.
1307b05b10a3SBram Moolenaar  */
1308b05b10a3SBram Moolenaar #define MHT_LOG_LOAD_FACTOR 6
1309b05b10a3SBram Moolenaar #define MHT_GROWTH_FACTOR   2   /* must be a power of two */
1310b05b10a3SBram Moolenaar 
1311b05b10a3SBram Moolenaar /*
1312b05b10a3SBram Moolenaar  * Initialize an empty hash table.
1313b05b10a3SBram Moolenaar  */
1314b05b10a3SBram Moolenaar     static void
131552ea13daSBram Moolenaar mf_hash_init(mf_hashtab_T *mht)
1316b05b10a3SBram Moolenaar {
1317b05b10a3SBram Moolenaar     vim_memset(mht, 0, sizeof(mf_hashtab_T));
1318b05b10a3SBram Moolenaar     mht->mht_buckets = mht->mht_small_buckets;
1319b05b10a3SBram Moolenaar     mht->mht_mask = MHT_INIT_SIZE - 1;
1320b05b10a3SBram Moolenaar }
1321b05b10a3SBram Moolenaar 
1322b05b10a3SBram Moolenaar /*
1323b05b10a3SBram Moolenaar  * Free the array of a hash table.  Does not free the items it contains!
1324b05b10a3SBram Moolenaar  * The hash table must not be used again without another mf_hash_init() call.
1325b05b10a3SBram Moolenaar  */
1326b05b10a3SBram Moolenaar     static void
132752ea13daSBram Moolenaar mf_hash_free(mf_hashtab_T *mht)
1328b05b10a3SBram Moolenaar {
1329b05b10a3SBram Moolenaar     if (mht->mht_buckets != mht->mht_small_buckets)
1330b05b10a3SBram Moolenaar 	vim_free(mht->mht_buckets);
1331b05b10a3SBram Moolenaar }
1332b05b10a3SBram Moolenaar 
1333b05b10a3SBram Moolenaar /*
1334b05b10a3SBram Moolenaar  * Free the array of a hash table and all the items it contains.
1335b05b10a3SBram Moolenaar  */
1336b05b10a3SBram Moolenaar     static void
133752ea13daSBram Moolenaar mf_hash_free_all(mf_hashtab_T *mht)
1338b05b10a3SBram Moolenaar {
1339b05b10a3SBram Moolenaar     long_u	    idx;
1340b05b10a3SBram Moolenaar     mf_hashitem_T   *mhi;
1341b05b10a3SBram Moolenaar     mf_hashitem_T   *next;
1342b05b10a3SBram Moolenaar 
1343b05b10a3SBram Moolenaar     for (idx = 0; idx <= mht->mht_mask; idx++)
1344b05b10a3SBram Moolenaar 	for (mhi = mht->mht_buckets[idx]; mhi != NULL; mhi = next)
1345b05b10a3SBram Moolenaar 	{
1346b05b10a3SBram Moolenaar 	    next = mhi->mhi_next;
1347b05b10a3SBram Moolenaar 	    vim_free(mhi);
1348b05b10a3SBram Moolenaar 	}
1349b05b10a3SBram Moolenaar 
1350b05b10a3SBram Moolenaar     mf_hash_free(mht);
1351b05b10a3SBram Moolenaar }
1352b05b10a3SBram Moolenaar 
1353b05b10a3SBram Moolenaar /*
1354b05b10a3SBram Moolenaar  * Find "key" in hashtable "mht".
1355b05b10a3SBram Moolenaar  * Returns a pointer to a mf_hashitem_T or NULL if the item was not found.
1356b05b10a3SBram Moolenaar  */
1357b05b10a3SBram Moolenaar     static mf_hashitem_T *
135852ea13daSBram Moolenaar mf_hash_find(mf_hashtab_T *mht, blocknr_T key)
1359b05b10a3SBram Moolenaar {
1360b05b10a3SBram Moolenaar     mf_hashitem_T   *mhi;
1361b05b10a3SBram Moolenaar 
1362b05b10a3SBram Moolenaar     mhi = mht->mht_buckets[key & mht->mht_mask];
1363b05b10a3SBram Moolenaar     while (mhi != NULL && mhi->mhi_key != key)
1364b05b10a3SBram Moolenaar 	mhi = mhi->mhi_next;
1365b05b10a3SBram Moolenaar 
1366b05b10a3SBram Moolenaar     return mhi;
1367b05b10a3SBram Moolenaar }
1368b05b10a3SBram Moolenaar 
1369b05b10a3SBram Moolenaar /*
1370b05b10a3SBram Moolenaar  * Add item "mhi" to hashtable "mht".
1371b05b10a3SBram Moolenaar  * "mhi" must not be NULL.
1372b05b10a3SBram Moolenaar  */
1373b05b10a3SBram Moolenaar     static void
137452ea13daSBram Moolenaar mf_hash_add_item(mf_hashtab_T *mht, mf_hashitem_T *mhi)
1375b05b10a3SBram Moolenaar {
1376b05b10a3SBram Moolenaar     long_u	    idx;
1377b05b10a3SBram Moolenaar 
1378b05b10a3SBram Moolenaar     idx = mhi->mhi_key & mht->mht_mask;
1379b05b10a3SBram Moolenaar     mhi->mhi_next = mht->mht_buckets[idx];
1380b05b10a3SBram Moolenaar     mhi->mhi_prev = NULL;
1381b05b10a3SBram Moolenaar     if (mhi->mhi_next != NULL)
1382b05b10a3SBram Moolenaar 	mhi->mhi_next->mhi_prev = mhi;
1383b05b10a3SBram Moolenaar     mht->mht_buckets[idx] = mhi;
1384b05b10a3SBram Moolenaar 
1385b05b10a3SBram Moolenaar     mht->mht_count++;
1386b05b10a3SBram Moolenaar 
1387b05b10a3SBram Moolenaar     /*
1388b05b10a3SBram Moolenaar      * Grow hashtable when we have more thank 2^MHT_LOG_LOAD_FACTOR
1389b05b10a3SBram Moolenaar      * items per bucket on average
1390b05b10a3SBram Moolenaar      */
1391b05b10a3SBram Moolenaar     if (mht->mht_fixed == 0
1392b05b10a3SBram Moolenaar 	&& (mht->mht_count >> MHT_LOG_LOAD_FACTOR) > mht->mht_mask)
1393b05b10a3SBram Moolenaar     {
1394b05b10a3SBram Moolenaar 	if (mf_hash_grow(mht) == FAIL)
1395b05b10a3SBram Moolenaar 	{
1396b05b10a3SBram Moolenaar 	    /* stop trying to grow after first failure to allocate memory */
1397b05b10a3SBram Moolenaar 	    mht->mht_fixed = 1;
1398b05b10a3SBram Moolenaar 	}
1399b05b10a3SBram Moolenaar     }
1400b05b10a3SBram Moolenaar }
1401b05b10a3SBram Moolenaar 
1402b05b10a3SBram Moolenaar /*
1403b05b10a3SBram Moolenaar  * Remove item "mhi" from hashtable "mht".
1404b05b10a3SBram Moolenaar  * "mhi" must not be NULL and must have been inserted into "mht".
1405b05b10a3SBram Moolenaar  */
1406b05b10a3SBram Moolenaar     static void
140752ea13daSBram Moolenaar mf_hash_rem_item(mf_hashtab_T *mht, mf_hashitem_T *mhi)
1408b05b10a3SBram Moolenaar {
1409b05b10a3SBram Moolenaar     if (mhi->mhi_prev == NULL)
1410b05b10a3SBram Moolenaar 	mht->mht_buckets[mhi->mhi_key & mht->mht_mask] = mhi->mhi_next;
1411b05b10a3SBram Moolenaar     else
1412b05b10a3SBram Moolenaar 	mhi->mhi_prev->mhi_next = mhi->mhi_next;
1413b05b10a3SBram Moolenaar 
1414b05b10a3SBram Moolenaar     if (mhi->mhi_next != NULL)
1415b05b10a3SBram Moolenaar 	mhi->mhi_next->mhi_prev = mhi->mhi_prev;
1416b05b10a3SBram Moolenaar 
1417b05b10a3SBram Moolenaar     mht->mht_count--;
1418b05b10a3SBram Moolenaar 
1419b05b10a3SBram Moolenaar     /* We could shrink the table here, but it typically takes little memory,
1420b05b10a3SBram Moolenaar      * so why bother?  */
1421b05b10a3SBram Moolenaar }
1422b05b10a3SBram Moolenaar 
1423b05b10a3SBram Moolenaar /*
1424b05b10a3SBram Moolenaar  * Increase number of buckets in the hashtable by MHT_GROWTH_FACTOR and
1425b05b10a3SBram Moolenaar  * rehash items.
1426b05b10a3SBram Moolenaar  * Returns FAIL when out of memory.
1427b05b10a3SBram Moolenaar  */
1428b05b10a3SBram Moolenaar     static int
142952ea13daSBram Moolenaar mf_hash_grow(mf_hashtab_T *mht)
1430b05b10a3SBram Moolenaar {
1431b05b10a3SBram Moolenaar     long_u	    i, j;
1432b05b10a3SBram Moolenaar     int		    shift;
1433b05b10a3SBram Moolenaar     mf_hashitem_T   *mhi;
1434b05b10a3SBram Moolenaar     mf_hashitem_T   *tails[MHT_GROWTH_FACTOR];
1435b05b10a3SBram Moolenaar     mf_hashitem_T   **buckets;
1436b05b10a3SBram Moolenaar     size_t	    size;
1437b05b10a3SBram Moolenaar 
1438b05b10a3SBram Moolenaar     size = (mht->mht_mask + 1) * MHT_GROWTH_FACTOR * sizeof(void *);
1439b05b10a3SBram Moolenaar     buckets = (mf_hashitem_T **)lalloc_clear(size, FALSE);
1440b05b10a3SBram Moolenaar     if (buckets == NULL)
1441b05b10a3SBram Moolenaar 	return FAIL;
1442b05b10a3SBram Moolenaar 
1443b05b10a3SBram Moolenaar     shift = 0;
1444b05b10a3SBram Moolenaar     while ((mht->mht_mask >> shift) != 0)
1445b05b10a3SBram Moolenaar 	shift++;
1446b05b10a3SBram Moolenaar 
1447b05b10a3SBram Moolenaar     for (i = 0; i <= mht->mht_mask; i++)
1448b05b10a3SBram Moolenaar     {
1449b05b10a3SBram Moolenaar 	/*
1450b05b10a3SBram Moolenaar 	 * Traverse the items in the i-th original bucket and move them into
1451b05b10a3SBram Moolenaar 	 * MHT_GROWTH_FACTOR new buckets, preserving their relative order
1452b05b10a3SBram Moolenaar 	 * within each new bucket.  Preserving the order is important because
1453b05b10a3SBram Moolenaar 	 * mf_get() tries to keep most recently used items at the front of
1454b05b10a3SBram Moolenaar 	 * each bucket.
1455b05b10a3SBram Moolenaar 	 *
1456b05b10a3SBram Moolenaar 	 * Here we strongly rely on the fact the hashes are computed modulo
1457b05b10a3SBram Moolenaar 	 * a power of two.
1458b05b10a3SBram Moolenaar 	 */
1459b05b10a3SBram Moolenaar 
1460b05b10a3SBram Moolenaar 	vim_memset(tails, 0, sizeof(tails));
1461b05b10a3SBram Moolenaar 
1462b05b10a3SBram Moolenaar 	for (mhi = mht->mht_buckets[i]; mhi != NULL; mhi = mhi->mhi_next)
1463b05b10a3SBram Moolenaar 	{
1464b05b10a3SBram Moolenaar 	    j = (mhi->mhi_key >> shift) & (MHT_GROWTH_FACTOR - 1);
1465b05b10a3SBram Moolenaar 	    if (tails[j] == NULL)
1466b05b10a3SBram Moolenaar 	    {
1467b05b10a3SBram Moolenaar 		buckets[i + (j << shift)] = mhi;
1468b05b10a3SBram Moolenaar 		tails[j] = mhi;
1469b05b10a3SBram Moolenaar 		mhi->mhi_prev = NULL;
1470b05b10a3SBram Moolenaar 	    }
1471b05b10a3SBram Moolenaar 	    else
1472b05b10a3SBram Moolenaar 	    {
1473b05b10a3SBram Moolenaar 		tails[j]->mhi_next = mhi;
1474b05b10a3SBram Moolenaar 		mhi->mhi_prev = tails[j];
1475b05b10a3SBram Moolenaar 		tails[j] = mhi;
1476b05b10a3SBram Moolenaar 	    }
1477b05b10a3SBram Moolenaar 	}
1478b05b10a3SBram Moolenaar 
1479b05b10a3SBram Moolenaar 	for (j = 0; j < MHT_GROWTH_FACTOR; j++)
1480b05b10a3SBram Moolenaar 	    if (tails[j] != NULL)
1481b05b10a3SBram Moolenaar 		tails[j]->mhi_next = NULL;
1482b05b10a3SBram Moolenaar     }
1483b05b10a3SBram Moolenaar 
1484b05b10a3SBram Moolenaar     if (mht->mht_buckets != mht->mht_small_buckets)
1485b05b10a3SBram Moolenaar 	vim_free(mht->mht_buckets);
1486b05b10a3SBram Moolenaar 
1487b05b10a3SBram Moolenaar     mht->mht_buckets = buckets;
1488b05b10a3SBram Moolenaar     mht->mht_mask = (mht->mht_mask + 1) * MHT_GROWTH_FACTOR - 1;
1489b05b10a3SBram Moolenaar 
1490b05b10a3SBram Moolenaar     return OK;
1491b05b10a3SBram Moolenaar }
1492