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