Annotation of src/usr.bin/vim/memfile.c, Revision 1.4
1.4 ! downsj 1: /* $OpenBSD: memfile.c,v 1.3 1996/10/14 03:55:15 downsj Exp $ */
1.1 downsj 2: /* vi:set ts=4 sw=4:
3: *
4: * VIM - Vi IMproved by Bram Moolenaar
5: *
6: * Do ":help uganda" in Vim to read copying and usage conditions.
7: * Do ":help credits" in Vim to see a list of people who contributed.
8: */
9:
10: /* for debugging */
11: #define CHECK(c, s) if (c) printf(s)
12:
13: /*
14: * memfile.c: Contains the functions for handling blocks of memory which can
15: * be stored in a file. This is the implementation of a sort of virtual memory.
16: *
17: * A memfile consists of a sequence of blocks. The blocks numbered from 0
18: * upwards have been assigned a place in the actual file. The block number
19: * is equal to the page number in the file. The
20: * blocks with negative numbers are currently in memory only. They can be
21: * assigned a place in the file when too much memory is being used. At that
22: * moment they get a new, positive, number. A list is used for translation of
23: * negative to positive numbers.
24: *
25: * The size of a block is a multiple of a page size, normally the page size of
26: * the device the file is on. Most blocks are 1 page long. A Block of multiple
27: * pages is used for a line that does not fit in a single page.
28: *
29: * Each block can be in memory and/or in a file. The block stays in memory
30: * as long as it is locked. If it is no longer locked it can be swapped out to
31: * the file. It is only written to the file if it has been changed.
32: *
33: * Under normal operation the file is created when opening the memory file and
34: * deleted when closing the memory file. Only with recovery an existing memory
35: * file is opened.
36: */
37:
38: #if defined MSDOS || defined WIN32
39: # include <io.h> /* for lseek(), must be before vim.h */
40: #endif
41:
42: #include "vim.h"
43: #include "globals.h"
44: #include "proto.h"
45: #include "option.h"
46: #ifdef HAVE_FCNTL_H
47: # include <fcntl.h>
48: #endif
49:
50: /*
1.3 downsj 51: * Some systems have the page size in statfs.f_bsize, some in stat.st_blksize
1.1 downsj 52: */
1.3 downsj 53: #ifdef HAVE_ST_BLKSIZE
1.1 downsj 54: # define STATFS stat
55: # define F_BSIZE st_blksize
56: # define fstatfs(fd, buf, len, nul) fstat((fd), (buf))
1.3 downsj 57: #else
58: # ifdef HAVE_SYS_STATFS_H
59: # include <sys/statfs.h>
60: # define STATFS statfs
61: # define F_BSIZE f_bsize
62: # ifdef MINT /* do we still need this? */
63: # define fstatfs(fd, buf, len, nul) fstat((fd), (buf))
64: # endif
65: # endif
1.1 downsj 66: #endif
67:
68: /*
69: * for Amiga Dos 2.0x we use Flush
70: */
71: #ifdef AMIGA
72: # ifndef NO_ARP
73: extern int dos2; /* this is in amiga.c */
74: # endif
75: # ifdef SASC
76: # include <proto/dos.h>
77: # include <ios1.h> /* for chkufb() */
78: # endif
79: #endif
80:
81: #define MEMFILE_PAGE_SIZE 4096 /* default page size */
82:
83: static long total_mem_used = 0; /* total memory used for memfiles */
84:
85: static void mf_ins_hash __ARGS((MEMFILE *, BHDR *));
86: static void mf_rem_hash __ARGS((MEMFILE *, BHDR *));
87: static BHDR *mf_find_hash __ARGS((MEMFILE *, blocknr_t));
88: static void mf_ins_used __ARGS((MEMFILE *, BHDR *));
89: static void mf_rem_used __ARGS((MEMFILE *, BHDR *));
90: static BHDR *mf_release __ARGS((MEMFILE *, int));
91: static BHDR *mf_alloc_bhdr __ARGS((MEMFILE *, int));
92: static void mf_free_bhdr __ARGS((BHDR *));
93: static void mf_ins_free __ARGS((MEMFILE *, BHDR *));
94: static BHDR *mf_rem_free __ARGS((MEMFILE *));
95: static int mf_read __ARGS((MEMFILE *, BHDR *));
96: static int mf_write __ARGS((MEMFILE *, BHDR *));
97: static int mf_trans_add __ARGS((MEMFILE *, BHDR *));
98: static void mf_do_open __ARGS((MEMFILE *, char_u *, int));
99:
100: /*
101: * The functions for using a memfile:
102: *
103: * mf_open() open a new or existing memfile
104: * mf_open_file() open a swap file for an existing memfile
105: * mf_close() close (and delete) a memfile
106: * mf_new() create a new block in a memfile and lock it
107: * mf_get() get an existing block and lock it
108: * mf_put() unlock a block, may be marked for writing
109: * mf_free() remove a block
110: * mf_sync() sync changed parts of memfile to disk
111: * mf_release_all() release as much memory as possible
112: * mf_trans_del() may translate negative to positive block number
113: * mf_fullname() make file name full path (use before first :cd)
114: */
115:
116: /*
117: * mf_open: open an existing or new memory block file
118: *
119: * fname: name of file to use (NULL means no file at all)
120: * Note: fname must have been allocated, it is not copied!
121: * If opening the file fails, fname is NOT freed.
122: * trunc_file: if TRUE: file should be truncated when opening
123: *
124: * If fname != NULL and file cannot be opened, fail.
125: *
126: * return value: identifier for this memory block file.
127: */
128: MEMFILE *
129: mf_open(fname, trunc_file)
130: char_u *fname;
131: int trunc_file;
132: {
133: MEMFILE *mfp;
134: int i;
1.2 downsj 135: off_t size;
1.3 downsj 136: #if defined(STATFS) && defined(UNIX) && !defined(__QNX__)
137: # define USE_FSTATFS
1.1 downsj 138: struct STATFS stf;
139: #endif
140:
141: if ((mfp = (MEMFILE *)alloc((unsigned)sizeof(MEMFILE))) == NULL)
142: return NULL;
143:
144: if (fname == NULL) /* no file for this memfile, use memory only */
145: {
146: mfp->mf_fname = NULL;
147: mfp->mf_xfname = NULL;
148: mfp->mf_fd = -1;
149: }
150: else
151: {
152: mf_do_open(mfp, fname, trunc_file); /* try to open the file */
153:
154: /* if the file cannot be opened, return here */
155: if (mfp->mf_fd < 0)
156: {
157: vim_free(mfp);
158: return NULL;
159: }
160: }
161:
162: mfp->mf_free_first = NULL; /* free list is empty */
163: mfp->mf_used_first = NULL; /* used list is empty */
164: mfp->mf_used_last = NULL;
165: mfp->mf_dirty = FALSE;
166: mfp->mf_used_count = 0;
167: for (i = 0; i < MEMHASHSIZE; ++i)
168: {
169: mfp->mf_hash[i] = NULL; /* hash lists are empty */
170: mfp->mf_trans[i] = NULL; /* trans lists are empty */
171: }
172: mfp->mf_page_size = MEMFILE_PAGE_SIZE;
173:
1.3 downsj 174: #ifdef USE_FSTATFS
1.1 downsj 175: /*
176: * Try to set the page size equal to the block size of the device.
177: * Speeds up I/O a lot.
178: * NOTE: minimal block size depends on size of block 0 data! It's not done
179: * with a sizeof(), because block 0 is defined in memline.c (Sorry).
180: * The maximal block size is arbitrary.
181: */
182: if (mfp->mf_fd >= 0 &&
183: fstatfs(mfp->mf_fd, &stf, sizeof(struct statfs), 0) == 0 &&
184: stf.F_BSIZE >= 1048 && stf.F_BSIZE <= 50000)
185: mfp->mf_page_size = stf.F_BSIZE;
186: #endif
187:
188: if (mfp->mf_fd < 0 || trunc_file ||
189: (size = lseek(mfp->mf_fd, 0L, SEEK_END)) <= 0)
190: mfp->mf_blocknr_max = 0; /* no file or empty file */
191: else
192: mfp->mf_blocknr_max = size / mfp->mf_page_size;
193: mfp->mf_blocknr_min = -1;
194: mfp->mf_neg_count = 0;
195: mfp->mf_infile_count = mfp->mf_blocknr_max;
196: if (mfp->mf_fd < 0)
197: mfp->mf_used_count_max = 0; /* no limit */
198: else
199: mfp->mf_used_count_max = p_mm * 1024 / mfp->mf_page_size;
200:
201: return mfp;
202: }
203:
204: /*
205: * mf_open_file: open a file for an existing memfile. Used when updatecount
206: * set from 0 to some value.
207: *
208: * fname: name of file to use (NULL means no file at all)
209: * Note: fname must have been allocated, it is not copied!
210: * If opening the file fails, fname is NOT freed.
211: *
212: * return value: FAIL if file could not be opened, OK otherwise
213: */
214: int
215: mf_open_file(mfp, fname)
216: MEMFILE *mfp;
217: char_u *fname;
218: {
219: mf_do_open(mfp, fname, TRUE); /* try to open the file */
220:
221: if (mfp->mf_fd < 0)
222: return FAIL;
223:
224: mfp->mf_dirty = TRUE;
225: return OK;
226: }
227:
228: /*
229: * close a memory file and delete the associated file if 'del_file' is TRUE
230: */
231: void
232: mf_close(mfp, del_file)
233: MEMFILE *mfp;
234: int del_file;
235: {
236: BHDR *hp, *nextp;
237: NR_TRANS *tp, *tpnext;
238: int i;
239:
240: if (mfp == NULL) /* safety check */
241: return;
242: if (mfp->mf_fd >= 0)
243: {
244: if (close(mfp->mf_fd) < 0)
245: EMSG("Close error on swap file");
246: }
247: if (del_file && mfp->mf_fname != NULL)
248: vim_remove(mfp->mf_fname);
249: /* free entries in used list */
250: for (hp = mfp->mf_used_first; hp != NULL; hp = nextp)
251: {
252: total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
253: nextp = hp->bh_next;
254: mf_free_bhdr(hp);
255: }
256: while (mfp->mf_free_first != NULL) /* free entries in free list */
257: vim_free(mf_rem_free(mfp));
258: for (i = 0; i < MEMHASHSIZE; ++i) /* free entries in trans lists */
259: for (tp = mfp->mf_trans[i]; tp != NULL; tp = tpnext)
260: {
261: tpnext = tp->nt_next;
262: vim_free(tp);
263: }
264: vim_free(mfp->mf_fname);
265: vim_free(mfp->mf_xfname);
266: vim_free(mfp);
267: }
268:
269: /*
270: * get a new block
271: *
272: * negative: TRUE if negative block number desired (data block)
273: */
274: BHDR *
275: mf_new(mfp, negative, page_count)
276: MEMFILE *mfp;
277: int negative;
278: int page_count;
279: {
280: BHDR *hp; /* new BHDR */
281: BHDR *freep; /* first block in free list */
282: char_u *p;
283:
284: /*
285: * If we reached the maximum size for the used memory blocks, release one
286: * If a BHDR is returned, use it and adjust the page_count if necessary.
287: */
288: hp = mf_release(mfp, page_count);
289:
290: /*
291: * Decide on the number to use:
292: * If there is a free block, use its number.
293: * Otherwise use mf_block_min for a negative number, mf_block_max for
294: * a positive number.
295: */
296: freep = mfp->mf_free_first;
297: if (!negative && freep != NULL && freep->bh_page_count >= page_count)
298: {
299: /*
300: * If the block in the free list has more pages, take only the number
301: * of pages needed and allocate a new BHDR with data
302: *
303: * If the number of pages matches and mf_release did not return a BHDR,
304: * use the BHDR from the free list and allocate the data
305: *
306: * If the number of pages matches and mf_release returned a BHDR,
307: * just use the number and free the BHDR from the free list
308: */
309: if (freep->bh_page_count > page_count)
310: {
311: if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
312: return NULL;
313: hp->bh_bnum = freep->bh_bnum;
314: freep->bh_bnum += page_count;
315: freep->bh_page_count -= page_count;
316: }
317: else if (hp == NULL) /* need to allocate memory for this block */
318: {
319: if ((p = (char_u *)alloc(mfp->mf_page_size * page_count)) == NULL)
320: return NULL;
321: hp = mf_rem_free(mfp);
322: hp->bh_data = p;
323: }
324: else /* use the number, remove entry from free list */
325: {
326: freep = mf_rem_free(mfp);
327: hp->bh_bnum = freep->bh_bnum;
328: vim_free(freep);
329: }
330: }
331: else /* get a new number */
332: {
333: if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
334: return NULL;
335: if (negative)
336: {
337: hp->bh_bnum = mfp->mf_blocknr_min--;
338: mfp->mf_neg_count++;
339: }
340: else
341: {
342: hp->bh_bnum = mfp->mf_blocknr_max;
343: mfp->mf_blocknr_max += page_count;
344: }
345: }
346: hp->bh_flags = BH_LOCKED | BH_DIRTY; /* new block is always dirty */
347: mfp->mf_dirty = TRUE;
348: hp->bh_page_count = page_count;
349: mf_ins_used(mfp, hp);
350: mf_ins_hash(mfp, hp);
351:
1.2 downsj 352: /*
353: * Init the data to all zero, to avoid reading uninitialized data.
354: * This also avoids that the passwd file ends up in the swap file!
355: */
356: (void)vim_memset((char *)(hp->bh_data), 0, (size_t)mfp->mf_page_size);
357:
1.1 downsj 358: return hp;
359: }
360:
361: /*
362: * get existing block 'nr' with 'page_count' pages
363: *
364: * Note: The caller should first check a negative nr with mf_trans_del()
365: */
366: BHDR *
367: mf_get(mfp, nr, page_count)
368: MEMFILE *mfp;
369: blocknr_t nr;
370: int page_count;
371: {
372: BHDR *hp;
373: /* doesn't exist */
374: if (nr >= mfp->mf_blocknr_max || nr <= mfp->mf_blocknr_min)
375: return NULL;
376:
377: /*
378: * see if it is in the cache
379: */
380: hp = mf_find_hash(mfp, nr);
381: if (hp == NULL) /* not in the hash list */
382: {
383: if (nr < 0 || nr >= mfp->mf_infile_count) /* can't be in the file */
384: return NULL;
385:
386: /* could check here if the block is in the free list */
387:
388: /*
389: * Check if we need to flush an existing block.
390: * If so, use that block.
391: * If not, allocate a new block.
392: */
393: hp = mf_release(mfp, page_count);
394: if (hp == NULL && (hp = mf_alloc_bhdr(mfp, page_count)) == NULL)
395: return NULL;
396:
397: hp->bh_bnum = nr;
398: hp->bh_flags = 0;
399: hp->bh_page_count = page_count;
400: if (mf_read(mfp, hp) == FAIL) /* cannot read the block! */
401: {
402: mf_free_bhdr(hp);
403: return NULL;
404: }
405: }
406: else
407: {
408: mf_rem_used(mfp, hp); /* remove from list, insert in front below */
409: mf_rem_hash(mfp, hp);
410: }
411:
412: hp->bh_flags |= BH_LOCKED;
413: mf_ins_used(mfp, hp); /* put in front of used list */
414: mf_ins_hash(mfp, hp); /* put in front of hash list */
415:
416: return hp;
417: }
418:
419: /*
420: * release the block *hp
421: *
422: * dirty: Block must be written to file later
423: * infile: Block should be in file (needed for recovery)
424: *
425: * no return value, function cannot fail
426: */
427: void
428: mf_put(mfp, hp, dirty, infile)
429: MEMFILE *mfp;
430: BHDR *hp;
431: int dirty;
432: int infile;
433: {
434: int flags;
435:
436: flags = hp->bh_flags;
437: CHECK((flags & BH_LOCKED) == 0, "block was not locked");
438: flags &= ~BH_LOCKED;
439: if (dirty)
440: {
441: flags |= BH_DIRTY;
442: mfp->mf_dirty = TRUE;
443: }
444: hp->bh_flags = flags;
445: if (infile)
446: mf_trans_add(mfp, hp); /* may translate negative in positive nr */
447: }
448:
449: /*
450: * block *hp is no longer in used, may put it in the free list of memfile *mfp
451: */
452: void
453: mf_free(mfp, hp)
454: MEMFILE *mfp;
455: BHDR *hp;
456: {
457: vim_free(hp->bh_data); /* free the memory */
458: mf_rem_hash(mfp, hp); /* get *hp out of the hash list */
459: mf_rem_used(mfp, hp); /* get *hp out of the used list */
460: if (hp->bh_bnum < 0)
461: {
462: vim_free(hp); /* don't want negative numbers in free list */
463: mfp->mf_neg_count--;
464: }
465: else
466: mf_ins_free(mfp, hp); /* put *hp in the free list */
467: }
468:
469: /*
470: * sync the memory file *mfp to disk
471: * if 'all' is FALSE blocks with negative numbers are not synced, even when
472: * they are dirty!
473: * if 'check_char' is TRUE, stop syncing when a character becomes available,
474: * but sync at least one block.
475: * if 'do_fsync' is TRUE make sure buffers are flushed to disk, so they will
476: * survive a system crash.
477: *
478: * Return FAIL for failure, OK otherwise
479: */
480: int
481: mf_sync(mfp, all, check_char, do_fsync)
482: MEMFILE *mfp;
483: int all;
484: int check_char;
485: int do_fsync;
486: {
487: int status;
488: BHDR *hp;
489: #ifdef SYNC_DUP_CLOSE
490: int fd;
491: #endif
492:
493: if (mfp->mf_fd < 0) /* there is no file, nothing to do */
494: {
495: mfp->mf_dirty = FALSE;
496: return FAIL;
497: }
498:
499: /*
500: * sync from last to first (may reduce the probability of an inconsistent
501: * file) If a write fails, it is very likely caused by a full filesystem.
502: * Then we only try to write blocks within the existing file. If that also
503: * fails then we give up.
504: */
505: status = OK;
506: for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
507: if ((all || hp->bh_bnum >= 0) && (hp->bh_flags & BH_DIRTY) &&
508: (status == OK || (hp->bh_bnum >= 0 &&
509: hp->bh_bnum < mfp->mf_infile_count)))
510: {
511: if (mf_write(mfp, hp) == FAIL)
512: {
513: if (status == FAIL) /* double error: quit syncing */
514: break;
515: status = FAIL;
516: }
517: if (check_char && mch_char_avail()) /* char available now */
518: break;
519: }
520:
521: /*
522: * If the whole list is flushed, the memfile is not dirty anymore.
523: * In case of an error this flag is also set, to avoid trying all the time.
524: */
525: if (hp == NULL || status == FAIL)
526: mfp->mf_dirty = FALSE;
527:
528: if (do_fsync && *p_sws != NUL)
529: {
530: #if defined(UNIX)
531: # ifdef HAVE_FSYNC
532: /*
533: * most Unixes have the very useful fsync() function, just what we need.
534: * However, with OS/2 and EMX it is also available, but there are
535: * reports of bad problems with it (a bug in HPFS.IFS).
536: * So we disable use of it here in case someone tries to be smart
537: * and changes conf_os2.h... (even though there is no __EMX__ test
538: * in the #if, as __EMX__ does not have sync(); we hope for a timely
539: * sync from the system itself).
540: */
541: # if defined(__EMX__)
542: error "Dont use fsync with EMX! Read emxdoc.doc or emxfix01.doc for info."
543: # endif
544: if (STRCMP(p_sws, "fsync") == 0)
545: {
546: if (fsync(mfp->mf_fd))
547: status = FAIL;
548: }
549: else
550: # endif
551: sync();
552: #endif
553: #ifdef DJGPP
554: if (_dos_commit(mfp->mf_fd))
555: status = FAIL;
556: #else
557: # ifdef SYNC_DUP_CLOSE
558: /*
559: * MSdos is a bit more work: Duplicate the file handle and close it.
560: * This should flush the file to disk.
561: */
562: if ((fd = dup(mfp->mf_fd)) >= 0)
563: close(fd);
564: # endif
565: #endif
566: #ifdef AMIGA
567: /*
568: * Flush() only exists for AmigaDos 2.0.
569: * For 1.3 it should be done with close() + open(), but then the risk
570: * is that the open() may fail and lose the file....
571: */
572: # ifndef NO_ARP
573: if (dos2)
574: # endif
575: # ifdef SASC
576: {
577: struct UFB *fp = chkufb(mfp->mf_fd);
578:
579: if (fp != NULL)
580: Flush(fp->ufbfh);
581: }
582: # else
583: # ifdef _DCC
584: {
585: BPTR fh = (BPTR)fdtofh(mfp->mf_fd);
586:
587: if (fh != 0)
588: Flush(fh);
589: }
590: # else /* assume Manx */
591: Flush(_devtab[mfp->mf_fd].fd);
592: # endif
593: # endif
594: #endif /* AMIGA */
595: }
596:
597: return status;
598: }
599:
600: /*
601: * insert block *hp in front of hashlist of memfile *mfp
602: */
603: static void
604: mf_ins_hash(mfp, hp)
605: MEMFILE *mfp;
606: BHDR *hp;
607: {
608: BHDR *hhp;
609: int hash;
610:
611: hash = MEMHASH(hp->bh_bnum);
612: hhp = mfp->mf_hash[hash];
613: hp->bh_hash_next = hhp;
614: hp->bh_hash_prev = NULL;
615: if (hhp != NULL)
616: hhp->bh_hash_prev = hp;
617: mfp->mf_hash[hash] = hp;
618: }
619:
620: /*
621: * remove block *hp from hashlist of memfile list *mfp
622: */
623: static void
624: mf_rem_hash(mfp, hp)
625: MEMFILE *mfp;
626: BHDR *hp;
627: {
628: if (hp->bh_hash_prev == NULL)
629: mfp->mf_hash[MEMHASH(hp->bh_bnum)] = hp->bh_hash_next;
630: else
631: hp->bh_hash_prev->bh_hash_next = hp->bh_hash_next;
632:
633: if (hp->bh_hash_next)
634: hp->bh_hash_next->bh_hash_prev = hp->bh_hash_prev;
635: }
636:
637: /*
638: * look in hash lists of memfile *mfp for block header with number 'nr'
639: */
640: static BHDR *
641: mf_find_hash(mfp, nr)
642: MEMFILE *mfp;
643: blocknr_t nr;
644: {
645: BHDR *hp;
646:
647: for (hp = mfp->mf_hash[MEMHASH(nr)]; hp != NULL; hp = hp->bh_hash_next)
648: if (hp->bh_bnum == nr)
649: break;
650: return hp;
651: }
652:
653: /*
654: * insert block *hp in front of used list of memfile *mfp
655: */
656: static void
657: mf_ins_used(mfp, hp)
658: MEMFILE *mfp;
659: BHDR *hp;
660: {
661: hp->bh_next = mfp->mf_used_first;
662: mfp->mf_used_first = hp;
663: hp->bh_prev = NULL;
664: if (hp->bh_next == NULL) /* list was empty, adjust last pointer */
665: mfp->mf_used_last = hp;
666: else
667: hp->bh_next->bh_prev = hp;
668: mfp->mf_used_count += hp->bh_page_count;
669: total_mem_used += hp->bh_page_count * mfp->mf_page_size;
670: }
671:
672: /*
673: * remove block *hp from used list of memfile *mfp
674: */
675: static void
676: mf_rem_used(mfp, hp)
677: MEMFILE *mfp;
678: BHDR *hp;
679: {
680: if (hp->bh_next == NULL) /* last block in used list */
681: mfp->mf_used_last = hp->bh_prev;
682: else
683: hp->bh_next->bh_prev = hp->bh_prev;
684: if (hp->bh_prev == NULL) /* first block in used list */
685: mfp->mf_used_first = hp->bh_next;
686: else
687: hp->bh_prev->bh_next = hp->bh_next;
688: mfp->mf_used_count -= hp->bh_page_count;
689: total_mem_used -= hp->bh_page_count * mfp->mf_page_size;
690: }
691:
692: /*
693: * Release the least recently used block from the used list if the number
694: * of used memory blocks gets to big.
695: *
696: * Return the block header to the caller, including the memory block, so
697: * it can be re-used. Make sure the page_count is right.
698: */
699: static BHDR *
700: mf_release(mfp, page_count)
701: MEMFILE *mfp;
702: int page_count;
703: {
704: BHDR *hp;
705:
706: /*
707: * don't release a block if
708: * there is no file for this memfile
709: * or
710: * there is no limit to the number of blocks for this memfile or
711: * the maximum is not reached yet
712: * and
713: * total memory used is not up to 'maxmemtot'
714: */
715: if (mfp->mf_fd < 0 || ((mfp->mf_used_count < mfp->mf_used_count_max ||
716: mfp->mf_used_count_max == 0) &&
717: (total_mem_used >> 10) < p_mmt))
718: return NULL;
719:
720: for (hp = mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
721: if (!(hp->bh_flags & BH_LOCKED))
722: break;
723: if (hp == NULL) /* not a single one that can be released */
724: return NULL;
725:
726: /*
727: * If the block is dirty, write it.
728: * If the write fails we don't free it.
729: */
730: if ((hp->bh_flags & BH_DIRTY) && mf_write(mfp, hp) == FAIL)
731: return NULL;
732:
733: mf_rem_used(mfp, hp);
734: mf_rem_hash(mfp, hp);
735:
736: /*
737: * If a BHDR is returned, make sure that the page_count of bh_data is right
738: */
739: if (hp->bh_page_count != page_count)
740: {
741: vim_free(hp->bh_data);
742: if ((hp->bh_data = alloc(mfp->mf_page_size * page_count)) == NULL)
743: {
744: vim_free(hp);
745: return NULL;
746: }
747: hp->bh_page_count = page_count;
748: }
749: return hp;
750: }
751:
752: /*
753: * release as many blocks as possible
754: * Used in case of out of memory
755: *
756: * return TRUE if any memory was released
757: */
758: int
759: mf_release_all()
760: {
761: BUF *buf;
762: MEMFILE *mfp;
763: BHDR *hp;
764: int retval = FALSE;
765:
766: for (buf = firstbuf; buf != NULL; buf = buf->b_next)
767: {
768: mfp = buf->b_ml.ml_mfp;
769: if (mfp != NULL && mfp->mf_fd >= 0) /* only if there is a memfile with a file */
770: for (hp = mfp->mf_used_last; hp != NULL; )
771: {
772: if (!(hp->bh_flags & BH_LOCKED) &&
773: (!(hp->bh_flags & BH_DIRTY) || mf_write(mfp, hp) != FAIL))
774: {
775: mf_rem_used(mfp, hp);
776: mf_rem_hash(mfp, hp);
777: mf_free_bhdr(hp);
778: hp = mfp->mf_used_last; /* re-start, list was changed */
779: retval = TRUE;
780: }
781: else
782: hp = hp->bh_prev;
783: }
784: }
785: return retval;
786: }
787:
788: /*
789: * Allocate a block header and a block of memory for it
790: */
791: static BHDR *
792: mf_alloc_bhdr(mfp, page_count)
793: MEMFILE *mfp;
794: int page_count;
795: {
796: BHDR *hp;
797:
798: if ((hp = (BHDR *)alloc((unsigned)sizeof(BHDR))) != NULL)
799: {
800: if ((hp->bh_data = (char_u *)alloc(mfp->mf_page_size * page_count))
801: == NULL)
802: {
803: vim_free(hp); /* not enough memory */
804: return NULL;
805: }
806: hp->bh_page_count = page_count;
807: }
808: return hp;
809: }
810:
811: /*
812: * Free a block header and the block of memory for it
813: */
814: static void
815: mf_free_bhdr(hp)
816: BHDR *hp;
817: {
818: vim_free(hp->bh_data);
819: vim_free(hp);
820: }
821:
822: /*
823: * insert entry *hp in the free list
824: */
825: static void
826: mf_ins_free(mfp, hp)
827: MEMFILE *mfp;
828: BHDR *hp;
829: {
830: hp->bh_next = mfp->mf_free_first;
831: mfp->mf_free_first = hp;
832: }
833:
834: /*
835: * remove the first entry from the free list and return a pointer to it
836: * Note: caller must check that mfp->mf_free_first is not NULL!
837: */
838: static BHDR *
839: mf_rem_free(mfp)
840: MEMFILE *mfp;
841: {
842: BHDR *hp;
843:
844: hp = mfp->mf_free_first;
845: mfp->mf_free_first = hp->bh_next;
846: return hp;
847: }
848:
849: /*
850: * read a block from disk
851: *
852: * Return FAIL for failure, OK otherwise
853: */
854: static int
855: mf_read(mfp, hp)
856: MEMFILE *mfp;
857: BHDR *hp;
858: {
1.2 downsj 859: off_t offset;
1.1 downsj 860: unsigned page_size;
861: unsigned size;
862:
863: if (mfp->mf_fd < 0) /* there is no file, can't read */
864: return FAIL;
865:
866: page_size = mfp->mf_page_size;
867: offset = page_size * hp->bh_bnum;
868: size = page_size * hp->bh_page_count;
1.2 downsj 869: if (lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
1.1 downsj 870: {
871: EMSG("Seek error in swap file read");
872: return FAIL;
873: }
874: if ((unsigned)read(mfp->mf_fd, (char *)hp->bh_data, (size_t)size) != size)
875: {
876: EMSG("Read error in swap file");
877: return FAIL;
878: }
879: return OK;
880: }
881:
882: /*
883: * write a block to disk
884: *
885: * Return FAIL for failure, OK otherwise
886: */
887: static int
888: mf_write(mfp, hp)
889: MEMFILE *mfp;
890: BHDR *hp;
891: {
1.2 downsj 892: off_t offset; /* offset in the file */
1.1 downsj 893: blocknr_t nr; /* block nr which is being written */
894: BHDR *hp2;
895: unsigned page_size; /* number of bytes in a page */
896: unsigned page_count; /* number of pages written */
897: unsigned size; /* number of bytes written */
898:
899: if (mfp->mf_fd < 0) /* there is no file, can't write */
900: return FAIL;
901:
902: if (hp->bh_bnum < 0) /* must assign file block number */
903: if (mf_trans_add(mfp, hp) == FAIL)
904: return FAIL;
905:
906: page_size = mfp->mf_page_size;
907:
908: /*
909: * We don't want gaps in the file. Write the blocks in front of *hp
910: * to extend the file.
911: * If block 'mf_infile_count' is not in the hash list, it has been
912: * freed. Fill the space in the file with data from the current block.
913: */
914: for (;;)
915: {
916: nr = hp->bh_bnum;
917: if (nr > mfp->mf_infile_count) /* beyond end of file */
918: {
919: nr = mfp->mf_infile_count;
920: hp2 = mf_find_hash(mfp, nr); /* NULL catched below */
921: }
922: else
923: hp2 = hp;
924:
925: offset = page_size * nr;
1.2 downsj 926: if (lseek(mfp->mf_fd, offset, SEEK_SET) != offset)
1.1 downsj 927: {
928: EMSG("Seek error in swap file write");
929: return FAIL;
930: }
931: if (hp2 == NULL) /* freed block, fill with dummy data */
932: page_count = 1;
933: else
934: page_count = hp2->bh_page_count;
935: size = page_size * page_count;
936: if ((unsigned)write(mfp->mf_fd,
937: (char *)(hp2 == NULL ? hp : hp2)->bh_data, (size_t)size) != size)
938: {
939: /*
940: * Avoid repeating the error message, this mostly happens when the
941: * disk is full. We give the message again only after a succesful
942: * write or when hitting a key. We keep on trying, in case some
943: * space becomes available.
944: */
945: if (!did_swapwrite_msg)
946: EMSG("Write error in swap file");
947: did_swapwrite_msg = TRUE;
948: return FAIL;
949: }
950: did_swapwrite_msg = FALSE;
951: if (hp2 != NULL) /* written a non-dummy block */
952: hp2->bh_flags &= ~BH_DIRTY;
953: /* appended to the file */
954: if (nr + (blocknr_t)page_count > mfp->mf_infile_count)
955: mfp->mf_infile_count = nr + page_count;
956: if (nr == hp->bh_bnum) /* written the desired block */
957: break;
958: }
959: return OK;
960: }
961:
962: /*
963: * Make block number for *hp positive and add it to the translation list
964: *
965: * Return FAIL for failure, OK otherwise
966: */
967: static int
968: mf_trans_add(mfp, hp)
969: MEMFILE *mfp;
970: BHDR *hp;
971: {
972: BHDR *freep;
973: blocknr_t new_bnum;
974: int hash;
975: NR_TRANS *np;
976: int page_count;
977:
978: if (hp->bh_bnum >= 0) /* it's already positive */
979: return OK;
980:
981: if ((np = (NR_TRANS *)alloc((unsigned)sizeof(NR_TRANS))) == NULL)
982: return FAIL;
983:
984: /*
985: * get a new number for the block.
986: * If the first item in the free list has sufficient pages, use its number
987: * Otherwise use mf_blocknr_max.
988: */
989: freep = mfp->mf_free_first;
990: page_count = hp->bh_page_count;
991: if (freep != NULL && freep->bh_page_count >= page_count)
992: {
993: new_bnum = freep->bh_bnum;
994: /*
995: * If the page count of the free block was larger, recude it.
996: * If the page count matches, remove the block from the free list
997: */
998: if (freep->bh_page_count > page_count)
999: {
1000: freep->bh_bnum += page_count;
1001: freep->bh_page_count -= page_count;
1002: }
1003: else
1004: {
1005: freep = mf_rem_free(mfp);
1006: vim_free(freep);
1007: }
1008: }
1009: else
1010: {
1011: new_bnum = mfp->mf_blocknr_max;
1012: mfp->mf_blocknr_max += page_count;
1013: }
1014:
1015: np->nt_old_bnum = hp->bh_bnum; /* adjust number */
1016: np->nt_new_bnum = new_bnum;
1017:
1018: mf_rem_hash(mfp, hp); /* remove from old hash list */
1019: hp->bh_bnum = new_bnum;
1020: mf_ins_hash(mfp, hp); /* insert in new hash list */
1021:
1022: hash = MEMHASH(np->nt_old_bnum); /* insert in trans list */
1023: np->nt_next = mfp->mf_trans[hash];
1024: mfp->mf_trans[hash] = np;
1025: if (np->nt_next != NULL)
1026: np->nt_next->nt_prev = np;
1027: np->nt_prev = NULL;
1028:
1029: return OK;
1030: }
1031:
1032: /*
1033: * Lookup a tranlation from the trans lists and delete the entry
1034: *
1035: * Return the positive new number when found, the old number when not found
1036: */
1037: blocknr_t
1038: mf_trans_del(mfp, old_nr)
1039: MEMFILE *mfp;
1040: blocknr_t old_nr;
1041: {
1042: int hash;
1043: NR_TRANS *np;
1044: blocknr_t new_bnum;
1045:
1046: hash = MEMHASH(old_nr);
1047: for (np = mfp->mf_trans[hash]; np != NULL; np = np->nt_next)
1048: if (np->nt_old_bnum == old_nr)
1049: break;
1050: if (np == NULL) /* not found */
1051: return old_nr;
1052:
1053: mfp->mf_neg_count--;
1054: new_bnum = np->nt_new_bnum;
1055: if (np->nt_prev != NULL) /* remove entry from the trans list */
1056: np->nt_prev->nt_next = np->nt_next;
1057: else
1058: mfp->mf_trans[hash] = np->nt_next;
1059: if (np->nt_next != NULL)
1060: np->nt_next->nt_prev = np->nt_prev;
1061: vim_free(np);
1062:
1063: return new_bnum;
1064: }
1065:
1066: /*
1067: * Set mfp->mf_xfname according to mfp->mf_fname and some other things.
1068: * Don't get the full path name if did_cd is TRUE, then fname should
1069: * already be a full path name.
1070: */
1071: void
1072: mf_set_xfname(mfp)
1073: MEMFILE *mfp;
1074: {
1075: mfp->mf_xfname = NULL;
1076: if (!did_cd)
1077: mfp->mf_xfname = FullName_save(mfp->mf_fname);
1078: }
1079:
1080: /*
1081: * Make the name of the file used for the memfile a full path.
1082: * Used before doing a :cd
1083: */
1084: void
1085: mf_fullname(mfp)
1086: MEMFILE *mfp;
1087: {
1088: if (mfp != NULL && mfp->mf_fname != NULL && mfp->mf_xfname != NULL)
1089: {
1090: vim_free(mfp->mf_fname);
1091: mfp->mf_fname = mfp->mf_xfname;
1092: mfp->mf_xfname = NULL;
1093: }
1094: }
1095:
1096: /*
1097: * return TRUE if there are any translations pending for 'mfp'
1098: */
1099: int
1100: mf_need_trans(mfp)
1101: MEMFILE *mfp;
1102: {
1103: return (mfp->mf_fname != NULL && mfp->mf_neg_count > 0);
1104: }
1105:
1.4 ! downsj 1106: #if 0 /* included for beta testing only */
1.1 downsj 1107: /*
1108: * print statistics for a memfile (for debugging)
1109: */
1110: void
1111: mf_statistics()
1112: {
1113: MEMFILE *mfp;
1114: BHDR *hp;
1115: int used = 0;
1116: int locked = 0;
1117: int dirty = 0;
1118: int nfree = 0;
1119: int negative = 0;
1120:
1121: mfp = curbuf->b_ml.ml_mfp;
1122: if (mfp == NULL)
1123: MSG("No memfile");
1124: else
1125: {
1126: for (hp = mfp->mf_used_first; hp != NULL; hp = hp->bh_next)
1127: {
1128: ++used;
1129: if (hp->bh_flags & BH_LOCKED)
1130: ++locked;
1131: if (hp->bh_flags & BH_DIRTY)
1132: ++dirty;
1133: if (hp->bh_bnum < 0)
1134: ++negative;
1135: }
1136: for (hp = mfp->mf_free_first; hp != NULL; hp = hp->bh_next)
1137: ++nfree;
1138: sprintf((char *)IObuff, "%d used (%d locked, %d dirty, %d (%d) negative), %d free",
1139: used, locked, dirty, negative, (int)mfp->mf_neg_count, nfree);
1140: msg(IObuff);
1141: sprintf((char *)IObuff, "Total mem used is %ld bytes", total_mem_used);
1142: msg(IObuff);
1143: }
1144: }
1145: #endif
1146:
1147: /*
1148: * open a swap file for a memfile
1149: */
1150: static void
1151: mf_do_open(mfp, fname, trunc_file)
1152: MEMFILE *mfp;
1153: char_u *fname;
1154: int trunc_file;
1155: {
1156: mfp->mf_fname = fname;
1157: /*
1158: * Get the full path name before the open, because this is
1159: * not possible after the open on the Amiga.
1160: * fname cannot be NameBuff, because it must have been allocated.
1161: */
1162: mf_set_xfname(mfp);
1163:
1164: /*
1165: * try to open the file
1166: */
1167: mfp->mf_fd = open((char *)fname,
1168: (trunc_file ? (O_CREAT | O_RDWR | O_TRUNC) : (O_RDONLY)) | O_EXTRA
1169:
1170: #ifdef AMIGA /* Amiga has no mode argument */
1171: );
1172: #endif
1173: #ifdef UNIX /* open in rw------- mode */
1174: , (mode_t)0600);
1175: #endif
1176: #if defined(MSDOS) || defined(WIN32) || defined(__EMX__)
1177: , S_IREAD | S_IWRITE); /* open read/write */
1178: #endif
1179: #ifdef VMS /* open in rw------- mode */
1180: , 0600);
1181: #endif
1182:
1183: /*
1184: * If the file cannot be opened, use memory only
1185: */
1186: if (mfp->mf_fd < 0)
1187: {
1188: vim_free(mfp->mf_xfname);
1189: mfp->mf_fname = NULL;
1190: mfp->mf_xfname = NULL;
1191: }
1192: }