[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.4

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