xref: /vim-8.2.3635/src/memfile.c (revision 3f2d9814)
1071d4279SBram Moolenaar /* vi:set ts=8 sts=4 sw=4:
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 #if defined MSDOS || defined(WIN32) || defined(_WIN64)
36362e1a30SBram Moolenaar # include "vimio.h"	/* for lseek(), must be before vim.h */
37071d4279SBram Moolenaar #endif
38071d4279SBram Moolenaar 
39071d4279SBram Moolenaar #include "vim.h"
40071d4279SBram Moolenaar 
41071d4279SBram Moolenaar #ifdef HAVE_FCNTL_H
42071d4279SBram Moolenaar # include <fcntl.h>
43071d4279SBram Moolenaar #endif
44071d4279SBram Moolenaar 
45071d4279SBram Moolenaar /*
46071d4279SBram Moolenaar  * Some systems have the page size in statfs.f_bsize, some in stat.st_blksize
47071d4279SBram Moolenaar  */
48071d4279SBram Moolenaar #ifdef HAVE_ST_BLKSIZE
49071d4279SBram Moolenaar # define STATFS stat
50071d4279SBram Moolenaar # define F_BSIZE st_blksize
51071d4279SBram Moolenaar # define fstatfs(fd, buf, len, nul) mch_fstat((fd), (buf))
52071d4279SBram Moolenaar #else
53071d4279SBram Moolenaar # ifdef HAVE_SYS_STATFS_H
54071d4279SBram Moolenaar #  include <sys/statfs.h>
55071d4279SBram Moolenaar #  define STATFS statfs
56071d4279SBram Moolenaar #  define F_BSIZE f_bsize
57071d4279SBram Moolenaar #  ifdef __MINT__		/* do we still need this? */
58071d4279SBram Moolenaar #   define fstatfs(fd, buf, len, nul) mch_fstat((fd), (buf))
59071d4279SBram Moolenaar #  endif
60071d4279SBram Moolenaar # endif
61071d4279SBram Moolenaar #endif
62071d4279SBram Moolenaar 
63071d4279SBram Moolenaar /*
64071d4279SBram Moolenaar  * for Amiga Dos 2.0x we use Flush
65071d4279SBram Moolenaar  */
66071d4279SBram Moolenaar #ifdef AMIGA
67071d4279SBram Moolenaar # ifdef FEAT_ARP
68071d4279SBram Moolenaar extern int dos2;			/* this is in os_amiga.c */
69071d4279SBram Moolenaar # endif
70071d4279SBram Moolenaar # ifdef SASC
71071d4279SBram Moolenaar #  include <proto/dos.h>
72071d4279SBram Moolenaar #  include <ios1.h>			/* for chkufb() */
73071d4279SBram Moolenaar # endif
74071d4279SBram Moolenaar #endif
75071d4279SBram Moolenaar 
76071d4279SBram Moolenaar #define MEMFILE_PAGE_SIZE 4096		/* default page size */
77071d4279SBram Moolenaar 
78071d4279SBram Moolenaar static long_u	total_mem_used = 0;	/* total memory used for memfiles */
79071d4279SBram Moolenaar static int	dont_release = FALSE;	/* don't release blocks */
80071d4279SBram Moolenaar 
81071d4279SBram Moolenaar static void mf_ins_hash __ARGS((memfile_T *, bhdr_T *));
82071d4279SBram Moolenaar static void mf_rem_hash __ARGS((memfile_T *, bhdr_T *));
83071d4279SBram Moolenaar static bhdr_T *mf_find_hash __ARGS((memfile_T *, blocknr_T));
84071d4279SBram Moolenaar static void mf_ins_used __ARGS((memfile_T *, bhdr_T *));
85071d4279SBram Moolenaar static void mf_rem_used __ARGS((memfile_T *, bhdr_T *));
86071d4279SBram Moolenaar static bhdr_T *mf_release __ARGS((memfile_T *, int));
87071d4279SBram Moolenaar static bhdr_T *mf_alloc_bhdr __ARGS((memfile_T *, int));
88071d4279SBram Moolenaar static void mf_free_bhdr __ARGS((bhdr_T *));
89071d4279SBram Moolenaar static void mf_ins_free __ARGS((memfile_T *, bhdr_T *));
90071d4279SBram Moolenaar static bhdr_T *mf_rem_free __ARGS((memfile_T *));
91071d4279SBram Moolenaar static int  mf_read __ARGS((memfile_T *, bhdr_T *));
92071d4279SBram Moolenaar static int  mf_write __ARGS((memfile_T *, bhdr_T *));
93071d4279SBram Moolenaar static int  mf_trans_add __ARGS((memfile_T *, bhdr_T *));
94071d4279SBram Moolenaar static void mf_do_open __ARGS((memfile_T *, char_u *, int));
95071d4279SBram Moolenaar 
96071d4279SBram Moolenaar /*
97071d4279SBram Moolenaar  * The functions for using a memfile:
98071d4279SBram Moolenaar  *
99071d4279SBram Moolenaar  * mf_open()	    open a new or existing memfile
100071d4279SBram Moolenaar  * mf_open_file()   open a swap file for an existing memfile
101071d4279SBram Moolenaar  * mf_close()	    close (and delete) a memfile
102071d4279SBram Moolenaar  * mf_new()	    create a new block in a memfile and lock it
103071d4279SBram Moolenaar  * mf_get()	    get an existing block and lock it
104071d4279SBram Moolenaar  * mf_put()	    unlock a block, may be marked for writing
105071d4279SBram Moolenaar  * mf_free()	    remove a block
106071d4279SBram Moolenaar  * mf_sync()	    sync changed parts of memfile to disk
107071d4279SBram Moolenaar  * mf_release_all() release as much memory as possible
108071d4279SBram Moolenaar  * mf_trans_del()   may translate negative to positive block number
109071d4279SBram Moolenaar  * mf_fullname()    make file name full path (use before first :cd)
110071d4279SBram Moolenaar  */
111071d4279SBram Moolenaar 
112071d4279SBram Moolenaar /*
113071d4279SBram Moolenaar  * Open an existing or new memory block file.
114071d4279SBram Moolenaar  *
115071d4279SBram Moolenaar  *  fname:	name of file to use (NULL means no file at all)
116071d4279SBram Moolenaar  *		Note: fname must have been allocated, it is not copied!
117071d4279SBram Moolenaar  *			If opening the file fails, fname is freed.
118071d4279SBram Moolenaar  *  flags:	flags for open() call
119071d4279SBram Moolenaar  *
120071d4279SBram Moolenaar  *  If fname != NULL and file cannot be opened, fail.
121071d4279SBram Moolenaar  *
122071d4279SBram Moolenaar  * return value: identifier for this memory block file.
123071d4279SBram Moolenaar  */
124071d4279SBram Moolenaar     memfile_T *
125071d4279SBram Moolenaar mf_open(fname, flags)
126071d4279SBram Moolenaar     char_u	*fname;
127071d4279SBram Moolenaar     int		flags;
128071d4279SBram Moolenaar {
129071d4279SBram Moolenaar     memfile_T		*mfp;
130071d4279SBram Moolenaar     int			i;
131071d4279SBram Moolenaar     off_t		size;
132071d4279SBram Moolenaar #if defined(STATFS) && defined(UNIX) && !defined(__QNX__)
133071d4279SBram Moolenaar # define USE_FSTATFS
134071d4279SBram Moolenaar     struct STATFS	stf;
135071d4279SBram Moolenaar #endif
136071d4279SBram Moolenaar 
137071d4279SBram Moolenaar     if ((mfp = (memfile_T *)alloc((unsigned)sizeof(memfile_T))) == NULL)
138071d4279SBram Moolenaar 	return NULL;
139071d4279SBram Moolenaar 
140071d4279SBram Moolenaar     if (fname == NULL)	    /* no file for this memfile, use memory only */
141071d4279SBram Moolenaar     {
142071d4279SBram Moolenaar 	mfp->mf_fname = NULL;
143071d4279SBram Moolenaar 	mfp->mf_ffname = NULL;
144071d4279SBram Moolenaar 	mfp->mf_fd = -1;
145071d4279SBram Moolenaar     }
146071d4279SBram Moolenaar     else
147071d4279SBram Moolenaar     {
148071d4279SBram Moolenaar 	mf_do_open(mfp, fname, flags);	/* try to open the file */
149071d4279SBram Moolenaar 
150071d4279SBram Moolenaar 	/* if the file cannot be opened, return here */
151071d4279SBram Moolenaar 	if (mfp->mf_fd < 0)
152071d4279SBram Moolenaar 	{
153071d4279SBram Moolenaar 	    vim_free(mfp);
154071d4279SBram Moolenaar 	    return NULL;
155071d4279SBram Moolenaar 	}
156071d4279SBram Moolenaar     }
157071d4279SBram Moolenaar 
158071d4279SBram Moolenaar     mfp->mf_free_first = NULL;		/* free list is empty */
159071d4279SBram Moolenaar     mfp->mf_used_first = NULL;		/* used list is empty */
160071d4279SBram Moolenaar     mfp->mf_used_last = NULL;
161071d4279SBram Moolenaar     mfp->mf_dirty = FALSE;
162071d4279SBram Moolenaar     mfp->mf_used_count = 0;
163071d4279SBram Moolenaar     for (i = 0; i < MEMHASHSIZE; ++i)
164071d4279SBram Moolenaar     {
165071d4279SBram Moolenaar 	mfp->mf_hash[i] = NULL;		/* hash lists are empty */
166071d4279SBram Moolenaar 	mfp->mf_trans[i] = NULL;	/* trans lists are empty */
167071d4279SBram Moolenaar     }
168071d4279SBram Moolenaar     mfp->mf_page_size = MEMFILE_PAGE_SIZE;
169071d4279SBram Moolenaar 
170071d4279SBram Moolenaar #ifdef USE_FSTATFS
171071d4279SBram Moolenaar     /*
172071d4279SBram Moolenaar      * Try to set the page size equal to the block size of the device.
173071d4279SBram Moolenaar      * Speeds up I/O a lot.
174071d4279SBram Moolenaar      * When recovering, the actual block size will be retrieved from block 0
175071d4279SBram Moolenaar      * in ml_recover().  The size used here may be wrong, therefore
176071d4279SBram Moolenaar      * mf_blocknr_max must be rounded up.
177071d4279SBram Moolenaar      */
178071d4279SBram Moolenaar     if (mfp->mf_fd >= 0
179071d4279SBram Moolenaar 	    && fstatfs(mfp->mf_fd, &stf, sizeof(struct statfs), 0) == 0
180071d4279SBram Moolenaar 	    && stf.F_BSIZE >= MIN_SWAP_PAGE_SIZE
181071d4279SBram Moolenaar 	    && stf.F_BSIZE <= MAX_SWAP_PAGE_SIZE)
182071d4279SBram Moolenaar 	mfp->mf_page_size = stf.F_BSIZE;
183071d4279SBram Moolenaar #endif
184071d4279SBram Moolenaar 
185071d4279SBram Moolenaar     if (mfp->mf_fd < 0 || (flags & (O_TRUNC|O_EXCL))
186071d4279SBram Moolenaar 		      || (size = lseek(mfp->mf_fd, (off_t)0L, SEEK_END)) <= 0)
187071d4279SBram Moolenaar 	mfp->mf_blocknr_max = 0;	/* no file or empty file */
188071d4279SBram Moolenaar     else
189071d4279SBram Moolenaar 	mfp->mf_blocknr_max = (blocknr_T)((size + mfp->mf_page_size - 1)
190071d4279SBram Moolenaar 							 / mfp->mf_page_size);
191071d4279SBram Moolenaar     mfp->mf_blocknr_min = -1;
192071d4279SBram Moolenaar     mfp->mf_neg_count = 0;
193071d4279SBram Moolenaar     mfp->mf_infile_count = mfp->mf_blocknr_max;
194071d4279SBram Moolenaar     mfp->mf_used_count_max = p_mm * 1024 / mfp->mf_page_size;
195071d4279SBram Moolenaar 
196071d4279SBram Moolenaar     return mfp;
197071d4279SBram Moolenaar }
198071d4279SBram Moolenaar 
199071d4279SBram Moolenaar /*
200071d4279SBram Moolenaar  * Open a file for an existing memfile.  Used when updatecount set from 0 to
201071d4279SBram Moolenaar  * some value.
202071d4279SBram Moolenaar  * If the file already exists, this fails.
203071d4279SBram Moolenaar  * "fname" is the name of file to use (NULL means no file at all)
204071d4279SBram Moolenaar  * Note: "fname" must have been allocated, it is not copied!  If opening the
205071d4279SBram Moolenaar  * file fails, "fname" is freed.
206071d4279SBram Moolenaar  *
207071d4279SBram Moolenaar  * return value: FAIL if file could not be opened, OK otherwise
208071d4279SBram Moolenaar  */
209071d4279SBram Moolenaar     int
210071d4279SBram Moolenaar mf_open_file(mfp, fname)
211071d4279SBram Moolenaar     memfile_T	*mfp;
212071d4279SBram Moolenaar     char_u	*fname;
213071d4279SBram Moolenaar {
214071d4279SBram Moolenaar     mf_do_open(mfp, fname, O_RDWR|O_CREAT|O_EXCL); /* try to open the file */
215071d4279SBram Moolenaar 
216071d4279SBram Moolenaar     if (mfp->mf_fd < 0)
217071d4279SBram Moolenaar 	return FAIL;
218071d4279SBram Moolenaar 
219071d4279SBram Moolenaar     mfp->mf_dirty = TRUE;
220071d4279SBram Moolenaar     return OK;
221071d4279SBram Moolenaar }
222071d4279SBram Moolenaar 
223071d4279SBram Moolenaar /*
224071d4279SBram Moolenaar  * close a memory file and delete the associated file if 'del_file' is TRUE
225071d4279SBram Moolenaar  */
226071d4279SBram Moolenaar     void
227071d4279SBram Moolenaar mf_close(mfp, del_file)
228071d4279SBram Moolenaar     memfile_T	*mfp;
229071d4279SBram Moolenaar     int		del_file;
230071d4279SBram Moolenaar {
231071d4279SBram Moolenaar     bhdr_T	*hp, *nextp;
232071d4279SBram Moolenaar     NR_TRANS	*tp, *tpnext;
233071d4279SBram Moolenaar     int		i;
234071d4279SBram Moolenaar 
235071d4279SBram Moolenaar     if (mfp == NULL)		    /* safety check */
236071d4279SBram Moolenaar 	return;
237071d4279SBram Moolenaar     if (mfp->mf_fd >= 0)
238071d4279SBram Moolenaar     {
239071d4279SBram Moolenaar 	if (close(mfp->mf_fd) < 0)
240071d4279SBram Moolenaar 	    EMSG(_(e_swapclose));
241071d4279SBram Moolenaar     }
242071d4279SBram Moolenaar     if (del_file && mfp->mf_fname != NULL)
243071d4279SBram Moolenaar 	mch_remove(mfp->mf_fname);
244071d4279SBram Moolenaar 					    /* free entries in used list */
245071d4279SBram Moolenaar     for (hp = mfp->mf_used_first; hp != NULL; hp = nextp)
246071d4279SBram Moolenaar     {
247071d4279SBram Moolenaar 	total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
248071d4279SBram Moolenaar 	nextp = hp->bh_next;
249071d4279SBram Moolenaar 	mf_free_bhdr(hp);
250071d4279SBram Moolenaar     }
251071d4279SBram Moolenaar     while (mfp->mf_free_first != NULL)	    /* free entries in free list */
252071d4279SBram Moolenaar 	vim_free(mf_rem_free(mfp));
253071d4279SBram Moolenaar     for (i = 0; i < MEMHASHSIZE; ++i)	    /* free entries in trans lists */
254071d4279SBram Moolenaar 	for (tp = mfp->mf_trans[i]; tp != NULL; tp = tpnext)
255071d4279SBram Moolenaar 	{
256071d4279SBram Moolenaar 	    tpnext = tp->nt_next;
257071d4279SBram Moolenaar 	    vim_free(tp);
258071d4279SBram Moolenaar 	}
259071d4279SBram Moolenaar     vim_free(mfp->mf_fname);
260071d4279SBram Moolenaar     vim_free(mfp->mf_ffname);
261071d4279SBram Moolenaar     vim_free(mfp);
262071d4279SBram Moolenaar }
263071d4279SBram Moolenaar 
264071d4279SBram Moolenaar /*
265071d4279SBram Moolenaar  * Close the swap file for a memfile.  Used when 'swapfile' is reset.
266071d4279SBram Moolenaar  */
267071d4279SBram Moolenaar     void
268071d4279SBram Moolenaar mf_close_file(buf, getlines)
269071d4279SBram Moolenaar     buf_T	*buf;
270071d4279SBram Moolenaar     int		getlines;	/* get all lines into memory? */
271071d4279SBram Moolenaar {
272071d4279SBram Moolenaar     memfile_T	*mfp;
273071d4279SBram Moolenaar     linenr_T	lnum;
274071d4279SBram Moolenaar 
275071d4279SBram Moolenaar     mfp = buf->b_ml.ml_mfp;
276071d4279SBram Moolenaar     if (mfp == NULL || mfp->mf_fd < 0)		/* nothing to close */
277071d4279SBram Moolenaar 	return;
278071d4279SBram Moolenaar 
279071d4279SBram Moolenaar     if (getlines)
280071d4279SBram Moolenaar     {
281071d4279SBram Moolenaar 	/* get all blocks in memory by accessing all lines (clumsy!) */
282071d4279SBram Moolenaar 	dont_release = TRUE;
283071d4279SBram Moolenaar 	for (lnum = 1; lnum <= buf->b_ml.ml_line_count; ++lnum)
284071d4279SBram Moolenaar 	    (void)ml_get_buf(buf, lnum, FALSE);
285071d4279SBram Moolenaar 	dont_release = FALSE;
286071d4279SBram Moolenaar 	/* TODO: should check if all blocks are really in core */
287071d4279SBram Moolenaar     }
288071d4279SBram Moolenaar 
289071d4279SBram Moolenaar     if (close(mfp->mf_fd) < 0)			/* close the file */
290071d4279SBram Moolenaar 	EMSG(_(e_swapclose));
291071d4279SBram Moolenaar     mfp->mf_fd = -1;
292071d4279SBram Moolenaar 
293071d4279SBram Moolenaar     if (mfp->mf_fname != NULL)
294071d4279SBram Moolenaar     {
295071d4279SBram Moolenaar 	mch_remove(mfp->mf_fname);		/* delete the swap file */
296071d4279SBram Moolenaar 	vim_free(mfp->mf_fname);
297071d4279SBram Moolenaar 	vim_free(mfp->mf_ffname);
298071d4279SBram Moolenaar 	mfp->mf_fname = NULL;
299071d4279SBram Moolenaar 	mfp->mf_ffname = NULL;
300071d4279SBram Moolenaar     }
301071d4279SBram Moolenaar }
302071d4279SBram Moolenaar 
303071d4279SBram Moolenaar /*
304071d4279SBram Moolenaar  * Set new size for a memfile.  Used when block 0 of a swapfile has been read
305071d4279SBram Moolenaar  * and the size it indicates differs from what was guessed.
306071d4279SBram Moolenaar  */
307071d4279SBram Moolenaar     void
308071d4279SBram Moolenaar mf_new_page_size(mfp, new_size)
309071d4279SBram Moolenaar     memfile_T	*mfp;
310071d4279SBram Moolenaar     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 *
324071d4279SBram Moolenaar mf_new(mfp, negative, page_count)
325071d4279SBram Moolenaar     memfile_T	*mfp;
326071d4279SBram Moolenaar     int		negative;
327071d4279SBram Moolenaar     int		page_count;
328071d4279SBram Moolenaar {
329071d4279SBram Moolenaar     bhdr_T    *hp;	    /* new bhdr_T */
330071d4279SBram Moolenaar     bhdr_T    *freep;	    /* first block in free list */
331071d4279SBram Moolenaar     char_u  *p;
332071d4279SBram Moolenaar 
333071d4279SBram Moolenaar     /*
334071d4279SBram Moolenaar      * If we reached the maximum size for the used memory blocks, release one
335071d4279SBram Moolenaar      * If a bhdr_T is returned, use it and adjust the page_count if necessary.
336071d4279SBram Moolenaar      */
337071d4279SBram Moolenaar     hp = mf_release(mfp, page_count);
338071d4279SBram Moolenaar 
339071d4279SBram Moolenaar /*
340071d4279SBram Moolenaar  * Decide on the number to use:
341071d4279SBram Moolenaar  * If there is a free block, use its number.
342071d4279SBram Moolenaar  * Otherwise use mf_block_min for a negative number, mf_block_max for
343071d4279SBram Moolenaar  * a positive number.
344071d4279SBram Moolenaar  */
345071d4279SBram Moolenaar     freep = mfp->mf_free_first;
346071d4279SBram Moolenaar     if (!negative && freep != NULL && freep->bh_page_count >= page_count)
347071d4279SBram Moolenaar     {
348071d4279SBram Moolenaar 	/*
349071d4279SBram Moolenaar 	 * If the block in the free list has more pages, take only the number
350071d4279SBram Moolenaar 	 * of pages needed and allocate a new bhdr_T with data
351071d4279SBram Moolenaar 	 *
352071d4279SBram Moolenaar 	 * If the number of pages matches and mf_release did not return a bhdr_T,
353071d4279SBram Moolenaar 	 * use the bhdr_T from the free list and allocate the data
354071d4279SBram Moolenaar 	 *
355071d4279SBram Moolenaar 	 * If the number of pages matches and mf_release returned a bhdr_T,
356071d4279SBram Moolenaar 	 * just use the number and free the bhdr_T from the free list
357071d4279SBram Moolenaar 	 */
358071d4279SBram Moolenaar 	if (freep->bh_page_count > page_count)
359071d4279SBram Moolenaar 	{
360071d4279SBram Moolenaar 	    if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
361071d4279SBram Moolenaar 		return NULL;
362071d4279SBram Moolenaar 	    hp->bh_bnum = freep->bh_bnum;
363071d4279SBram Moolenaar 	    freep->bh_bnum += page_count;
364071d4279SBram Moolenaar 	    freep->bh_page_count -= page_count;
365071d4279SBram Moolenaar 	}
366071d4279SBram Moolenaar 	else if (hp == NULL)	    /* need to allocate memory for this block */
367071d4279SBram Moolenaar 	{
368071d4279SBram Moolenaar 	    if ((p = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
369071d4279SBram Moolenaar 		return NULL;
370071d4279SBram Moolenaar 	    hp = mf_rem_free(mfp);
371071d4279SBram Moolenaar 	    hp->bh_data = p;
372071d4279SBram Moolenaar 	}
373071d4279SBram Moolenaar 	else		    /* use the number, remove entry from free list */
374071d4279SBram Moolenaar 	{
375071d4279SBram Moolenaar 	    freep = mf_rem_free(mfp);
376071d4279SBram Moolenaar 	    hp->bh_bnum = freep->bh_bnum;
377071d4279SBram Moolenaar 	    vim_free(freep);
378071d4279SBram Moolenaar 	}
379071d4279SBram Moolenaar     }
380071d4279SBram Moolenaar     else	/* get a new number */
381071d4279SBram Moolenaar     {
382071d4279SBram Moolenaar 	if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
383071d4279SBram Moolenaar 	    return NULL;
384071d4279SBram Moolenaar 	if (negative)
385071d4279SBram Moolenaar 	{
386071d4279SBram Moolenaar 	    hp->bh_bnum = mfp->mf_blocknr_min--;
387071d4279SBram Moolenaar 	    mfp->mf_neg_count++;
388071d4279SBram Moolenaar 	}
389071d4279SBram Moolenaar 	else
390071d4279SBram Moolenaar 	{
391071d4279SBram Moolenaar 	    hp->bh_bnum = mfp->mf_blocknr_max;
392071d4279SBram Moolenaar 	    mfp->mf_blocknr_max += page_count;
393071d4279SBram Moolenaar 	}
394071d4279SBram Moolenaar     }
395071d4279SBram Moolenaar     hp->bh_flags = BH_LOCKED | BH_DIRTY;	/* new block is always dirty */
396071d4279SBram Moolenaar     mfp->mf_dirty = TRUE;
397071d4279SBram Moolenaar     hp->bh_page_count = page_count;
398071d4279SBram Moolenaar     mf_ins_used(mfp, hp);
399071d4279SBram Moolenaar     mf_ins_hash(mfp, hp);
400071d4279SBram Moolenaar 
401071d4279SBram Moolenaar     /*
402071d4279SBram Moolenaar      * Init the data to all zero, to avoid reading uninitialized data.
403071d4279SBram Moolenaar      * This also avoids that the passwd file ends up in the swap file!
404071d4279SBram Moolenaar      */
405071d4279SBram Moolenaar     (void)vim_memset((char *)(hp->bh_data), 0, (size_t)mfp->mf_page_size);
406071d4279SBram Moolenaar 
407071d4279SBram Moolenaar     return hp;
408071d4279SBram Moolenaar }
409071d4279SBram Moolenaar 
410071d4279SBram Moolenaar /*
411071d4279SBram Moolenaar  * get existing block 'nr' with 'page_count' pages
412071d4279SBram Moolenaar  *
413071d4279SBram Moolenaar  * Note: The caller should first check a negative nr with mf_trans_del()
414071d4279SBram Moolenaar  */
415071d4279SBram Moolenaar     bhdr_T *
416071d4279SBram Moolenaar mf_get(mfp, nr, page_count)
417071d4279SBram Moolenaar     memfile_T	*mfp;
418071d4279SBram Moolenaar     blocknr_T	nr;
419071d4279SBram Moolenaar     int		page_count;
420071d4279SBram Moolenaar {
421071d4279SBram Moolenaar     bhdr_T    *hp;
422071d4279SBram Moolenaar 						/* doesn't exist */
423071d4279SBram Moolenaar     if (nr >= mfp->mf_blocknr_max || nr <= mfp->mf_blocknr_min)
424071d4279SBram Moolenaar 	return NULL;
425071d4279SBram Moolenaar 
426071d4279SBram Moolenaar     /*
427071d4279SBram Moolenaar      * see if it is in the cache
428071d4279SBram Moolenaar      */
429071d4279SBram Moolenaar     hp = mf_find_hash(mfp, nr);
430071d4279SBram Moolenaar     if (hp == NULL)	/* not in the hash list */
431071d4279SBram Moolenaar     {
432071d4279SBram Moolenaar 	if (nr < 0 || nr >= mfp->mf_infile_count)   /* can't be in the file */
433071d4279SBram Moolenaar 	    return NULL;
434071d4279SBram Moolenaar 
435071d4279SBram Moolenaar 	/* could check here if the block is in the free list */
436071d4279SBram Moolenaar 
437071d4279SBram Moolenaar 	/*
438071d4279SBram Moolenaar 	 * Check if we need to flush an existing block.
439071d4279SBram Moolenaar 	 * If so, use that block.
440071d4279SBram Moolenaar 	 * If not, allocate a new block.
441071d4279SBram Moolenaar 	 */
442071d4279SBram Moolenaar 	hp = mf_release(mfp, page_count);
443071d4279SBram Moolenaar 	if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
444071d4279SBram Moolenaar 	    return NULL;
445071d4279SBram Moolenaar 
446071d4279SBram Moolenaar 	hp->bh_bnum = nr;
447071d4279SBram Moolenaar 	hp->bh_flags = 0;
448071d4279SBram Moolenaar 	hp->bh_page_count = page_count;
449071d4279SBram Moolenaar 	if (mf_read(mfp, hp) == FAIL)	    /* cannot read the block! */
450071d4279SBram Moolenaar 	{
451071d4279SBram Moolenaar 	    mf_free_bhdr(hp);
452071d4279SBram Moolenaar 	    return NULL;
453071d4279SBram Moolenaar 	}
454071d4279SBram Moolenaar     }
455071d4279SBram Moolenaar     else
456071d4279SBram Moolenaar     {
457071d4279SBram Moolenaar 	mf_rem_used(mfp, hp);	/* remove from list, insert in front below */
458071d4279SBram Moolenaar 	mf_rem_hash(mfp, hp);
459071d4279SBram Moolenaar     }
460071d4279SBram Moolenaar 
461071d4279SBram Moolenaar     hp->bh_flags |= BH_LOCKED;
462071d4279SBram Moolenaar     mf_ins_used(mfp, hp);	/* put in front of used list */
463071d4279SBram Moolenaar     mf_ins_hash(mfp, hp);	/* put in front of hash list */
464071d4279SBram Moolenaar 
465071d4279SBram Moolenaar     return hp;
466071d4279SBram Moolenaar }
467071d4279SBram Moolenaar 
468071d4279SBram Moolenaar /*
469071d4279SBram Moolenaar  * release the block *hp
470071d4279SBram Moolenaar  *
471071d4279SBram Moolenaar  *   dirty: Block must be written to file later
472071d4279SBram Moolenaar  *   infile: Block should be in file (needed for recovery)
473071d4279SBram Moolenaar  *
474071d4279SBram Moolenaar  *  no return value, function cannot fail
475071d4279SBram Moolenaar  */
476071d4279SBram Moolenaar     void
477071d4279SBram Moolenaar mf_put(mfp, hp, dirty, infile)
478071d4279SBram Moolenaar     memfile_T	*mfp;
479071d4279SBram Moolenaar     bhdr_T	*hp;
480071d4279SBram Moolenaar     int		dirty;
481071d4279SBram Moolenaar     int		infile;
482071d4279SBram Moolenaar {
483071d4279SBram Moolenaar     int		flags;
484071d4279SBram Moolenaar 
485071d4279SBram Moolenaar     flags = hp->bh_flags;
486071d4279SBram Moolenaar 
487071d4279SBram Moolenaar     if ((flags & BH_LOCKED) == 0)
488071d4279SBram Moolenaar 	EMSG(_("E293: block was not locked"));
489071d4279SBram Moolenaar     flags &= ~BH_LOCKED;
490071d4279SBram Moolenaar     if (dirty)
491071d4279SBram Moolenaar     {
492071d4279SBram Moolenaar 	flags |= BH_DIRTY;
493071d4279SBram Moolenaar 	mfp->mf_dirty = TRUE;
494071d4279SBram Moolenaar     }
495071d4279SBram Moolenaar     hp->bh_flags = flags;
496071d4279SBram Moolenaar     if (infile)
497071d4279SBram Moolenaar 	mf_trans_add(mfp, hp);	    /* may translate negative in positive nr */
498071d4279SBram Moolenaar }
499071d4279SBram Moolenaar 
500071d4279SBram Moolenaar /*
501071d4279SBram Moolenaar  * block *hp is no longer in used, may put it in the free list of memfile *mfp
502071d4279SBram Moolenaar  */
503071d4279SBram Moolenaar     void
504071d4279SBram Moolenaar mf_free(mfp, hp)
505071d4279SBram Moolenaar     memfile_T	*mfp;
506071d4279SBram Moolenaar     bhdr_T	*hp;
507071d4279SBram Moolenaar {
508071d4279SBram Moolenaar     vim_free(hp->bh_data);	/* free the memory */
509071d4279SBram Moolenaar     mf_rem_hash(mfp, hp);	/* get *hp out of the hash list */
510071d4279SBram Moolenaar     mf_rem_used(mfp, hp);	/* get *hp out of the used list */
511071d4279SBram Moolenaar     if (hp->bh_bnum < 0)
512071d4279SBram Moolenaar     {
513071d4279SBram Moolenaar 	vim_free(hp);		/* don't want negative numbers in free list */
514071d4279SBram Moolenaar 	mfp->mf_neg_count--;
515071d4279SBram Moolenaar     }
516071d4279SBram Moolenaar     else
517071d4279SBram Moolenaar 	mf_ins_free(mfp, hp);	/* put *hp in the free list */
518071d4279SBram Moolenaar }
519071d4279SBram Moolenaar 
52089f3727bSBram Moolenaar #if defined(__MORPHOS__) && defined(__libnix__)
521071d4279SBram Moolenaar /* function is missing in MorphOS libnix version */
522071d4279SBram Moolenaar extern unsigned long *__stdfiledes;
523071d4279SBram Moolenaar 
524071d4279SBram Moolenaar     static unsigned long
525071d4279SBram Moolenaar fdtofh(int filedescriptor)
526071d4279SBram Moolenaar {
527071d4279SBram Moolenaar     return __stdfiledes[filedescriptor];
528071d4279SBram Moolenaar }
529071d4279SBram Moolenaar #endif
530071d4279SBram Moolenaar 
531071d4279SBram Moolenaar /*
532071d4279SBram Moolenaar  * Sync the memory file *mfp to disk.
533071d4279SBram Moolenaar  * Flags:
534071d4279SBram Moolenaar  *  MFS_ALL	If not given, blocks with negative numbers are not synced,
535071d4279SBram Moolenaar  *		even when they are dirty!
536071d4279SBram Moolenaar  *  MFS_STOP	Stop syncing when a character becomes available, but sync at
537071d4279SBram Moolenaar  *		least one block.
538071d4279SBram Moolenaar  *  MFS_FLUSH	Make sure buffers are flushed to disk, so they will survive a
539071d4279SBram Moolenaar  *		system crash.
540071d4279SBram Moolenaar  *  MFS_ZERO	Only write block 0.
541071d4279SBram Moolenaar  *
542071d4279SBram Moolenaar  * Return FAIL for failure, OK otherwise
543071d4279SBram Moolenaar  */
544071d4279SBram Moolenaar     int
545071d4279SBram Moolenaar mf_sync(mfp, flags)
546071d4279SBram Moolenaar     memfile_T	*mfp;
547071d4279SBram Moolenaar     int		flags;
548071d4279SBram Moolenaar {
549071d4279SBram Moolenaar     int		status;
550071d4279SBram Moolenaar     bhdr_T	*hp;
551071d4279SBram Moolenaar #if defined(SYNC_DUP_CLOSE) && !defined(MSDOS)
552071d4279SBram Moolenaar     int		fd;
553071d4279SBram Moolenaar #endif
554071d4279SBram Moolenaar     int		got_int_save = got_int;
555071d4279SBram Moolenaar 
556071d4279SBram Moolenaar     if (mfp->mf_fd < 0)	    /* there is no file, nothing to do */
557071d4279SBram Moolenaar     {
558071d4279SBram Moolenaar 	mfp->mf_dirty = FALSE;
559071d4279SBram Moolenaar 	return FAIL;
560071d4279SBram Moolenaar     }
561071d4279SBram Moolenaar 
562071d4279SBram Moolenaar     /* Only a CTRL-C while writing will break us here, not one typed
563071d4279SBram Moolenaar      * previously. */
564071d4279SBram Moolenaar     got_int = FALSE;
565071d4279SBram Moolenaar 
566071d4279SBram Moolenaar     /*
567071d4279SBram Moolenaar      * sync from last to first (may reduce the probability of an inconsistent
568071d4279SBram Moolenaar      * file) If a write fails, it is very likely caused by a full filesystem.
569071d4279SBram Moolenaar      * Then we only try to write blocks within the existing file. If that also
570071d4279SBram Moolenaar      * fails then we give up.
571071d4279SBram Moolenaar      */
572071d4279SBram Moolenaar     status = OK;
573071d4279SBram Moolenaar     for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
574071d4279SBram Moolenaar 	if (((flags & MFS_ALL) || hp->bh_bnum >= 0)
575071d4279SBram Moolenaar 		&& (hp->bh_flags & BH_DIRTY)
576071d4279SBram Moolenaar 		&& (status == OK || (hp->bh_bnum >= 0
577071d4279SBram Moolenaar 		    && hp->bh_bnum < mfp->mf_infile_count)))
578071d4279SBram Moolenaar 	{
579071d4279SBram Moolenaar 	    if ((flags & MFS_ZERO) && hp->bh_bnum != 0)
580071d4279SBram Moolenaar 		continue;
581071d4279SBram Moolenaar 	    if (mf_write(mfp, hp) == FAIL)
582071d4279SBram Moolenaar 	    {
583071d4279SBram Moolenaar 		if (status == FAIL)	/* double error: quit syncing */
584071d4279SBram Moolenaar 		    break;
585071d4279SBram Moolenaar 		status = FAIL;
586071d4279SBram Moolenaar 	    }
587071d4279SBram Moolenaar 	    if (flags & MFS_STOP)
588071d4279SBram Moolenaar 	    {
589071d4279SBram Moolenaar 		/* Stop when char available now. */
590071d4279SBram Moolenaar 		if (ui_char_avail())
591071d4279SBram Moolenaar 		    break;
592071d4279SBram Moolenaar 	    }
593071d4279SBram Moolenaar 	    else
594071d4279SBram Moolenaar 		ui_breakcheck();
595071d4279SBram Moolenaar 	    if (got_int)
596071d4279SBram Moolenaar 		break;
597071d4279SBram Moolenaar 	}
598071d4279SBram Moolenaar 
599071d4279SBram Moolenaar     /*
600071d4279SBram Moolenaar      * If the whole list is flushed, the memfile is not dirty anymore.
601071d4279SBram Moolenaar      * In case of an error this flag is also set, to avoid trying all the time.
602071d4279SBram Moolenaar      */
603071d4279SBram Moolenaar     if (hp == NULL || status == FAIL)
604071d4279SBram Moolenaar 	mfp->mf_dirty = FALSE;
605071d4279SBram Moolenaar 
606071d4279SBram Moolenaar     if ((flags & MFS_FLUSH) && *p_sws != NUL)
607071d4279SBram Moolenaar     {
608071d4279SBram Moolenaar #if defined(UNIX)
609071d4279SBram Moolenaar # ifdef HAVE_FSYNC
610071d4279SBram Moolenaar 	/*
611071d4279SBram Moolenaar 	 * most Unixes have the very useful fsync() function, just what we need.
612071d4279SBram Moolenaar 	 * However, with OS/2 and EMX it is also available, but there are
613071d4279SBram Moolenaar 	 * reports of bad problems with it (a bug in HPFS.IFS).
614071d4279SBram Moolenaar 	 * So we disable use of it here in case someone tries to be smart
615071d4279SBram Moolenaar 	 * and changes os_os2_cfg.h... (even though there is no __EMX__ test
616071d4279SBram Moolenaar 	 * in the #if, as __EMX__ does not have sync(); we hope for a timely
617071d4279SBram Moolenaar 	 * sync from the system itself).
618071d4279SBram Moolenaar 	 */
619071d4279SBram Moolenaar #  if defined(__EMX__)
620071d4279SBram Moolenaar    error "Dont use fsync with EMX! Read emxdoc.doc or emxfix01.doc for info."
621071d4279SBram Moolenaar #  endif
622071d4279SBram Moolenaar 	if (STRCMP(p_sws, "fsync") == 0)
623071d4279SBram Moolenaar 	{
624071d4279SBram Moolenaar 	    if (fsync(mfp->mf_fd))
625071d4279SBram Moolenaar 		status = FAIL;
626071d4279SBram Moolenaar 	}
627071d4279SBram Moolenaar 	else
628071d4279SBram Moolenaar # endif
629071d4279SBram Moolenaar 	    /* OpenNT is strictly POSIX (Benzinger) */
630071d4279SBram Moolenaar 	    /* Tandem/Himalaya NSK-OSS doesn't have sync() */
631071d4279SBram Moolenaar # if defined(__OPENNT) || defined(__TANDEM)
632071d4279SBram Moolenaar 	    fflush(NULL);
633071d4279SBram Moolenaar # else
634071d4279SBram Moolenaar 	    sync();
635071d4279SBram Moolenaar # endif
636071d4279SBram Moolenaar #endif
637071d4279SBram Moolenaar #ifdef VMS
638071d4279SBram Moolenaar 	if (STRCMP(p_sws, "fsync") == 0)
639071d4279SBram Moolenaar 	{
640071d4279SBram Moolenaar 	    if (fsync(mfp->mf_fd))
641071d4279SBram Moolenaar 		status = FAIL;
642071d4279SBram Moolenaar 	}
643071d4279SBram Moolenaar #endif
644071d4279SBram Moolenaar #ifdef MSDOS
645071d4279SBram Moolenaar 	if (_dos_commit(mfp->mf_fd))
646071d4279SBram Moolenaar 	    status = FAIL;
647071d4279SBram Moolenaar #else
648071d4279SBram Moolenaar # ifdef SYNC_DUP_CLOSE
649071d4279SBram Moolenaar 	/*
650071d4279SBram Moolenaar 	 * Win32 is a bit more work: Duplicate the file handle and close it.
651071d4279SBram Moolenaar 	 * This should flush the file to disk.
652071d4279SBram Moolenaar 	 */
653071d4279SBram Moolenaar 	if ((fd = dup(mfp->mf_fd)) >= 0)
654071d4279SBram Moolenaar 	    close(fd);
655071d4279SBram Moolenaar # endif
656071d4279SBram Moolenaar #endif
657071d4279SBram Moolenaar #ifdef AMIGA
6585a6404cfSBram Moolenaar # if defined(__AROS__) || defined(__amigaos4__)
659071d4279SBram Moolenaar 	if (fsync(mfp->mf_fd) != 0)
660071d4279SBram Moolenaar 	    status = FAIL;
661071d4279SBram Moolenaar # else
662071d4279SBram Moolenaar 	/*
663071d4279SBram Moolenaar 	 * Flush() only exists for AmigaDos 2.0.
664071d4279SBram Moolenaar 	 * For 1.3 it should be done with close() + open(), but then the risk
665071d4279SBram Moolenaar 	 * is that the open() may fail and lose the file....
666071d4279SBram Moolenaar 	 */
667071d4279SBram Moolenaar #  ifdef FEAT_ARP
668071d4279SBram Moolenaar 	if (dos2)
669071d4279SBram Moolenaar #  endif
670071d4279SBram Moolenaar #  ifdef SASC
671071d4279SBram Moolenaar 	{
672071d4279SBram Moolenaar 	    struct UFB *fp = chkufb(mfp->mf_fd);
673071d4279SBram Moolenaar 
674071d4279SBram Moolenaar 	    if (fp != NULL)
675071d4279SBram Moolenaar 		Flush(fp->ufbfh);
676071d4279SBram Moolenaar 	}
677071d4279SBram Moolenaar #  else
678071d4279SBram Moolenaar #   if defined(_DCC) || defined(__GNUC__) || defined(__MORPHOS__)
679071d4279SBram Moolenaar 	{
68089f3727bSBram Moolenaar #    if defined(__GNUC__) && !defined(__MORPHOS__) && defined(__libnix__)
681071d4279SBram Moolenaar 	    /* Have function (in libnix at least),
682071d4279SBram Moolenaar 	     * but ain't got no prototype anywhere. */
683071d4279SBram Moolenaar 	    extern unsigned long fdtofh(int filedescriptor);
684071d4279SBram Moolenaar #    endif
68589f3727bSBram Moolenaar #    if !defined(__libnix__)
68689f3727bSBram Moolenaar 	    fflush(NULL);
68789f3727bSBram Moolenaar #    else
688071d4279SBram Moolenaar 	    BPTR fh = (BPTR)fdtofh(mfp->mf_fd);
689071d4279SBram Moolenaar 
690071d4279SBram Moolenaar 	    if (fh != 0)
691071d4279SBram Moolenaar 		Flush(fh);
69289f3727bSBram Moolenaar #    endif
693071d4279SBram Moolenaar 	}
694071d4279SBram Moolenaar #   else /* assume Manx */
695071d4279SBram Moolenaar 	    Flush(_devtab[mfp->mf_fd].fd);
696071d4279SBram Moolenaar #   endif
697071d4279SBram Moolenaar #  endif
698071d4279SBram Moolenaar # endif
699071d4279SBram Moolenaar #endif /* AMIGA */
700071d4279SBram Moolenaar     }
701071d4279SBram Moolenaar 
702071d4279SBram Moolenaar     got_int |= got_int_save;
703071d4279SBram Moolenaar 
704071d4279SBram Moolenaar     return status;
705071d4279SBram Moolenaar }
706071d4279SBram Moolenaar 
707071d4279SBram Moolenaar /*
708c32840f2SBram Moolenaar  * For all blocks in memory file *mfp that have a positive block number set
709c32840f2SBram Moolenaar  * the dirty flag.  These are blocks that need to be written to a newly
710c32840f2SBram Moolenaar  * created swapfile.
711c32840f2SBram Moolenaar  */
712c32840f2SBram Moolenaar     void
713c32840f2SBram Moolenaar mf_set_dirty(mfp)
714c32840f2SBram Moolenaar     memfile_T	*mfp;
715c32840f2SBram Moolenaar {
716c32840f2SBram Moolenaar     bhdr_T	*hp;
717c32840f2SBram Moolenaar 
718c32840f2SBram Moolenaar     for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
719c32840f2SBram Moolenaar 	if (hp->bh_bnum > 0)
720c32840f2SBram Moolenaar 	    hp->bh_flags |= BH_DIRTY;
721c32840f2SBram Moolenaar     mfp->mf_dirty = TRUE;
722c32840f2SBram Moolenaar }
723c32840f2SBram Moolenaar 
724c32840f2SBram Moolenaar /*
725071d4279SBram Moolenaar  * insert block *hp in front of hashlist of memfile *mfp
726071d4279SBram Moolenaar  */
727071d4279SBram Moolenaar     static void
728071d4279SBram Moolenaar mf_ins_hash(mfp, hp)
729071d4279SBram Moolenaar     memfile_T	*mfp;
730071d4279SBram Moolenaar     bhdr_T	*hp;
731071d4279SBram Moolenaar {
732071d4279SBram Moolenaar     bhdr_T	*hhp;
733071d4279SBram Moolenaar     int		hash;
734071d4279SBram Moolenaar 
735071d4279SBram Moolenaar     hash = MEMHASH(hp->bh_bnum);
736071d4279SBram Moolenaar     hhp = mfp->mf_hash[hash];
737071d4279SBram Moolenaar     hp->bh_hash_next = hhp;
738071d4279SBram Moolenaar     hp->bh_hash_prev = NULL;
739071d4279SBram Moolenaar     if (hhp != NULL)
740071d4279SBram Moolenaar 	hhp->bh_hash_prev = hp;
741071d4279SBram Moolenaar     mfp->mf_hash[hash] = hp;
742071d4279SBram Moolenaar }
743071d4279SBram Moolenaar 
744071d4279SBram Moolenaar /*
745071d4279SBram Moolenaar  * remove block *hp from hashlist of memfile list *mfp
746071d4279SBram Moolenaar  */
747071d4279SBram Moolenaar     static void
748071d4279SBram Moolenaar mf_rem_hash(mfp, hp)
749071d4279SBram Moolenaar     memfile_T	*mfp;
750071d4279SBram Moolenaar     bhdr_T	*hp;
751071d4279SBram Moolenaar {
752071d4279SBram Moolenaar     if (hp->bh_hash_prev == NULL)
753071d4279SBram Moolenaar 	mfp->mf_hash[MEMHASH(hp->bh_bnum)] = hp->bh_hash_next;
754071d4279SBram Moolenaar     else
755071d4279SBram Moolenaar 	hp->bh_hash_prev->bh_hash_next = hp->bh_hash_next;
756071d4279SBram Moolenaar 
757071d4279SBram Moolenaar     if (hp->bh_hash_next)
758071d4279SBram Moolenaar 	hp->bh_hash_next->bh_hash_prev = hp->bh_hash_prev;
759071d4279SBram Moolenaar }
760071d4279SBram Moolenaar 
761071d4279SBram Moolenaar /*
762071d4279SBram Moolenaar  * look in hash lists of memfile *mfp for block header with number 'nr'
763071d4279SBram Moolenaar  */
764071d4279SBram Moolenaar     static bhdr_T *
765071d4279SBram Moolenaar mf_find_hash(mfp, nr)
766071d4279SBram Moolenaar     memfile_T	*mfp;
767071d4279SBram Moolenaar     blocknr_T	nr;
768071d4279SBram Moolenaar {
769071d4279SBram Moolenaar     bhdr_T	*hp;
770071d4279SBram Moolenaar 
771071d4279SBram Moolenaar     for (hp = mfp->mf_hash[MEMHASH(nr)]; hp != NULL; hp = hp->bh_hash_next)
772071d4279SBram Moolenaar 	if (hp->bh_bnum == nr)
773071d4279SBram Moolenaar 	    break;
774071d4279SBram Moolenaar     return hp;
775071d4279SBram Moolenaar }
776071d4279SBram Moolenaar 
777071d4279SBram Moolenaar /*
778071d4279SBram Moolenaar  * insert block *hp in front of used list of memfile *mfp
779071d4279SBram Moolenaar  */
780071d4279SBram Moolenaar     static void
781071d4279SBram Moolenaar mf_ins_used(mfp, hp)
782071d4279SBram Moolenaar     memfile_T	*mfp;
783071d4279SBram Moolenaar     bhdr_T	*hp;
784071d4279SBram Moolenaar {
785071d4279SBram Moolenaar     hp->bh_next = mfp->mf_used_first;
786071d4279SBram Moolenaar     mfp->mf_used_first = hp;
787071d4279SBram Moolenaar     hp->bh_prev = NULL;
788071d4279SBram Moolenaar     if (hp->bh_next == NULL)	    /* list was empty, adjust last pointer */
789071d4279SBram Moolenaar 	mfp->mf_used_last = hp;
790071d4279SBram Moolenaar     else
791071d4279SBram Moolenaar 	hp->bh_next->bh_prev = hp;
792071d4279SBram Moolenaar     mfp->mf_used_count += hp->bh_page_count;
793071d4279SBram Moolenaar     total_mem_used += hp->bh_page_count * mfp->mf_page_size;
794071d4279SBram Moolenaar }
795071d4279SBram Moolenaar 
796071d4279SBram Moolenaar /*
797071d4279SBram Moolenaar  * remove block *hp from used list of memfile *mfp
798071d4279SBram Moolenaar  */
799071d4279SBram Moolenaar     static void
800071d4279SBram Moolenaar mf_rem_used(mfp, hp)
801071d4279SBram Moolenaar     memfile_T	*mfp;
802071d4279SBram Moolenaar     bhdr_T	*hp;
803071d4279SBram Moolenaar {
804071d4279SBram Moolenaar     if (hp->bh_next == NULL)	    /* last block in used list */
805071d4279SBram Moolenaar 	mfp->mf_used_last = hp->bh_prev;
806071d4279SBram Moolenaar     else
807071d4279SBram Moolenaar 	hp->bh_next->bh_prev = hp->bh_prev;
808071d4279SBram Moolenaar     if (hp->bh_prev == NULL)	    /* first block in used list */
809071d4279SBram Moolenaar 	mfp->mf_used_first = hp->bh_next;
810071d4279SBram Moolenaar     else
811071d4279SBram Moolenaar 	hp->bh_prev->bh_next = hp->bh_next;
812071d4279SBram Moolenaar     mfp->mf_used_count -= hp->bh_page_count;
813071d4279SBram Moolenaar     total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
814071d4279SBram Moolenaar }
815071d4279SBram Moolenaar 
816071d4279SBram Moolenaar /*
817071d4279SBram Moolenaar  * Release the least recently used block from the used list if the number
818071d4279SBram Moolenaar  * of used memory blocks gets to big.
819071d4279SBram Moolenaar  *
820071d4279SBram Moolenaar  * Return the block header to the caller, including the memory block, so
821071d4279SBram Moolenaar  * it can be re-used. Make sure the page_count is right.
822071d4279SBram Moolenaar  */
823071d4279SBram Moolenaar     static bhdr_T *
824071d4279SBram Moolenaar mf_release(mfp, page_count)
825071d4279SBram Moolenaar     memfile_T	*mfp;
826071d4279SBram Moolenaar     int		page_count;
827071d4279SBram Moolenaar {
828071d4279SBram Moolenaar     bhdr_T	*hp;
829071d4279SBram Moolenaar     int		need_release;
830071d4279SBram Moolenaar     buf_T	*buf;
831071d4279SBram Moolenaar 
832071d4279SBram Moolenaar     /* don't release while in mf_close_file() */
833071d4279SBram Moolenaar     if (dont_release)
834071d4279SBram Moolenaar 	return NULL;
835071d4279SBram Moolenaar 
836071d4279SBram Moolenaar     /*
837071d4279SBram Moolenaar      * Need to release a block if the number of blocks for this memfile is
838071d4279SBram Moolenaar      * higher than the maximum or total memory used is over 'maxmemtot'
839071d4279SBram Moolenaar      */
840071d4279SBram Moolenaar     need_release = ((mfp->mf_used_count >= mfp->mf_used_count_max)
841071d4279SBram Moolenaar 				  || (total_mem_used >> 10) >= (long_u)p_mmt);
842071d4279SBram Moolenaar 
843071d4279SBram Moolenaar     /*
844071d4279SBram Moolenaar      * Try to create a swap file if the amount of memory used is getting too
845071d4279SBram Moolenaar      * high.
846071d4279SBram Moolenaar      */
847071d4279SBram Moolenaar     if (mfp->mf_fd < 0 && need_release && p_uc)
848071d4279SBram Moolenaar     {
849071d4279SBram Moolenaar 	/* find for which buffer this memfile is */
850071d4279SBram Moolenaar 	for (buf = firstbuf; buf != NULL; buf = buf->b_next)
851071d4279SBram Moolenaar 	    if (buf->b_ml.ml_mfp == mfp)
852071d4279SBram Moolenaar 		break;
853071d4279SBram Moolenaar 	if (buf != NULL && buf->b_may_swap)
854071d4279SBram Moolenaar 	    ml_open_file(buf);
855071d4279SBram Moolenaar     }
856071d4279SBram Moolenaar 
857071d4279SBram Moolenaar     /*
858071d4279SBram Moolenaar      * don't release a block if
859071d4279SBram Moolenaar      *	there is no file for this memfile
860071d4279SBram Moolenaar      * or
861071d4279SBram Moolenaar      *	the number of blocks for this memfile is lower than the maximum
862071d4279SBram Moolenaar      *	  and
863071d4279SBram Moolenaar      *	total memory used is not up to 'maxmemtot'
864071d4279SBram Moolenaar      */
865071d4279SBram Moolenaar     if (mfp->mf_fd < 0 || !need_release)
866071d4279SBram Moolenaar 	return NULL;
867071d4279SBram Moolenaar 
868071d4279SBram Moolenaar     for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
869071d4279SBram Moolenaar 	if (!(hp->bh_flags & BH_LOCKED))
870071d4279SBram Moolenaar 	    break;
871071d4279SBram Moolenaar     if (hp == NULL)	/* not a single one that can be released */
872071d4279SBram Moolenaar 	return NULL;
873071d4279SBram Moolenaar 
874071d4279SBram Moolenaar     /*
875071d4279SBram Moolenaar      * If the block is dirty, write it.
876071d4279SBram Moolenaar      * If the write fails we don't free it.
877071d4279SBram Moolenaar      */
878071d4279SBram Moolenaar     if ((hp->bh_flags & BH_DIRTY) && mf_write(mfp, hp) == FAIL)
879071d4279SBram Moolenaar 	return NULL;
880071d4279SBram Moolenaar 
881071d4279SBram Moolenaar     mf_rem_used(mfp, hp);
882071d4279SBram Moolenaar     mf_rem_hash(mfp, hp);
883071d4279SBram Moolenaar 
884071d4279SBram Moolenaar     /*
885071d4279SBram Moolenaar      * If a bhdr_T is returned, make sure that the page_count of bh_data is
886071d4279SBram Moolenaar      * right
887071d4279SBram Moolenaar      */
888071d4279SBram Moolenaar     if (hp->bh_page_count != page_count)
889071d4279SBram Moolenaar     {
890071d4279SBram Moolenaar 	vim_free(hp->bh_data);
891071d4279SBram Moolenaar 	if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
892071d4279SBram Moolenaar 	{
893071d4279SBram Moolenaar 	    vim_free(hp);
894071d4279SBram Moolenaar 	    return NULL;
895071d4279SBram Moolenaar 	}
896071d4279SBram Moolenaar 	hp->bh_page_count = page_count;
897071d4279SBram Moolenaar     }
898071d4279SBram Moolenaar     return hp;
899071d4279SBram Moolenaar }
900071d4279SBram Moolenaar 
901071d4279SBram Moolenaar /*
902071d4279SBram Moolenaar  * release as many blocks as possible
903071d4279SBram Moolenaar  * Used in case of out of memory
904071d4279SBram Moolenaar  *
905071d4279SBram Moolenaar  * return TRUE if any memory was released
906071d4279SBram Moolenaar  */
907071d4279SBram Moolenaar     int
908071d4279SBram Moolenaar mf_release_all()
909071d4279SBram Moolenaar {
910071d4279SBram Moolenaar     buf_T	*buf;
911071d4279SBram Moolenaar     memfile_T	*mfp;
912071d4279SBram Moolenaar     bhdr_T	*hp;
913071d4279SBram Moolenaar     int		retval = FALSE;
914071d4279SBram Moolenaar 
915071d4279SBram Moolenaar     for (buf = firstbuf; buf != NULL; buf = buf->b_next)
916071d4279SBram Moolenaar     {
917071d4279SBram Moolenaar 	mfp = buf->b_ml.ml_mfp;
918071d4279SBram Moolenaar 	if (mfp != NULL)
919071d4279SBram Moolenaar 	{
920071d4279SBram Moolenaar 	    /* If no swap file yet, may open one */
921071d4279SBram Moolenaar 	    if (mfp->mf_fd < 0 && buf->b_may_swap)
922071d4279SBram Moolenaar 		ml_open_file(buf);
923071d4279SBram Moolenaar 
924071d4279SBram Moolenaar 	    /* only if there is a swapfile */
925071d4279SBram Moolenaar 	    if (mfp->mf_fd >= 0)
926071d4279SBram Moolenaar 	    {
927071d4279SBram Moolenaar 		for (hp = mfp->mf_used_last; hp != NULL; )
928071d4279SBram Moolenaar 		{
929071d4279SBram Moolenaar 		    if (!(hp->bh_flags & BH_LOCKED)
930071d4279SBram Moolenaar 			    && (!(hp->bh_flags & BH_DIRTY)
931071d4279SBram Moolenaar 				|| mf_write(mfp, hp) != FAIL))
932071d4279SBram Moolenaar 		    {
933071d4279SBram Moolenaar 			mf_rem_used(mfp, hp);
934071d4279SBram Moolenaar 			mf_rem_hash(mfp, hp);
935071d4279SBram Moolenaar 			mf_free_bhdr(hp);
936071d4279SBram Moolenaar 			hp = mfp->mf_used_last;	/* re-start, list was changed */
937071d4279SBram Moolenaar 			retval = TRUE;
938071d4279SBram Moolenaar 		    }
939071d4279SBram Moolenaar 		    else
940071d4279SBram Moolenaar 			hp = hp->bh_prev;
941071d4279SBram Moolenaar 		}
942071d4279SBram Moolenaar 	    }
943071d4279SBram Moolenaar 	}
944071d4279SBram Moolenaar     }
945071d4279SBram Moolenaar     return retval;
946071d4279SBram Moolenaar }
947071d4279SBram Moolenaar 
948071d4279SBram Moolenaar /*
949071d4279SBram Moolenaar  * Allocate a block header and a block of memory for it
950071d4279SBram Moolenaar  */
951071d4279SBram Moolenaar     static bhdr_T *
952071d4279SBram Moolenaar mf_alloc_bhdr(mfp, page_count)
953071d4279SBram Moolenaar     memfile_T	*mfp;
954071d4279SBram Moolenaar     int		page_count;
955071d4279SBram Moolenaar {
956071d4279SBram Moolenaar     bhdr_T	*hp;
957071d4279SBram Moolenaar 
958071d4279SBram Moolenaar     if ((hp = (bhdr_T *)alloc((unsigned)sizeof(bhdr_T))) != NULL)
959071d4279SBram Moolenaar     {
960071d4279SBram Moolenaar 	if ((hp->bh_data = (char_u *)alloc(mfp->mf_page_size * page_count))
961071d4279SBram Moolenaar 								      == NULL)
962071d4279SBram Moolenaar 	{
963071d4279SBram Moolenaar 	    vim_free(hp);	    /* not enough memory */
964071d4279SBram Moolenaar 	    return NULL;
965071d4279SBram Moolenaar 	}
966071d4279SBram Moolenaar 	hp->bh_page_count = page_count;
967071d4279SBram Moolenaar     }
968071d4279SBram Moolenaar     return hp;
969071d4279SBram Moolenaar }
970071d4279SBram Moolenaar 
971071d4279SBram Moolenaar /*
972071d4279SBram Moolenaar  * Free a block header and the block of memory for it
973071d4279SBram Moolenaar  */
974071d4279SBram Moolenaar     static void
975071d4279SBram Moolenaar mf_free_bhdr(hp)
976071d4279SBram Moolenaar     bhdr_T	*hp;
977071d4279SBram Moolenaar {
978071d4279SBram Moolenaar     vim_free(hp->bh_data);
979071d4279SBram Moolenaar     vim_free(hp);
980071d4279SBram Moolenaar }
981071d4279SBram Moolenaar 
982071d4279SBram Moolenaar /*
983071d4279SBram Moolenaar  * insert entry *hp in the free list
984071d4279SBram Moolenaar  */
985071d4279SBram Moolenaar     static void
986071d4279SBram Moolenaar mf_ins_free(mfp, hp)
987071d4279SBram Moolenaar     memfile_T	*mfp;
988071d4279SBram Moolenaar     bhdr_T	*hp;
989071d4279SBram Moolenaar {
990071d4279SBram Moolenaar     hp->bh_next = mfp->mf_free_first;
991071d4279SBram Moolenaar     mfp->mf_free_first = hp;
992071d4279SBram Moolenaar }
993071d4279SBram Moolenaar 
994071d4279SBram Moolenaar /*
995071d4279SBram Moolenaar  * remove the first entry from the free list and return a pointer to it
996071d4279SBram Moolenaar  * Note: caller must check that mfp->mf_free_first is not NULL!
997071d4279SBram Moolenaar  */
998071d4279SBram Moolenaar     static bhdr_T *
999071d4279SBram Moolenaar mf_rem_free(mfp)
1000071d4279SBram Moolenaar     memfile_T	*mfp;
1001071d4279SBram Moolenaar {
1002071d4279SBram Moolenaar     bhdr_T	*hp;
1003071d4279SBram Moolenaar 
1004071d4279SBram Moolenaar     hp = mfp->mf_free_first;
1005071d4279SBram Moolenaar     mfp->mf_free_first = hp->bh_next;
1006071d4279SBram Moolenaar     return hp;
1007071d4279SBram Moolenaar }
1008071d4279SBram Moolenaar 
1009071d4279SBram Moolenaar /*
1010071d4279SBram Moolenaar  * read a block from disk
1011071d4279SBram Moolenaar  *
1012071d4279SBram Moolenaar  * Return FAIL for failure, OK otherwise
1013071d4279SBram Moolenaar  */
1014071d4279SBram Moolenaar     static int
1015071d4279SBram Moolenaar mf_read(mfp, hp)
1016071d4279SBram Moolenaar     memfile_T	*mfp;
1017071d4279SBram Moolenaar     bhdr_T	*hp;
1018071d4279SBram Moolenaar {
1019071d4279SBram Moolenaar     off_t	offset;
1020071d4279SBram Moolenaar     unsigned	page_size;
1021071d4279SBram Moolenaar     unsigned	size;
1022071d4279SBram Moolenaar 
1023071d4279SBram Moolenaar     if (mfp->mf_fd < 0)	    /* there is no file, can't read */
1024071d4279SBram Moolenaar 	return FAIL;
1025071d4279SBram Moolenaar 
1026071d4279SBram Moolenaar     page_size = mfp->mf_page_size;
1027071d4279SBram Moolenaar     offset = (off_t)page_size * hp->bh_bnum;
1028071d4279SBram Moolenaar     size = page_size * hp->bh_page_count;
1029071d4279SBram Moolenaar     if (lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
1030071d4279SBram Moolenaar     {
1031*3f2d9814SBram Moolenaar 	PERROR(_("E294: Seek error in swap file read"));
1032071d4279SBram Moolenaar 	return FAIL;
1033071d4279SBram Moolenaar     }
1034071d4279SBram Moolenaar     if ((unsigned)vim_read(mfp->mf_fd, hp->bh_data, size) != size)
1035071d4279SBram Moolenaar     {
1036*3f2d9814SBram Moolenaar 	PERROR(_("E295: Read error in swap file"));
1037071d4279SBram Moolenaar 	return FAIL;
1038071d4279SBram Moolenaar     }
1039071d4279SBram Moolenaar     return OK;
1040071d4279SBram Moolenaar }
1041071d4279SBram Moolenaar 
1042071d4279SBram Moolenaar /*
1043071d4279SBram Moolenaar  * write a block to disk
1044071d4279SBram Moolenaar  *
1045071d4279SBram Moolenaar  * Return FAIL for failure, OK otherwise
1046071d4279SBram Moolenaar  */
1047071d4279SBram Moolenaar     static int
1048071d4279SBram Moolenaar mf_write(mfp, hp)
1049071d4279SBram Moolenaar     memfile_T	*mfp;
1050071d4279SBram Moolenaar     bhdr_T	*hp;
1051071d4279SBram Moolenaar {
1052071d4279SBram Moolenaar     off_t	offset;	    /* offset in the file */
1053071d4279SBram Moolenaar     blocknr_T	nr;	    /* block nr which is being written */
1054071d4279SBram Moolenaar     bhdr_T	*hp2;
1055071d4279SBram Moolenaar     unsigned	page_size;  /* number of bytes in a page */
1056071d4279SBram Moolenaar     unsigned	page_count; /* number of pages written */
1057071d4279SBram Moolenaar     unsigned	size;	    /* number of bytes written */
1058071d4279SBram Moolenaar 
1059071d4279SBram Moolenaar     if (mfp->mf_fd < 0)	    /* there is no file, can't write */
1060071d4279SBram Moolenaar 	return FAIL;
1061071d4279SBram Moolenaar 
1062071d4279SBram Moolenaar     if (hp->bh_bnum < 0)	/* must assign file block number */
1063071d4279SBram Moolenaar 	if (mf_trans_add(mfp, hp) == FAIL)
1064071d4279SBram Moolenaar 	    return FAIL;
1065071d4279SBram Moolenaar 
1066071d4279SBram Moolenaar     page_size = mfp->mf_page_size;
1067071d4279SBram Moolenaar 
1068071d4279SBram Moolenaar     /*
1069071d4279SBram Moolenaar      * We don't want gaps in the file. Write the blocks in front of *hp
1070071d4279SBram Moolenaar      * to extend the file.
1071071d4279SBram Moolenaar      * If block 'mf_infile_count' is not in the hash list, it has been
1072071d4279SBram Moolenaar      * freed. Fill the space in the file with data from the current block.
1073071d4279SBram Moolenaar      */
1074071d4279SBram Moolenaar     for (;;)
1075071d4279SBram Moolenaar     {
1076071d4279SBram Moolenaar 	nr = hp->bh_bnum;
1077071d4279SBram Moolenaar 	if (nr > mfp->mf_infile_count)		/* beyond end of file */
1078071d4279SBram Moolenaar 	{
1079071d4279SBram Moolenaar 	    nr = mfp->mf_infile_count;
1080071d4279SBram Moolenaar 	    hp2 = mf_find_hash(mfp, nr);	/* NULL catched below */
1081071d4279SBram Moolenaar 	}
1082071d4279SBram Moolenaar 	else
1083071d4279SBram Moolenaar 	    hp2 = hp;
1084071d4279SBram Moolenaar 
1085071d4279SBram Moolenaar 	offset = (off_t)page_size * nr;
1086071d4279SBram Moolenaar 	if (lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
1087071d4279SBram Moolenaar 	{
1088*3f2d9814SBram Moolenaar 	    PERROR(_("E296: Seek error in swap file write"));
1089071d4279SBram Moolenaar 	    return FAIL;
1090071d4279SBram Moolenaar 	}
1091071d4279SBram Moolenaar 	if (hp2 == NULL)	    /* freed block, fill with dummy data */
1092071d4279SBram Moolenaar 	    page_count = 1;
1093071d4279SBram Moolenaar 	else
1094071d4279SBram Moolenaar 	    page_count = hp2->bh_page_count;
1095071d4279SBram Moolenaar 	size = page_size * page_count;
1096071d4279SBram Moolenaar 	if ((unsigned)vim_write(mfp->mf_fd,
1097071d4279SBram Moolenaar 	     (hp2 == NULL ? hp : hp2)->bh_data, size) != size)
1098071d4279SBram Moolenaar 	{
1099071d4279SBram Moolenaar 	    /*
1100071d4279SBram Moolenaar 	     * Avoid repeating the error message, this mostly happens when the
1101071d4279SBram Moolenaar 	     * disk is full. We give the message again only after a succesful
1102071d4279SBram Moolenaar 	     * write or when hitting a key. We keep on trying, in case some
1103071d4279SBram Moolenaar 	     * space becomes available.
1104071d4279SBram Moolenaar 	     */
1105071d4279SBram Moolenaar 	    if (!did_swapwrite_msg)
1106071d4279SBram Moolenaar 		EMSG(_("E297: Write error in swap file"));
1107071d4279SBram Moolenaar 	    did_swapwrite_msg = TRUE;
1108071d4279SBram Moolenaar 	    return FAIL;
1109071d4279SBram Moolenaar 	}
1110071d4279SBram Moolenaar 	did_swapwrite_msg = FALSE;
1111071d4279SBram Moolenaar 	if (hp2 != NULL)		    /* written a non-dummy block */
1112071d4279SBram Moolenaar 	    hp2->bh_flags &= ~BH_DIRTY;
1113071d4279SBram Moolenaar 					    /* appended to the file */
1114071d4279SBram Moolenaar 	if (nr + (blocknr_T)page_count > mfp->mf_infile_count)
1115071d4279SBram Moolenaar 	    mfp->mf_infile_count = nr + page_count;
1116071d4279SBram Moolenaar 	if (nr == hp->bh_bnum)		    /* written the desired block */
1117071d4279SBram Moolenaar 	    break;
1118071d4279SBram Moolenaar     }
1119071d4279SBram Moolenaar     return OK;
1120071d4279SBram Moolenaar }
1121071d4279SBram Moolenaar 
1122071d4279SBram Moolenaar /*
1123071d4279SBram Moolenaar  * Make block number for *hp positive and add it to the translation list
1124071d4279SBram Moolenaar  *
1125071d4279SBram Moolenaar  * Return FAIL for failure, OK otherwise
1126071d4279SBram Moolenaar  */
1127071d4279SBram Moolenaar     static int
1128071d4279SBram Moolenaar mf_trans_add(mfp, hp)
1129071d4279SBram Moolenaar     memfile_T	*mfp;
1130071d4279SBram Moolenaar     bhdr_T	*hp;
1131071d4279SBram Moolenaar {
1132071d4279SBram Moolenaar     bhdr_T	*freep;
1133071d4279SBram Moolenaar     blocknr_T	new_bnum;
1134071d4279SBram Moolenaar     int		hash;
1135071d4279SBram Moolenaar     NR_TRANS	*np;
1136071d4279SBram Moolenaar     int		page_count;
1137071d4279SBram Moolenaar 
1138071d4279SBram Moolenaar     if (hp->bh_bnum >= 0)		    /* it's already positive */
1139071d4279SBram Moolenaar 	return OK;
1140071d4279SBram Moolenaar 
1141071d4279SBram Moolenaar     if ((np = (NR_TRANS *)alloc((unsigned)sizeof(NR_TRANS))) == NULL)
1142071d4279SBram Moolenaar 	return FAIL;
1143071d4279SBram Moolenaar 
1144071d4279SBram Moolenaar /*
1145071d4279SBram Moolenaar  * get a new number for the block.
1146071d4279SBram Moolenaar  * If the first item in the free list has sufficient pages, use its number
1147071d4279SBram Moolenaar  * Otherwise use mf_blocknr_max.
1148071d4279SBram Moolenaar  */
1149071d4279SBram Moolenaar     freep = mfp->mf_free_first;
1150071d4279SBram Moolenaar     page_count = hp->bh_page_count;
1151071d4279SBram Moolenaar     if (freep != NULL && freep->bh_page_count >= page_count)
1152071d4279SBram Moolenaar     {
1153071d4279SBram Moolenaar 	new_bnum = freep->bh_bnum;
1154071d4279SBram Moolenaar 	/*
1155071d4279SBram Moolenaar 	 * If the page count of the free block was larger, recude it.
1156071d4279SBram Moolenaar 	 * If the page count matches, remove the block from the free list
1157071d4279SBram Moolenaar 	 */
1158071d4279SBram Moolenaar 	if (freep->bh_page_count > page_count)
1159071d4279SBram Moolenaar 	{
1160071d4279SBram Moolenaar 	    freep->bh_bnum += page_count;
1161071d4279SBram Moolenaar 	    freep->bh_page_count -= page_count;
1162071d4279SBram Moolenaar 	}
1163071d4279SBram Moolenaar 	else
1164071d4279SBram Moolenaar 	{
1165071d4279SBram Moolenaar 	    freep = mf_rem_free(mfp);
1166071d4279SBram Moolenaar 	    vim_free(freep);
1167071d4279SBram Moolenaar 	}
1168071d4279SBram Moolenaar     }
1169071d4279SBram Moolenaar     else
1170071d4279SBram Moolenaar     {
1171071d4279SBram Moolenaar 	new_bnum = mfp->mf_blocknr_max;
1172071d4279SBram Moolenaar 	mfp->mf_blocknr_max += page_count;
1173071d4279SBram Moolenaar     }
1174071d4279SBram Moolenaar 
1175071d4279SBram Moolenaar     np->nt_old_bnum = hp->bh_bnum;	    /* adjust number */
1176071d4279SBram Moolenaar     np->nt_new_bnum = new_bnum;
1177071d4279SBram Moolenaar 
1178071d4279SBram Moolenaar     mf_rem_hash(mfp, hp);		    /* remove from old hash list */
1179071d4279SBram Moolenaar     hp->bh_bnum = new_bnum;
1180071d4279SBram Moolenaar     mf_ins_hash(mfp, hp);		    /* insert in new hash list */
1181071d4279SBram Moolenaar 
1182071d4279SBram Moolenaar     hash = MEMHASH(np->nt_old_bnum);	    /* insert in trans list */
1183071d4279SBram Moolenaar     np->nt_next = mfp->mf_trans[hash];
1184071d4279SBram Moolenaar     mfp->mf_trans[hash] = np;
1185071d4279SBram Moolenaar     if (np->nt_next != NULL)
1186071d4279SBram Moolenaar 	np->nt_next->nt_prev = np;
1187071d4279SBram Moolenaar     np->nt_prev = NULL;
1188071d4279SBram Moolenaar 
1189071d4279SBram Moolenaar     return OK;
1190071d4279SBram Moolenaar }
1191071d4279SBram Moolenaar 
1192071d4279SBram Moolenaar /*
1193071d4279SBram Moolenaar  * Lookup a tranlation from the trans lists and delete the entry
1194071d4279SBram Moolenaar  *
1195071d4279SBram Moolenaar  * Return the positive new number when found, the old number when not found
1196071d4279SBram Moolenaar  */
1197071d4279SBram Moolenaar     blocknr_T
1198071d4279SBram Moolenaar mf_trans_del(mfp, old_nr)
1199071d4279SBram Moolenaar     memfile_T	*mfp;
1200071d4279SBram Moolenaar     blocknr_T	old_nr;
1201071d4279SBram Moolenaar {
1202071d4279SBram Moolenaar     int		hash;
1203071d4279SBram Moolenaar     NR_TRANS	*np;
1204071d4279SBram Moolenaar     blocknr_T	new_bnum;
1205071d4279SBram Moolenaar 
1206071d4279SBram Moolenaar     hash = MEMHASH(old_nr);
1207071d4279SBram Moolenaar     for (np = mfp->mf_trans[hash]; np != NULL; np = np->nt_next)
1208071d4279SBram Moolenaar 	if (np->nt_old_bnum == old_nr)
1209071d4279SBram Moolenaar 	    break;
1210071d4279SBram Moolenaar     if (np == NULL)		/* not found */
1211071d4279SBram Moolenaar 	return old_nr;
1212071d4279SBram Moolenaar 
1213071d4279SBram Moolenaar     mfp->mf_neg_count--;
1214071d4279SBram Moolenaar     new_bnum = np->nt_new_bnum;
1215071d4279SBram Moolenaar     if (np->nt_prev != NULL)		/* remove entry from the trans list */
1216071d4279SBram Moolenaar 	np->nt_prev->nt_next = np->nt_next;
1217071d4279SBram Moolenaar     else
1218071d4279SBram Moolenaar 	mfp->mf_trans[hash] = np->nt_next;
1219071d4279SBram Moolenaar     if (np->nt_next != NULL)
1220071d4279SBram Moolenaar 	np->nt_next->nt_prev = np->nt_prev;
1221071d4279SBram Moolenaar     vim_free(np);
1222071d4279SBram Moolenaar 
1223071d4279SBram Moolenaar     return new_bnum;
1224071d4279SBram Moolenaar }
1225071d4279SBram Moolenaar 
1226071d4279SBram Moolenaar /*
1227071d4279SBram Moolenaar  * Set mfp->mf_ffname according to mfp->mf_fname and some other things.
1228071d4279SBram Moolenaar  * Only called when creating or renaming the swapfile.	Either way it's a new
1229071d4279SBram Moolenaar  * name so we must work out the full path name.
1230071d4279SBram Moolenaar  */
1231071d4279SBram Moolenaar     void
1232071d4279SBram Moolenaar mf_set_ffname(mfp)
1233071d4279SBram Moolenaar     memfile_T	*mfp;
1234071d4279SBram Moolenaar {
1235071d4279SBram Moolenaar     mfp->mf_ffname = FullName_save(mfp->mf_fname, FALSE);
1236071d4279SBram Moolenaar }
1237071d4279SBram Moolenaar 
1238071d4279SBram Moolenaar /*
1239071d4279SBram Moolenaar  * Make the name of the file used for the memfile a full path.
1240071d4279SBram Moolenaar  * Used before doing a :cd
1241071d4279SBram Moolenaar  */
1242071d4279SBram Moolenaar     void
1243071d4279SBram Moolenaar mf_fullname(mfp)
1244071d4279SBram Moolenaar     memfile_T	*mfp;
1245071d4279SBram Moolenaar {
1246071d4279SBram Moolenaar     if (mfp != NULL && mfp->mf_fname != NULL && mfp->mf_ffname != NULL)
1247071d4279SBram Moolenaar     {
1248071d4279SBram Moolenaar 	vim_free(mfp->mf_fname);
1249071d4279SBram Moolenaar 	mfp->mf_fname = mfp->mf_ffname;
1250071d4279SBram Moolenaar 	mfp->mf_ffname = NULL;
1251071d4279SBram Moolenaar     }
1252071d4279SBram Moolenaar }
1253071d4279SBram Moolenaar 
1254071d4279SBram Moolenaar /*
1255071d4279SBram Moolenaar  * return TRUE if there are any translations pending for 'mfp'
1256071d4279SBram Moolenaar  */
1257071d4279SBram Moolenaar     int
1258071d4279SBram Moolenaar mf_need_trans(mfp)
1259071d4279SBram Moolenaar     memfile_T	*mfp;
1260071d4279SBram Moolenaar {
1261071d4279SBram Moolenaar     return (mfp->mf_fname != NULL && mfp->mf_neg_count > 0);
1262071d4279SBram Moolenaar }
1263071d4279SBram Moolenaar 
1264071d4279SBram Moolenaar /*
1265071d4279SBram Moolenaar  * Open a swap file for a memfile.
1266071d4279SBram Moolenaar  * The "fname" must be in allocated memory, and is consumed (also when an
1267071d4279SBram Moolenaar  * error occurs).
1268071d4279SBram Moolenaar  */
1269071d4279SBram Moolenaar     static void
1270071d4279SBram Moolenaar mf_do_open(mfp, fname, flags)
1271071d4279SBram Moolenaar     memfile_T	*mfp;
1272071d4279SBram Moolenaar     char_u	*fname;
1273071d4279SBram Moolenaar     int		flags;		/* flags for open() */
1274071d4279SBram Moolenaar {
1275071d4279SBram Moolenaar #ifdef HAVE_LSTAT
1276071d4279SBram Moolenaar     struct stat sb;
1277071d4279SBram Moolenaar #endif
1278071d4279SBram Moolenaar 
1279071d4279SBram Moolenaar     mfp->mf_fname = fname;
1280071d4279SBram Moolenaar 
1281071d4279SBram Moolenaar     /*
1282071d4279SBram Moolenaar      * Get the full path name before the open, because this is
1283071d4279SBram Moolenaar      * not possible after the open on the Amiga.
1284071d4279SBram Moolenaar      * fname cannot be NameBuff, because it must have been allocated.
1285071d4279SBram Moolenaar      */
1286071d4279SBram Moolenaar     mf_set_ffname(mfp);
1287071d4279SBram Moolenaar #if defined(MSDOS) || defined(MSWIN) || defined(RISCOS)
1288071d4279SBram Moolenaar     /*
1289071d4279SBram Moolenaar      * A ":!cd e:xxx" may change the directory without us knowning, use the
1290071d4279SBram Moolenaar      * full pathname always.  Careful: This frees fname!
1291071d4279SBram Moolenaar      */
1292071d4279SBram Moolenaar     mf_fullname(mfp);
1293071d4279SBram Moolenaar #endif
1294071d4279SBram Moolenaar 
1295071d4279SBram Moolenaar #ifdef HAVE_LSTAT
1296071d4279SBram Moolenaar     /*
1297071d4279SBram Moolenaar      * Extra security check: When creating a swap file it really shouldn't
1298071d4279SBram Moolenaar      * exist yet.  If there is a symbolic link, this is most likely an attack.
1299071d4279SBram Moolenaar      */
1300071d4279SBram Moolenaar     if ((flags & O_CREAT) && mch_lstat((char *)mfp->mf_fname, &sb) >= 0)
1301071d4279SBram Moolenaar     {
1302071d4279SBram Moolenaar 	mfp->mf_fd = -1;
1303071d4279SBram Moolenaar 	EMSG(_("E300: Swap file already exists (symlink attack?)"));
1304071d4279SBram Moolenaar     }
1305071d4279SBram Moolenaar     else
1306071d4279SBram Moolenaar #endif
1307071d4279SBram Moolenaar     {
1308071d4279SBram Moolenaar 	/*
1309071d4279SBram Moolenaar 	 * try to open the file
1310071d4279SBram Moolenaar 	 */
1311a5792f58SBram Moolenaar 	flags |= O_EXTRA | O_NOFOLLOW;
1312071d4279SBram Moolenaar #ifdef WIN32
1313071d4279SBram Moolenaar 	/* Prevent handle inheritance that cause problems with Cscope
1314071d4279SBram Moolenaar 	 * (swap file may not be deleted if cscope connection was open after
1315071d4279SBram Moolenaar 	 * the file) */
1316071d4279SBram Moolenaar 	flags |= O_NOINHERIT;
1317071d4279SBram Moolenaar #endif
1318071d4279SBram Moolenaar 	mfp->mf_fd = mch_open_rw((char *)mfp->mf_fname, flags);
1319071d4279SBram Moolenaar     }
1320071d4279SBram Moolenaar 
1321071d4279SBram Moolenaar     /*
1322071d4279SBram Moolenaar      * If the file cannot be opened, use memory only
1323071d4279SBram Moolenaar      */
1324071d4279SBram Moolenaar     if (mfp->mf_fd < 0)
1325071d4279SBram Moolenaar     {
1326071d4279SBram Moolenaar 	vim_free(mfp->mf_fname);
1327071d4279SBram Moolenaar 	vim_free(mfp->mf_ffname);
1328071d4279SBram Moolenaar 	mfp->mf_fname = NULL;
1329071d4279SBram Moolenaar 	mfp->mf_ffname = NULL;
1330071d4279SBram Moolenaar     }
1331071d4279SBram Moolenaar     else
1332071d4279SBram Moolenaar 	mch_hide(mfp->mf_fname);    /* try setting the 'hidden' flag */
1333071d4279SBram Moolenaar }
1334