[BACK]Return to memfile.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / vim

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: }