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

Annotation of src/usr.bin/vim/memline.c, Revision 1.1

1.1     ! downsj      1: /* $OpenBSD$   */
        !             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) EMSG(s) */
        !            12: #define CHECK(c, s)
        !            13:
        !            14: /*
        !            15:  * memline.c: Contains the functions for appending, deleting and changing the
        !            16:  * text lines. The memfile functions are used to store the information in blocks
        !            17:  * of memory, backed up by a file. The structure of the information is a tree.
        !            18:  * The root of the tree is a pointer block. The leaves of the tree are data
        !            19:  * blocks. In between may be several layers of pointer blocks, forming branches.
        !            20:  *
        !            21:  * Three types of blocks are used:
        !            22:  * - Block nr 0 contains information for recovery
        !            23:  * - Pointer blocks contain list of pointers to other blocks.
        !            24:  * - Data blocks contain the actual text.
        !            25:  *
        !            26:  * Block nr 0 contains the block0 structure (see below).
        !            27:  *
        !            28:  * Block nr 1 is the first pointer block. It is the root of the tree.
        !            29:  * Other pointer blocks are branches.
        !            30:  *
        !            31:  *    If a line is too big to fit in a single page, the block
        !            32:  *    containing that line is made big enough to hold the line. It may span
        !            33:  *   several pages. Otherwise all blocks are one page.
        !            34:  *
        !            35:  *    A data block that was filled when starting to edit a file and was not
        !            36:  *   changed since then, can have a negative block number. This means that it
        !            37:  *   has not yet been assigned a place in the file. When recovering, the lines
        !            38:  *       in this data block can be read from the original file. When the block is
        !            39:  *    changed (lines appended/deleted/changed) or when it is flushed it gets
        !            40:  *    a positive number. Use mf_trans_del() to get the new number, before
        !            41:  *    calling mf_get().
        !            42:  */
        !            43:
        !            44: /*
        !            45:  * The following functions are available to work with a memline:
        !            46:  *
        !            47:  * ml_open()           open a new memline for a buffer
        !            48:  * ml_setname()            change the file name for a memline
        !            49:  * ml_close()          close the memline for a buffer
        !            50:  * ml_close_all()      close all memlines
        !            51:  * ml_recover()            read a memline for recovery
        !            52:  * ml_sync_all()       flush changed blocks to file for all files
        !            53:  * ml_preserve()       flush everything into the file for one file
        !            54:  * ml_get()                get a pointer to a line
        !            55:  * ml_get_pos()            get a pointer to a position in a line
        !            56:  * ml_get_cursor()     get a pointer to the char under the cursor
        !            57:  * ml_get_buf()            get a pointer to a line in a specific buffer
        !            58:  * ml_line_alloced()   return TRUE if line was allocated
        !            59:  * ml_append()         append a new line
        !            60:  * ml_replace()            replace a line
        !            61:  * ml_delete()         delete a line
        !            62:  * ml_setmarked()      set mark for a line (for :global command)
        !            63:  * ml_firstmarked()        get first line with a mark (for :global command)
        !            64:  * ml_clearmarked()        clear all line marks (for :global command)
        !            65:  */
        !            66:
        !            67: #if defined MSDOS  ||  defined WIN32
        !            68: # include <io.h>
        !            69: #endif
        !            70:
        !            71: #include "vim.h"
        !            72: #include "globals.h"
        !            73: #include "proto.h"
        !            74: #include "option.h"
        !            75: #ifdef HAVE_FCNTL_H
        !            76: # include <fcntl.h>
        !            77: #endif
        !            78: #ifndef UNIX           /* it's in unix.h for Unix */
        !            79: # include <time.h>
        !            80: #endif
        !            81:
        !            82: #ifdef SASC
        !            83: # include <proto/dos.h>        /* for Open() and Close() */
        !            84: #endif
        !            85:
        !            86: typedef struct block0          ZERO_BL;    /* contents of the first block */
        !            87: typedef struct pointer_block   PTR_BL;     /* contents of a pointer block */
        !            88: typedef struct data_block      DATA_BL;    /* contents of a data block */
        !            89: typedef struct pointer_entry   PTR_EN;     /* block/line-count pair */
        !            90:
        !            91: #define DATA_ID        (('d' << 8) + 'a')      /* data block id */
        !            92: #define PTR_ID     (('p' << 8) + 't')      /* pointer block id */
        !            93: #define BLOCK0_ID0 'b'                     /* block 0 id 0 */
        !            94: #define BLOCK0_ID1 '0'                     /* block 0 id 1 */
        !            95:
        !            96: /*
        !            97:  * pointer to a block, used in a pointer block
        !            98:  */
        !            99: struct pointer_entry
        !           100: {
        !           101:    blocknr_t   pe_bnum;        /* block number */
        !           102:    linenr_t    pe_line_count;  /* number of lines in this branch */
        !           103:    linenr_t    pe_old_lnum;    /* lnum for this block (for recovery) */
        !           104:    int         pe_page_count;  /* number of pages in block pe_bnum */
        !           105: };
        !           106:
        !           107: /*
        !           108:  * A pointer block contains a list of branches in the tree.
        !           109:  */
        !           110: struct pointer_block
        !           111: {
        !           112:    short_u     pb_id;          /* ID for pointer block: PTR_ID */
        !           113:    short_u     pb_count;       /* number of pointer in this block */
        !           114:    short_u     pb_count_max;   /* maximum value for pb_count */
        !           115:    PTR_EN      pb_pointer[1];  /* list of pointers to blocks (actually longer)
        !           116:                                 * followed by empty space until end of page */
        !           117: };
        !           118:
        !           119: /*
        !           120:  * A data block is a leaf in the tree.
        !           121:  *
        !           122:  * The text of the lines is at the end of the block. The text of the first line
        !           123:  * in the block is put at the end, the text of the second line in front of it,
        !           124:  * etc. Thus the order of the lines is the opposite of the line number.
        !           125:  */
        !           126: struct data_block
        !           127: {
        !           128:    short_u     db_id;          /* ID for data block: DATA_ID */
        !           129:    unsigned    db_free;        /* free space available */
        !           130:    unsigned    db_txt_start;   /* byte where text starts */
        !           131:    unsigned    db_txt_end;     /* byte just after data block */
        !           132:    linenr_t    db_line_count;  /* number of lines in this block */
        !           133:    unsigned    db_index[1];    /* index for start of line (actually bigger)
        !           134:                                 * followed by empty space upto db_txt_start
        !           135:                                 * followed by the text in the lines until
        !           136:                                 * end of page */
        !           137: };
        !           138:
        !           139: /*
        !           140:  * The low bits of db_index hold the actual index. The topmost bit is
        !           141:  * used for the global command to be able to mark a line.
        !           142:  * This method is not clean, but otherwise there would be at least one extra
        !           143:  * byte used for each line.
        !           144:  * The mark has to be in this place to keep it with the correct line when other
        !           145:  * lines are inserted or deleted.
        !           146:  */
        !           147: #define DB_MARKED      ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
        !           148: #define DB_INDEX_MASK  (~DB_MARKED)
        !           149:
        !           150: #define INDEX_SIZE (sizeof(unsigned))      /* size of one db_index entry */
        !           151: #define HEADER_SIZE    (sizeof(DATA_BL) - INDEX_SIZE)  /* size of data block header */
        !           152:
        !           153: #define B0_FNAME_SIZE  900
        !           154: #define B0_UNAME_SIZE  40
        !           155: #define B0_HNAME_SIZE  40
        !           156: /*
        !           157:  * Restrict the numbers to 32 bits, otherwise most compilers will complain.
        !           158:  * This won't detect a 64 bit machine that only swaps a byte in the top 32
        !           159:  * bits, but that is crazy anyway.
        !           160:  */
        !           161: #define B0_MAGIC_LONG  0x30313233
        !           162: #define B0_MAGIC_INT   0x20212223
        !           163: #define B0_MAGIC_SHORT 0x10111213
        !           164: #define B0_MAGIC_CHAR  0x55
        !           165:
        !           166: /*
        !           167:  * Block zero holds all info about the swap file.
        !           168:  *
        !           169:  * NOTE: DEFINITION OF BLOCK 0 SHOULD NOT CHANGE, it makes all existing swap
        !           170:  * files unusable!
        !           171:  *
        !           172:  * If size of block0 changes anyway, adjust minimal block size
        !           173:  * in mf_open()!!
        !           174:  *
        !           175:  * This block is built up of single bytes, to make it portable accros
        !           176:  * different machines. b0_magic_* is used to check the byte order and size of
        !           177:  * variables, because the rest of the swap is not portable.
        !           178:  */
        !           179: struct block0
        !           180: {
        !           181:    char_u      b0_id[2];       /* id for block 0: BLOCK0_ID0 and BLOCK0_ID1 */
        !           182:    char_u      b0_version[10]; /* Vim version string */
        !           183:    char_u      b0_page_size[4];/* number of bytes per page */
        !           184:    char_u      b0_mtime[4];    /* last modification time of file */
        !           185:    char_u      b0_ino[4];      /* inode of b0_fname */
        !           186:    char_u      b0_pid[4];      /* process id of creator (or 0) */
        !           187:    char_u      b0_uname[B0_UNAME_SIZE]; /* name of user (uid if no name) */
        !           188:    char_u      b0_hname[B0_HNAME_SIZE]; /* host name (if it has a name) */
        !           189:    char_u      b0_fname[B0_FNAME_SIZE]; /* name of file being edited */
        !           190:    long        b0_magic_long;  /* check for byte order of long */
        !           191:    int         b0_magic_int;   /* check for byte order of int */
        !           192:    short       b0_magic_short; /* check for byte order of short */
        !           193:    char_u      b0_magic_char;  /* check for last char */
        !           194: };
        !           195:
        !           196: #define STACK_INCR     5       /* nr of entries added to ml_stack at a time */
        !           197:
        !           198: /*
        !           199:  * The line number where the first mark may be is remembered.
        !           200:  * If it is 0 there are no marks at all.
        !           201:  * (always used for the current buffer only, no buffer change possible while
        !           202:  * executing a global command).
        !           203:  */
        !           204: static linenr_t    lowest_marked = 0;
        !           205:
        !           206: /*
        !           207:  * arguments for ml_find_line()
        !           208:  */
        !           209: #define ML_DELETE      0x11        /* delete line */
        !           210: #define ML_INSERT      0x12        /* insert line */
        !           211: #define ML_FIND            0x13        /* just find the line */
        !           212: #define ML_FLUSH       0x02        /* flush locked block */
        !           213: #define ML_SIMPLE(x)   (x & 0x10)  /* DEL, INS or FIND */
        !           214:
        !           215: static void ml_open_file __ARGS((BUF *));
        !           216: static void set_b0_fname __ARGS((ZERO_BL *, BUF *buf));
        !           217: static void swapfile_info __ARGS((char_u *));
        !           218: static int get_names __ARGS((char_u **, char_u *));
        !           219: static int ml_append_int __ARGS((BUF *, linenr_t, char_u *, colnr_t, int));
        !           220: static int ml_delete_int __ARGS((BUF *, linenr_t, int));
        !           221: static char_u *findswapname __ARGS((BUF *, char_u **, char_u *));
        !           222: static void ml_flush_line __ARGS((BUF *));
        !           223: static BHDR *ml_new_data __ARGS((MEMFILE *, int, int));
        !           224: static BHDR *ml_new_ptr __ARGS((MEMFILE *));
        !           225: static BHDR *ml_find_line __ARGS((BUF *, linenr_t, int));
        !           226: static int ml_add_stack __ARGS((BUF *));
        !           227: static char_u *makeswapname __ARGS((BUF *, char_u *));
        !           228: static void ml_lineadd __ARGS((BUF *, int));
        !           229: static int b0_magic_wrong __ARGS((ZERO_BL *));
        !           230: #ifdef CHECK_INODE
        !           231: static int fnamecmp_ino __ARGS((char_u *, char_u *, long));
        !           232: #endif
        !           233: static void long_to_char __ARGS((long, char_u *));
        !           234: static long char_to_long __ARGS((char_u *));
        !           235:
        !           236: /*
        !           237:  * open a new memline for 'curbuf'
        !           238:  *
        !           239:  * return FAIL for failure, OK otherwise
        !           240:  */
        !           241:    int
        !           242: ml_open()
        !           243: {
        !           244:    MEMFILE     *mfp;
        !           245:    char_u      *fname;
        !           246:    BHDR        *hp = NULL;
        !           247:    ZERO_BL     *b0p;
        !           248:    PTR_BL      *pp;
        !           249:    DATA_BL     *dp;
        !           250:    char_u      *dirp;
        !           251:
        !           252: /*
        !           253:  * init fields in memline struct
        !           254:  */
        !           255:    curbuf->b_ml.ml_stack_size = 0;     /* no stack yet */
        !           256:    curbuf->b_ml.ml_stack = NULL;       /* no stack yet */
        !           257:    curbuf->b_ml.ml_stack_top = 0;      /* nothing in the stack */
        !           258:    curbuf->b_ml.ml_locked = NULL;      /* no cached block */
        !           259:    curbuf->b_ml.ml_line_lnum = 0;      /* no cached line */
        !           260:
        !           261: /*
        !           262:  * Make file name for swap file.
        !           263:  * If we are unable to find a file name, mf_fname will be NULL
        !           264:  * and the memfile will be in memory only (no recovery possible).
        !           265:  * When 'updatecount' is 0 and for help files on Unix there is never a swap
        !           266:  * file.
        !           267:  */
        !           268: #if defined(UNIX) || defined(__EMX__)
        !           269: /* why only on Unix an exception for help files??? */
        !           270:    if (p_uc == 0 || curbuf->b_help)
        !           271: #else
        !           272:    if (p_uc == 0)
        !           273: #endif
        !           274:        dirp = (char_u *)"";
        !           275:    else
        !           276:        dirp = p_dir;
        !           277:
        !           278: /*
        !           279:  * Open the memfile.
        !           280:  *
        !           281:  * If no swap file is wanted *dirp will be NUL, try with fname == NULL.
        !           282:  * Otherwise try all directories in 'directory' option. If that fails try
        !           283:  * with fname == NULL.
        !           284:  */
        !           285:    for (;;)
        !           286:    {
        !           287:        if (*dirp == NUL)
        !           288:            fname = NULL;
        !           289:        else
        !           290:        {
        !           291:            fname = findswapname(curbuf, &dirp, NULL);
        !           292:            if (fname == NULL)
        !           293:                continue;
        !           294:        }
        !           295:        mfp = mf_open(fname, TRUE);
        !           296:        /* stop when memfile opened or no other fname to try */
        !           297:        if (mfp != NULL || (fname == NULL && *dirp == NUL))
        !           298:            break;
        !           299:        vim_free(fname);
        !           300:    }
        !           301:
        !           302:    if (mfp == NULL)
        !           303:        goto error;
        !           304:    curbuf->b_ml.ml_mfp = mfp;
        !           305:    curbuf->b_ml.ml_flags = ML_EMPTY;
        !           306:    curbuf->b_ml.ml_line_count = 1;
        !           307:
        !           308:    if (!(p_uc == 0 || curbuf->b_help) && mfp->mf_fname == NULL)
        !           309:    {
        !           310:        need_wait_return = TRUE;        /* call wait_return later */
        !           311:        ++no_wait_return;
        !           312:        (void)EMSG("Unable to open swap file, recovery impossible");
        !           313:        --no_wait_return;
        !           314:    }
        !           315: #if defined(MSDOS) || defined(WIN32)
        !           316:    /*
        !           317:     * set full pathname for swap file now, because a ":!cd dir" may
        !           318:     * change directory without us knowing it.
        !           319:     */
        !           320:    mf_fullname(mfp);
        !           321: #endif
        !           322:
        !           323: /*
        !           324:  * fill block0 struct and write page 0
        !           325:  */
        !           326:    if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
        !           327:        goto error;
        !           328:    if (hp->bh_bnum != 0)
        !           329:    {
        !           330:        EMSG("didn't get block nr 0?");
        !           331:        goto error;
        !           332:    }
        !           333:    b0p = (ZERO_BL *)(hp->bh_data);
        !           334:
        !           335:    (void)vim_memset(b0p, 0, sizeof(ZERO_BL));      /* init all to zero */
        !           336:
        !           337:    b0p->b0_id[0] = BLOCK0_ID0;
        !           338:    b0p->b0_id[1] = BLOCK0_ID1;
        !           339:    b0p->b0_magic_long = (long)B0_MAGIC_LONG;
        !           340:    b0p->b0_magic_int = (int)B0_MAGIC_INT;
        !           341:    b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
        !           342:    b0p->b0_magic_char = B0_MAGIC_CHAR;
        !           343:
        !           344:    STRNCPY(b0p->b0_version, Version, 10);
        !           345:    set_b0_fname(b0p, curbuf);
        !           346:    long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
        !           347:    (void)mch_get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
        !           348:    b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
        !           349:    mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
        !           350:    b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
        !           351:    long_to_char(mch_get_pid(), b0p->b0_pid);
        !           352:
        !           353:    /*
        !           354:     * Always sync block number 0 to disk, so we can check the file name in
        !           355:     * the swap file in findswapname(). Don't do this for help files though.
        !           356:     */
        !           357:    mf_put(mfp, hp, TRUE, FALSE);
        !           358:    if (!curbuf->b_help)
        !           359:        mf_sync(mfp, FALSE, FALSE, FALSE);
        !           360:
        !           361: /*
        !           362:  * fill in root pointer block and write page 1
        !           363:  */
        !           364:    if ((hp = ml_new_ptr(mfp)) == NULL)
        !           365:        goto error;
        !           366:    if (hp->bh_bnum != 1)
        !           367:    {
        !           368:        EMSG("didn't get block nr 1?");
        !           369:        goto error;
        !           370:    }
        !           371:    pp = (PTR_BL *)(hp->bh_data);
        !           372:    pp->pb_count = 1;
        !           373:    pp->pb_pointer[0].pe_bnum = 2;
        !           374:    pp->pb_pointer[0].pe_page_count = 1;
        !           375:    pp->pb_pointer[0].pe_old_lnum = 1;
        !           376:    pp->pb_pointer[0].pe_line_count = 1;    /* line count after insertion */
        !           377:    mf_put(mfp, hp, TRUE, FALSE);
        !           378:
        !           379: /*
        !           380:  * allocate first data block and create an empty line 1.
        !           381:  */
        !           382:    if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
        !           383:        goto error;
        !           384:    if (hp->bh_bnum != 2)
        !           385:    {
        !           386:        EMSG("didn't get block nr 2?");
        !           387:        goto error;
        !           388:    }
        !           389:
        !           390:    dp = (DATA_BL *)(hp->bh_data);
        !           391:    dp->db_index[0] = --dp->db_txt_start;       /* at end of block */
        !           392:    dp->db_free -= 1 + INDEX_SIZE;
        !           393:    dp->db_line_count = 1;
        !           394:    *((char_u *)dp + dp->db_txt_start) = NUL;   /* emtpy line */
        !           395:
        !           396:    return OK;
        !           397:
        !           398: error:
        !           399:    if (mfp != NULL)
        !           400:    {
        !           401:        if (hp)
        !           402:            mf_put(mfp, hp, FALSE, FALSE);
        !           403:        mf_close(mfp, TRUE);        /* will also free(mfp->mf_fname) */
        !           404:    }
        !           405:    else
        !           406:        vim_free(fname);
        !           407:    curbuf->b_ml.ml_mfp = NULL;
        !           408:    return FAIL;
        !           409: }
        !           410:
        !           411: /*
        !           412:  * ml_setname() is called when the file name has been changed.
        !           413:  * It may rename the swap file.
        !           414:  */
        !           415:    void
        !           416: ml_setname()
        !           417: {
        !           418:    int         success = FALSE;
        !           419:    MEMFILE     *mfp;
        !           420:    char_u      *fname;
        !           421:    char_u      *dirp;
        !           422:
        !           423: /*
        !           424:  * When 'updatecount' is 0 there is never a swap file.
        !           425:  * For help files we will make a swap file now.
        !           426:  */
        !           427:    if (p_uc == 0)
        !           428:        return;
        !           429:
        !           430:    mfp = curbuf->b_ml.ml_mfp;
        !           431:    if (mfp->mf_fd < 0)             /* there is no swap file yet */
        !           432:    {
        !           433:        ml_open_file(curbuf);       /* create a swap file */
        !           434:        return;
        !           435:    }
        !           436:
        !           437: /*
        !           438:  * Try all directories in the 'directory' option.
        !           439:  */
        !           440:    dirp = p_dir;
        !           441:    for (;;)
        !           442:    {
        !           443:        if (*dirp == NUL)           /* tried all directories, fail */
        !           444:            break;
        !           445:        fname = findswapname(curbuf, &dirp, mfp->mf_fname);
        !           446:        if (fname == NULL)          /* no file name found for this dir */
        !           447:            continue;
        !           448:
        !           449: #if defined(MSDOS) || defined(WIN32)
        !           450:        /*
        !           451:         * Set full pathname for swap file now, because a ":!cd dir" may
        !           452:         * change directory without us knowing it.
        !           453:         */
        !           454:        if (!did_cd)
        !           455:        {
        !           456:            char_u      *p;
        !           457:
        !           458:            p = FullName_save(fname);
        !           459:            vim_free(fname);
        !           460:            fname = p;
        !           461:            if (fname == NULL)
        !           462:                continue;
        !           463:        }
        !           464: #endif
        !           465:            /* if the file name is the same we don't have to do anything */
        !           466:        if (fnamecmp(fname, mfp->mf_fname) == 0)
        !           467:        {
        !           468:            vim_free(fname);
        !           469:            success = TRUE;
        !           470:            break;
        !           471:        }
        !           472:            /* need to close the swap file before renaming */
        !           473:        if (mfp->mf_fd >= 0)
        !           474:        {
        !           475:            close(mfp->mf_fd);
        !           476:            mfp->mf_fd = -1;
        !           477:        }
        !           478:
        !           479:            /* try to rename the swap file */
        !           480:        if (vim_rename(mfp->mf_fname, fname) == 0)
        !           481:        {
        !           482:            success = TRUE;
        !           483:            vim_free(mfp->mf_fname);
        !           484:            mfp->mf_fname = fname;
        !           485:            vim_free(mfp->mf_xfname);
        !           486:            mf_set_xfname(mfp);
        !           487:            break;
        !           488:        }
        !           489:        vim_free(fname);            /* this fname didn't work, try another */
        !           490:    }
        !           491:
        !           492:    if (mfp->mf_fd == -1)           /* need to (re)open the swap file */
        !           493:    {
        !           494:        mfp->mf_fd = open((char *)mfp->mf_fname, O_RDWR | O_EXTRA);
        !           495:        if (mfp->mf_fd < 0)
        !           496:        {
        !           497:            /* could not (re)open the swap file, what can we do???? */
        !           498:            EMSG("Oops, lost the swap file!!!");
        !           499:            return;
        !           500:        }
        !           501:    }
        !           502:    if (!success)
        !           503:        EMSG("Could not rename swap file");
        !           504: }
        !           505:
        !           506: /*
        !           507:  * Open a file for the memfile for all buffers.
        !           508:  * Used when 'updatecount' changes from zero to non-zero.
        !           509:  */
        !           510:    void
        !           511: ml_open_files()
        !           512: {
        !           513:    BUF         *buf;
        !           514:
        !           515:    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
        !           516:        ml_open_file(buf);
        !           517: }
        !           518:
        !           519: /*
        !           520:  * Open a swap file for an existing memfile, if there is non swap file yet.
        !           521:  */
        !           522:    static void
        !           523: ml_open_file(buf)
        !           524:    BUF         *buf;
        !           525: {
        !           526:    MEMFILE     *mfp;
        !           527:    char_u      *fname;
        !           528:    char_u      *dirp;
        !           529:
        !           530:    mfp = buf->b_ml.ml_mfp;
        !           531:    if (mfp == NULL || mfp->mf_fd >= 0)         /* nothing to do */
        !           532:        return;
        !           533:
        !           534:    dirp = p_dir;
        !           535: /*
        !           536:  * Try all directories in 'directory' option.
        !           537:  */
        !           538:    for (;;)
        !           539:    {
        !           540:        if (*dirp == NUL)
        !           541:            break;
        !           542:        fname = findswapname(buf, &dirp, NULL);
        !           543:        if (fname == NULL)
        !           544:            continue;
        !           545:        if (mf_open_file(mfp, fname) == OK)
        !           546:            break;
        !           547:        vim_free(fname);
        !           548:    }
        !           549:
        !           550:    if (mfp->mf_fname == NULL)          /* Failed! */
        !           551:    {
        !           552:        need_wait_return = TRUE;        /* call wait_return later */
        !           553:        ++no_wait_return;
        !           554:        (void)EMSG2("Unable to open swap file for \"%s\", recovery impossible",
        !           555:                buf->b_xfilename == NULL ? (char_u *)"No File"
        !           556:                                         : buf->b_xfilename);
        !           557:        --no_wait_return;
        !           558:    }
        !           559: #if defined(MSDOS) || defined(WIN32)
        !           560:    /*
        !           561:     * set full pathname for swap file now, because a ":!cd dir" may
        !           562:     * change directory without us knowing it.
        !           563:     */
        !           564:    else
        !           565:        mf_fullname(mfp);
        !           566: #endif
        !           567: }
        !           568:
        !           569: /*
        !           570:  * Close memline for buffer 'buf'.
        !           571:  * If 'del_file' is TRUE, delete the swap file
        !           572:  */
        !           573:    void
        !           574: ml_close(buf, del_file)
        !           575:    BUF     *buf;
        !           576:    int     del_file;
        !           577: {
        !           578:    if (buf->b_ml.ml_mfp == NULL)               /* not open */
        !           579:        return;
        !           580:    mf_close(buf->b_ml.ml_mfp, del_file);           /* close the .swp file */
        !           581:    if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY))
        !           582:        vim_free(buf->b_ml.ml_line_ptr);
        !           583:    vim_free(buf->b_ml.ml_stack);
        !           584:    buf->b_ml.ml_mfp = NULL;
        !           585: }
        !           586:
        !           587: /*
        !           588:  * Close all existing memlines and memfiles.
        !           589:  * Used when exiting.
        !           590:  * When 'del_file' is TRUE, delete the memfiles.
        !           591:  */
        !           592:    void
        !           593: ml_close_all(del_file)
        !           594:    int     del_file;
        !           595: {
        !           596:    BUF     *buf;
        !           597:
        !           598:    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
        !           599:        ml_close(buf, del_file);
        !           600: }
        !           601:
        !           602: /*
        !           603:  * Close all memfiles for not modified buffers.
        !           604:  * Only use just before exiting!
        !           605:  */
        !           606:    void
        !           607: ml_close_notmod()
        !           608: {
        !           609:    BUF     *buf;
        !           610:
        !           611:    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
        !           612:        if (!buf->b_changed)
        !           613:            ml_close(buf, TRUE);    /* close all not-modified buffers */
        !           614: }
        !           615:
        !           616: /*
        !           617:  * Update the timestamp in the .swp file.
        !           618:  * Used when the file has been written.
        !           619:  */
        !           620:    void
        !           621: ml_timestamp(buf)
        !           622:    BUF         *buf;
        !           623: {
        !           624:    MEMFILE     *mfp;
        !           625:    BHDR        *hp;
        !           626:    ZERO_BL     *b0p;
        !           627:
        !           628:    mfp = buf->b_ml.ml_mfp;
        !           629:
        !           630:    if (mfp == NULL || (hp = mf_get(mfp, (blocknr_t)0, 1)) == NULL)
        !           631:        return;
        !           632:    b0p = (ZERO_BL *)(hp->bh_data);
        !           633:    if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
        !           634:        EMSG("ml_timestamp: Didn't get block 0??");
        !           635:    else
        !           636:        set_b0_fname(b0p, buf);
        !           637:    mf_put(mfp, hp, TRUE, FALSE);
        !           638: }
        !           639:
        !           640: /*
        !           641:  * Write file name and timestamp into block 0 of a swap file.
        !           642:  * Also set buf->b_mtime.
        !           643:  * Don't use NameBuff[]!!!
        !           644:  */
        !           645:    static void
        !           646: set_b0_fname(b0p, buf)
        !           647:    ZERO_BL     *b0p;
        !           648:    BUF         *buf;
        !           649: {
        !           650:    struct stat st;
        !           651:    size_t      flen, ulen;
        !           652:    char_u      uname[B0_UNAME_SIZE];
        !           653:
        !           654:    if (buf->b_filename == NULL)
        !           655:        b0p->b0_fname[0] = NUL;
        !           656:    else
        !           657:    {
        !           658:        /*
        !           659:         * For a file under the home directory of the current user, we try to
        !           660:         * replace the home directory path with "~user". This helps when
        !           661:         * editing the same file on different machines over a network.
        !           662:         * First replace home dir path with "~/" with home_replace().
        !           663:         * Then insert the user name to get "~user/".
        !           664:         */
        !           665:        home_replace(NULL, buf->b_filename, b0p->b0_fname, B0_FNAME_SIZE);
        !           666:        if (b0p->b0_fname[0] == '~')
        !           667:        {
        !           668:            flen = STRLEN(b0p->b0_fname);
        !           669:            /* If there is no user name or it is too long, don't use "~/" */
        !           670:            if (mch_get_user_name(uname, B0_UNAME_SIZE) == FAIL ||
        !           671:                         (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE - 1)
        !           672:                STRNCPY(b0p->b0_fname, buf->b_filename, B0_FNAME_SIZE);
        !           673:            else
        !           674:            {
        !           675:                vim_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
        !           676:                vim_memmove(b0p->b0_fname + 1, uname, ulen);
        !           677:            }
        !           678:        }
        !           679:        if (stat((char *)buf->b_filename, &st) >= 0)
        !           680:        {
        !           681:            long_to_char((long)st.st_mtime, b0p->b0_mtime);
        !           682: #ifdef CHECK_INODE
        !           683:            long_to_char((long)st.st_ino, b0p->b0_ino);
        !           684: #endif
        !           685:            buf->b_mtime = st.st_mtime;
        !           686:            buf->b_mtime_read = st.st_mtime;
        !           687:        }
        !           688:        else
        !           689:        {
        !           690:            long_to_char(0L, b0p->b0_mtime);
        !           691: #ifdef CHECK_INODE
        !           692:            long_to_char(0L, b0p->b0_ino);
        !           693: #endif
        !           694:            buf->b_mtime = 0;
        !           695:            buf->b_mtime_read = 0;
        !           696:        }
        !           697:    }
        !           698: }
        !           699:
        !           700: /*
        !           701:  * try to recover curbuf from the .swp file
        !           702:  */
        !           703:    void
        !           704: ml_recover()
        !           705: {
        !           706:    BUF         *buf = NULL;
        !           707:    MEMFILE     *mfp = NULL;
        !           708:    char_u      *fname;
        !           709:    BHDR        *hp = NULL;
        !           710:    ZERO_BL     *b0p;
        !           711:    PTR_BL      *pp;
        !           712:    DATA_BL     *dp;
        !           713:    IPTR        *ip;
        !           714:    blocknr_t   bnum;
        !           715:    int         page_count;
        !           716:    struct stat org_stat, swp_stat;
        !           717:    int         len;
        !           718:    int         directly;
        !           719:    linenr_t    lnum;
        !           720:    char_u      *p;
        !           721:    int         i;
        !           722:    long        error;
        !           723:    int         cannot_open;
        !           724:    linenr_t    line_count;
        !           725:    int         has_error;
        !           726:    int         idx;
        !           727:    int         top;
        !           728:    int         txt_start;
        !           729:    long        size;
        !           730:    int         called_from_main;
        !           731:    int         serious_error = TRUE;
        !           732:    long        mtime;
        !           733:
        !           734:    called_from_main = (curbuf->b_ml.ml_mfp == NULL);
        !           735: /*
        !           736:  * If the file name ends in ".sw?" we use it directly.
        !           737:  * Otherwise a search is done to find the swap file(s).
        !           738:  */
        !           739:    fname = curbuf->b_xfilename;
        !           740:    if (fname == NULL)              /* When there is no file name */
        !           741:        fname = (char_u *)"";
        !           742:    len = STRLEN(fname);
        !           743:    if (len >= 4 && vim_strnicmp(fname + len - 4,
        !           744:                                    (char_u *)".sw", (size_t)3) == 0)
        !           745:    {
        !           746:        directly = TRUE;
        !           747:        fname = strsave(fname);     /* make a copy for mf_open */
        !           748:    }
        !           749:    else
        !           750:    {
        !           751:        directly = FALSE;
        !           752:
        !           753:            /* count the number of matching swap files */
        !           754:        len = recover_names(&fname, FALSE, 0);
        !           755:        if (len == 0)               /* no swap files found */
        !           756:        {
        !           757:            EMSG2("No swap file found for %s", fname);
        !           758:            fname = NULL;
        !           759:            goto theend;
        !           760:        }
        !           761:        if (len == 1)               /* one swap file found, use it */
        !           762:            i = 1;
        !           763:        else                        /* several swap files found, choose */
        !           764:        {
        !           765:                /* list the names of the swap files */
        !           766:            (void)recover_names(&fname, TRUE, 0);
        !           767:            msg_outchar('\n');
        !           768:            MSG_OUTSTR("Enter number of swap file to use (0 to quit): ");
        !           769:            i = get_number();
        !           770:            if (i < 1 || i > len)
        !           771:            {
        !           772:                fname = NULL;
        !           773:                goto theend;
        !           774:            }
        !           775:        }
        !           776:                /* get the swap file name that will be used */
        !           777:        (void)recover_names(&fname, FALSE, i);
        !           778:    }
        !           779:    if (fname == NULL)
        !           780:        goto theend;                    /* out of memory */
        !           781:
        !           782:    /* When called from main() still need to initialize storage structure */
        !           783:    if (called_from_main && ml_open() == FAIL)
        !           784:        getout(1);
        !           785:
        !           786: /*
        !           787:  * allocate a buffer structure (only the memline in it is really used)
        !           788:  */
        !           789:    buf = (BUF *)alloc((unsigned)sizeof(BUF));
        !           790:    if (buf == NULL)
        !           791:        goto theend;
        !           792:
        !           793: /*
        !           794:  * init fields in memline struct
        !           795:  */
        !           796:    buf->b_ml.ml_stack_size = 0;        /* no stack yet */
        !           797:    buf->b_ml.ml_stack = NULL;          /* no stack yet */
        !           798:    buf->b_ml.ml_stack_top = 0;         /* nothing in the stack */
        !           799:    buf->b_ml.ml_line_lnum = 0;         /* no cached line */
        !           800:    buf->b_ml.ml_locked = NULL;         /* no locked block */
        !           801:    buf->b_ml.ml_flags = 0;
        !           802:
        !           803: /*
        !           804:  * open the memfile from the old swap file
        !           805:  */
        !           806:    mfp = mf_open(fname, FALSE);
        !           807:    if (mfp == NULL || mfp->mf_fd < 0)
        !           808:    {
        !           809:        EMSG2("Cannot open %s", fname);
        !           810:        goto theend;
        !           811:    }
        !           812:    buf->b_ml.ml_mfp = mfp;
        !           813: #if defined(MSDOS) || defined(WIN32)
        !           814:    /*
        !           815:     * set full pathname for swap file now, because a ":!cd dir" may
        !           816:     * change directory without us knowing it.
        !           817:     * Careful: May free fname!
        !           818:     */
        !           819:    mf_fullname(mfp);
        !           820: #endif
        !           821:
        !           822: /*
        !           823:  * try to read block 0
        !           824:  */
        !           825:    if ((hp = mf_get(mfp, (blocknr_t)0, 1)) == NULL)
        !           826:    {
        !           827:        msg_start();
        !           828:        MSG_OUTSTR("Unable to read block 0 from ");
        !           829:        msg_outtrans(mfp->mf_fname);
        !           830:        MSG_OUTSTR("\nMaybe no changes were made or Vim did not update the swap file");
        !           831:        msg_end();
        !           832:        goto theend;
        !           833:    }
        !           834:    b0p = (ZERO_BL *)(hp->bh_data);
        !           835:    if (b0p->b0_id[0] != BLOCK0_ID0 || b0p->b0_id[1] != BLOCK0_ID1)
        !           836:    {
        !           837:        EMSG2("%s is not a swap file", mfp->mf_fname);
        !           838:        goto theend;
        !           839:    }
        !           840:    if (b0_magic_wrong(b0p))
        !           841:    {
        !           842:        msg_start();
        !           843:        MSG_OUTSTR("The file ");
        !           844:        msg_outtrans(mfp->mf_fname);
        !           845: #if defined(MSDOS) || defined(WIN32)
        !           846:        if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
        !           847:            MSG_OUTSTR(" cannot be used with this version of Vim.\n");
        !           848:        else
        !           849: #endif
        !           850:            MSG_OUTSTR(" cannot be used on this computer.\n");
        !           851:        MSG_OUTSTR("The file was created on ");
        !           852:                /* avoid going past the end of currupted hostname */
        !           853:        b0p->b0_fname[0] = NUL;
        !           854:        MSG_OUTSTR(b0p->b0_hname);
        !           855:        MSG_OUTSTR(",\nor the file has been damaged.");
        !           856:        msg_end();
        !           857:        goto theend;
        !           858:    }
        !           859:    /*
        !           860:     * If we guessed the wrong page size, we have to recalculate the
        !           861:     * highest block number in the file.
        !           862:     */
        !           863:    if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
        !           864:    {
        !           865:        mfp->mf_page_size = (unsigned)char_to_long(b0p->b0_page_size);
        !           866:        if ((size = lseek(mfp->mf_fd, 0L, SEEK_END)) <= 0)
        !           867:            mfp->mf_blocknr_max = 0;        /* no file or empty file */
        !           868:        else
        !           869:            mfp->mf_blocknr_max = size / mfp->mf_page_size;
        !           870:        mfp->mf_infile_count = mfp->mf_blocknr_max;
        !           871:    }
        !           872:
        !           873: /*
        !           874:  * If .swp file name given directly, use name from swap file for buffer.
        !           875:  */
        !           876:    if (directly)
        !           877:    {
        !           878:        expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
        !           879:        if (setfname(NameBuff, NULL, TRUE) == FAIL)
        !           880:            goto theend;
        !           881:    }
        !           882:
        !           883:    home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL);
        !           884:    smsg((char_u *)"Using swap file \"%s\"", NameBuff);
        !           885:
        !           886:    if (curbuf->b_filename == NULL)
        !           887:        STRCPY(NameBuff, "No File");
        !           888:    else
        !           889:        home_replace(NULL, curbuf->b_filename, NameBuff, MAXPATHL);
        !           890:    smsg((char_u *)"Original file \"%s\"", NameBuff);
        !           891:    msg_outchar((char_u)'\n');
        !           892:
        !           893: /*
        !           894:  * check date of swap file and original file
        !           895:  */
        !           896:    mtime = char_to_long(b0p->b0_mtime);
        !           897:    if (curbuf->b_filename != NULL &&
        !           898:            stat((char *)curbuf->b_filename, &org_stat) != -1 &&
        !           899:            ((stat((char *)mfp->mf_fname, &swp_stat) != -1 &&
        !           900:            org_stat.st_mtime > swp_stat.st_mtime) ||
        !           901:            org_stat.st_mtime != mtime))
        !           902:    {
        !           903:        EMSG("Warning: Original file may have been changed");
        !           904:    }
        !           905:    flushbuf();
        !           906:    mf_put(mfp, hp, FALSE, FALSE);      /* release block 0 */
        !           907:    hp = NULL;
        !           908:
        !           909:    /*
        !           910:     * Now that we are sure that the file is going to be recovered, clear the
        !           911:     * contents of the current buffer.
        !           912:     */
        !           913:    while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
        !           914:        ml_delete((linenr_t)1, FALSE);
        !           915:
        !           916:    bnum = 1;           /* start with block 1 */
        !           917:    page_count = 1;     /* which is 1 page */
        !           918:    lnum = 0;           /* append after line 0 in curbuf */
        !           919:    line_count = 0;
        !           920:    idx = 0;            /* start with first index in block 1 */
        !           921:    error = 0;
        !           922:    buf->b_ml.ml_stack_top = 0;
        !           923:    buf->b_ml.ml_stack = NULL;
        !           924:    buf->b_ml.ml_stack_size = 0;        /* no stack yet */
        !           925:
        !           926:    if (curbuf->b_filename == NULL)
        !           927:        cannot_open = TRUE;
        !           928:    else
        !           929:        cannot_open = FALSE;
        !           930:
        !           931:    serious_error = FALSE;
        !           932:    for ( ; !got_int; line_breakcheck())
        !           933:    {
        !           934:        if (hp != NULL)
        !           935:            mf_put(mfp, hp, FALSE, FALSE);      /* release previous block */
        !           936:
        !           937:        /*
        !           938:         * get block
        !           939:         */
        !           940:        if ((hp = mf_get(mfp, (blocknr_t)bnum, page_count)) == NULL)
        !           941:        {
        !           942:            if (bnum == 1)
        !           943:            {
        !           944:                EMSG2("Unable to read block 1 from %s", mfp->mf_fname);
        !           945:                goto theend;
        !           946:            }
        !           947:            ++error;
        !           948:            ml_append(lnum++, (char_u *)"???MANY LINES MISSING", (colnr_t)0, TRUE);
        !           949:        }
        !           950:        else            /* there is a block */
        !           951:        {
        !           952:            pp = (PTR_BL *)(hp->bh_data);
        !           953:            if (pp->pb_id == PTR_ID)            /* it is a pointer block */
        !           954:            {
        !           955:                    /* check line count when using pointer block first time */
        !           956:                if (idx == 0 && line_count != 0)
        !           957:                {
        !           958:                    for (i = 0; i < (int)pp->pb_count; ++i)
        !           959:                        line_count -= pp->pb_pointer[i].pe_line_count;
        !           960:                    if (line_count != 0)
        !           961:                    {
        !           962:                        ++error;
        !           963:                        ml_append(lnum++, (char_u *)"???LINE COUNT WRONG", (colnr_t)0, TRUE);
        !           964:                    }
        !           965:                }
        !           966:
        !           967:                if (pp->pb_count == 0)
        !           968:                {
        !           969:                    ml_append(lnum++, (char_u *)"???EMPTY BLOCK", (colnr_t)0, TRUE);
        !           970:                    ++error;
        !           971:                }
        !           972:                else if (idx < (int)pp->pb_count)       /* go a block deeper */
        !           973:                {
        !           974:                    if (pp->pb_pointer[idx].pe_bnum < 0)
        !           975:                    {
        !           976:                        /*
        !           977:                         * Data block with negative block number.
        !           978:                         * Try to read lines from the original file.
        !           979:                         * This is slow, but it works.
        !           980:                         */
        !           981:                        if (!cannot_open)
        !           982:                        {
        !           983:                            line_count = pp->pb_pointer[idx].pe_line_count;
        !           984:                            if (readfile(curbuf->b_filename, NULL, lnum, FALSE,
        !           985:                                        pp->pb_pointer[idx].pe_old_lnum - 1,
        !           986:                                        line_count, FALSE) == FAIL)
        !           987:                                cannot_open = TRUE;
        !           988:                            else
        !           989:                                lnum += line_count;
        !           990:                        }
        !           991:                        if (cannot_open)
        !           992:                        {
        !           993:                            ++error;
        !           994:                            ml_append(lnum++, (char_u *)"???LINES MISSING", (colnr_t)0, TRUE);
        !           995:                        }
        !           996:                        ++idx;      /* get same block again for next index */
        !           997:                        continue;
        !           998:                    }
        !           999:
        !          1000:                    /*
        !          1001:                     * going one block deeper in the tree
        !          1002:                     */
        !          1003:                    if ((top = ml_add_stack(buf)) < 0)      /* new entry in stack */
        !          1004:                    {
        !          1005:                        ++error;
        !          1006:                        break;              /* out of memory */
        !          1007:                    }
        !          1008:                    ip = &(buf->b_ml.ml_stack[top]);
        !          1009:                    ip->ip_bnum = bnum;
        !          1010:                    ip->ip_index = idx;
        !          1011:
        !          1012:                    bnum = pp->pb_pointer[idx].pe_bnum;
        !          1013:                    line_count = pp->pb_pointer[idx].pe_line_count;
        !          1014:                    page_count = pp->pb_pointer[idx].pe_page_count;
        !          1015:                    continue;
        !          1016:                }
        !          1017:            }
        !          1018:            else            /* not a pointer block */
        !          1019:            {
        !          1020:                dp = (DATA_BL *)(hp->bh_data);
        !          1021:                if (dp->db_id != DATA_ID)       /* block id wrong */
        !          1022:                {
        !          1023:                    if (bnum == 1)
        !          1024:                    {
        !          1025:                        EMSG2("Block 1 ID wrong (%s not a .swp file?)",
        !          1026:                                                               mfp->mf_fname);
        !          1027:                        goto theend;
        !          1028:                    }
        !          1029:                    ++error;
        !          1030:                    ml_append(lnum++, (char_u *)"???BLOCK MISSING", (colnr_t)0, TRUE);
        !          1031:                }
        !          1032:                else
        !          1033:                {
        !          1034:                    /*
        !          1035:                     * it is a data block
        !          1036:                     * Append all the lines in this block
        !          1037:                     */
        !          1038:                    has_error = FALSE;
        !          1039:                        /*
        !          1040:                         * check length of block
        !          1041:                         * if wrong, use length in pointer block
        !          1042:                         */
        !          1043:                    if (page_count * mfp->mf_page_size != dp->db_txt_end)
        !          1044:                    {
        !          1045:                        ml_append(lnum++, (char_u *)"??? from here until ???END lines may be messed up", (colnr_t)0, TRUE);
        !          1046:                        ++error;
        !          1047:                        has_error = TRUE;
        !          1048:                        dp->db_txt_end = page_count * mfp->mf_page_size;
        !          1049:                    }
        !          1050:
        !          1051:                        /* make sure there is a NUL at the end of the block */
        !          1052:                    *((char_u *)dp + dp->db_txt_end - 1) = NUL;
        !          1053:
        !          1054:                        /*
        !          1055:                         * check number of lines in block
        !          1056:                         * if wrong, use count in data block
        !          1057:                         */
        !          1058:                    if (line_count != dp->db_line_count)
        !          1059:                    {
        !          1060:                        ml_append(lnum++, (char_u *)"??? from here until ???END lines may have been inserted/deleted", (colnr_t)0, TRUE);
        !          1061:                        ++error;
        !          1062:                        has_error = TRUE;
        !          1063:                    }
        !          1064:
        !          1065:                    for (i = 0; i < dp->db_line_count; ++i)
        !          1066:                    {
        !          1067:                        txt_start = (dp->db_index[i] & DB_INDEX_MASK);
        !          1068:                        if (txt_start <= HEADER_SIZE || txt_start >= (int)dp->db_txt_end)
        !          1069:                        {
        !          1070:                            p = (char_u *)"???";
        !          1071:                            ++error;
        !          1072:                        }
        !          1073:                        else
        !          1074:                            p = (char_u *)dp + txt_start;
        !          1075:                        ml_append(lnum++, p, (colnr_t)0, TRUE);
        !          1076:                    }
        !          1077:                    if (has_error)
        !          1078:                        ml_append(lnum++, (char_u *)"???END", (colnr_t)0, TRUE);
        !          1079:                }
        !          1080:            }
        !          1081:        }
        !          1082:
        !          1083:        if (buf->b_ml.ml_stack_top == 0)        /* finished */
        !          1084:            break;
        !          1085:
        !          1086:        /*
        !          1087:         * go one block up in the tree
        !          1088:         */
        !          1089:        ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
        !          1090:        bnum = ip->ip_bnum;
        !          1091:        idx = ip->ip_index + 1;     /* go to next index */
        !          1092:        page_count = 1;
        !          1093:    }
        !          1094:
        !          1095:    /*
        !          1096:     * The dummy line from the empty buffer will now be after the last line in
        !          1097:     * the buffer. Delete it.
        !          1098:     */
        !          1099:    ml_delete(curbuf->b_ml.ml_line_count, FALSE);
        !          1100:
        !          1101:    recoverymode = FALSE;
        !          1102:    if (got_int)
        !          1103:        EMSG("Recovery Interrupted");
        !          1104:    else if (error)
        !          1105:        EMSG("Errors detected while recovering; look for lines starting with ???");
        !          1106:    else
        !          1107:    {
        !          1108:        MSG("Recovery completed. You should check if everything is OK.");
        !          1109:        MSG_OUTSTR("\n(You might want to write out this file under another name\n");
        !          1110:        MSG_OUTSTR("and run diff with the original file to check for changes)\n");
        !          1111:        MSG_OUTSTR("Delete the .swp file afterwards.\n\n");
        !          1112:        cmdline_row = msg_row;
        !          1113:    }
        !          1114:
        !          1115: theend:
        !          1116:    if (mfp != NULL)
        !          1117:    {
        !          1118:        if (hp != NULL)
        !          1119:            mf_put(mfp, hp, FALSE, FALSE);
        !          1120:        mf_close(mfp, FALSE);       /* will also vim_free(mfp->mf_fname) */
        !          1121:    }
        !          1122:    else
        !          1123:        vim_free(fname);
        !          1124:    vim_free(buf);
        !          1125:    if (serious_error && called_from_main)
        !          1126:        ml_close(curbuf, TRUE);
        !          1127:    return;
        !          1128: }
        !          1129:
        !          1130: /*
        !          1131:  * Find the names of swap files in current directory and the directory given
        !          1132:  * with the 'directory' option.
        !          1133:  *
        !          1134:  * Used to:
        !          1135:  * - list the swap files for "vim -r"
        !          1136:  * - count the number of swap files when recovering
        !          1137:  * - list the swap files when recovering
        !          1138:  * - find the name of the n'th swap file when recovering
        !          1139:  */
        !          1140:    int
        !          1141: recover_names(fname, list, nr)
        !          1142:    char_u      **fname;    /* base for swap file name */
        !          1143:    int         list;       /* when TRUE, list the swap file names */
        !          1144:    int         nr;         /* when non-zero, return nr'th swap file name */
        !          1145: {
        !          1146:    int         num_names;
        !          1147:    char_u      *(names[6]);
        !          1148:    char_u      *tail;
        !          1149:    char_u      *p;
        !          1150:    int         num_files;
        !          1151:    int         file_count = 0;
        !          1152:    char_u      **files;
        !          1153:    int         i;
        !          1154:    char_u      *dirp;
        !          1155:    char_u      *dir_name;
        !          1156:
        !          1157:    if (list)
        !          1158:    {
        !          1159:            /* use msg() to start the scrolling properly */
        !          1160:        msg((char_u *)"Swap files found:");
        !          1161:        msg_outchar('\n');
        !          1162:    }
        !          1163:    expand_interactively = TRUE;
        !          1164:    /*
        !          1165:     * Do the loop for every directory in 'directory'.
        !          1166:     */
        !          1167:    dirp = p_dir;
        !          1168:    while (*dirp)
        !          1169:    {
        !          1170:        /* find character after directory name */
        !          1171:        dir_name = dirp;
        !          1172:        while (*dirp && *dirp != ',')
        !          1173:            ++dirp;
        !          1174:        dir_name = strnsave(dir_name, (int)(dirp - dir_name));
        !          1175:        if (dir_name == NULL)       /* out of memory */
        !          1176:            break;
        !          1177:        if (*dir_name == '.')       /* check current dir */
        !          1178:        {
        !          1179:            if (fname == NULL || *fname == NULL)
        !          1180:            {
        !          1181:                names[0] = strsave((char_u *)"*.sw?");
        !          1182: #ifdef UNIX
        !          1183:                    /* for Unix names starting with a dot are special */
        !          1184:                names[1] = strsave((char_u *)".*.sw?");
        !          1185:                names[2] = strsave((char_u *)".sw?");
        !          1186:                num_names = 3;
        !          1187: #else
        !          1188:                num_names = 1;
        !          1189: #endif
        !          1190:            }
        !          1191:            else
        !          1192:                num_names = get_names(names, *fname);
        !          1193:        }
        !          1194:        else                        /* check directory dir_name */
        !          1195:        {
        !          1196:            if (fname == NULL || *fname == NULL)
        !          1197:            {
        !          1198:                names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
        !          1199: #ifdef UNIX
        !          1200:                    /* for Unix names starting with a dot are special */
        !          1201:                names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
        !          1202:                names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
        !          1203:                num_names = 3;
        !          1204: #else
        !          1205:                num_names = 1;
        !          1206: #endif
        !          1207:            }
        !          1208:            else
        !          1209:            {
        !          1210:                tail = gettail(*fname);
        !          1211:                tail = concat_fnames(dir_name, tail, TRUE);
        !          1212:                if (tail == NULL)
        !          1213:                    num_names = 0;
        !          1214:                else
        !          1215:                {
        !          1216:                    num_names = get_names(names, tail);
        !          1217:                    vim_free(tail);
        !          1218:                }
        !          1219:            }
        !          1220:        }
        !          1221:
        !          1222:            /* check for out-of-memory */
        !          1223:        for (i = 0; i < num_names; ++i)
        !          1224:        {
        !          1225:            if (names[i] == NULL)
        !          1226:            {
        !          1227:                for (i = 0; i < num_names; ++i)
        !          1228:                    vim_free(names[i]);
        !          1229:                num_names = 0;
        !          1230:            }
        !          1231:        }
        !          1232:        if (num_names == 0)
        !          1233:            num_files = 0;
        !          1234:        else if (ExpandWildCards(num_names, names,
        !          1235:                                     &num_files, &files, TRUE, FALSE) == FAIL)
        !          1236:        {
        !          1237:            MSG_OUTSTR(files);      /* print error message */
        !          1238:            num_files = 0;
        !          1239:        }
        !          1240:        /*
        !          1241:         * remove swapfile name of the current buffer, it must be ignored
        !          1242:         */
        !          1243:        if (curbuf->b_ml.ml_mfp != NULL &&
        !          1244:                                (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
        !          1245:        {
        !          1246:            for (i = 0; i < num_files; ++i)
        !          1247:                if (fullpathcmp(p, files[i]) == FPC_SAME)
        !          1248:                {
        !          1249:                    vim_free(files[i]);
        !          1250:                    --num_files;
        !          1251:                    for ( ; i < num_files; ++i)
        !          1252:                        files[i] = files[i + 1];
        !          1253:                    break;
        !          1254:                }
        !          1255:        }
        !          1256:        if (nr)
        !          1257:        {
        !          1258:            file_count += num_files;
        !          1259:            if (nr <= file_count)
        !          1260:            {
        !          1261:                *fname = strsave(files[nr - 1 + num_files - file_count]);
        !          1262:                dirp = (char_u *)"";                /* stop searching */
        !          1263:            }
        !          1264:        }
        !          1265:        else if (list)
        !          1266:        {
        !          1267:            if (*dir_name == '.')
        !          1268:            {
        !          1269:                if (fname == NULL || *fname == NULL)
        !          1270:                    MSG_OUTSTR("   In current directory:\n");
        !          1271:                else
        !          1272:                    MSG_OUTSTR("   Using specified name:\n");
        !          1273:            }
        !          1274:            else
        !          1275:            {
        !          1276:                MSG_OUTSTR("   In directory ");
        !          1277:                msg_home_replace(dir_name);
        !          1278:                MSG_OUTSTR(":\n");
        !          1279:            }
        !          1280:
        !          1281:            if (num_files)
        !          1282:            {
        !          1283:                for (i = 0; i < num_files; ++i)
        !          1284:                {
        !          1285:                    /* print the swap file name */
        !          1286:                    msg_outnum((long)++file_count);
        !          1287:                    MSG_OUTSTR(".    ");
        !          1288:                    msg_outstr(gettail(files[i]));
        !          1289:                    msg_outchar('\n');
        !          1290:                    swapfile_info(files[i]);
        !          1291:                }
        !          1292:            }
        !          1293:            else
        !          1294:                MSG_OUTSTR("      -- none --\n");
        !          1295:            flushbuf();
        !          1296:        }
        !          1297:        else
        !          1298:            file_count += num_files;
        !          1299:
        !          1300:        for (i = 0; i < num_names; ++i)
        !          1301:            vim_free(names[i]);
        !          1302:        FreeWild(num_files, files);
        !          1303:
        !          1304:        /* advance dirp to next directory name */
        !          1305:        vim_free(dir_name);
        !          1306:        if (*dirp == ',')
        !          1307:            ++dirp;
        !          1308:        dirp = skipwhite(dirp);
        !          1309:    }
        !          1310:    expand_interactively = FALSE;
        !          1311:    return file_count;
        !          1312: }
        !          1313:
        !          1314: /*
        !          1315:  * Give information about an existing swap file
        !          1316:  */
        !          1317:    static void
        !          1318: swapfile_info(fname)
        !          1319:    char_u      *fname;
        !          1320: {
        !          1321:    struct stat     st;
        !          1322:    int             fd;
        !          1323:    struct block0   b0;
        !          1324:    time_t          x;
        !          1325:
        !          1326:    /* print the swap file date */
        !          1327:    if (stat((char *)fname, &st) != -1)
        !          1328:    {
        !          1329:        MSG_OUTSTR("             dated: ");
        !          1330:        x = st.st_mtime;                    /* Manx C can't do &st.st_mtime */
        !          1331:        MSG_OUTSTR(ctime(&x));
        !          1332:    }
        !          1333:
        !          1334:    /*
        !          1335:     * print the original file name
        !          1336:     */
        !          1337:    fd = open((char *)fname, O_RDONLY | O_EXTRA);
        !          1338:    if (fd >= 0)
        !          1339:    {
        !          1340:        if (read(fd, (char *)&b0, sizeof(b0)) == sizeof(b0))
        !          1341:        {
        !          1342:            if (b0.b0_id[0] != BLOCK0_ID0 ||
        !          1343:                                b0.b0_id[1] != BLOCK0_ID1)
        !          1344:                MSG_OUTSTR("         [is not a swap file]");
        !          1345:            else
        !          1346:            {
        !          1347:                MSG_OUTSTR("         file name: ");
        !          1348:                msg_outstr(b0.b0_fname);
        !          1349:
        !          1350:                if (*(b0.b0_hname) != NUL)
        !          1351:                {
        !          1352:                    MSG_OUTSTR("\n         host name: ");
        !          1353:                    msg_outstr(b0.b0_hname);
        !          1354:                }
        !          1355:
        !          1356:                if (*(b0.b0_uname) != NUL)
        !          1357:                {
        !          1358:                    MSG_OUTSTR("\n         user name: ");
        !          1359:                    msg_outstr(b0.b0_uname);
        !          1360:                }
        !          1361:
        !          1362:                if (char_to_long(b0.b0_pid) != 0L)
        !          1363:                {
        !          1364:                    MSG_OUTSTR("\n        process ID: ");
        !          1365:                    msg_outnum(char_to_long(b0.b0_pid));
        !          1366: #if defined(UNIX) || defined(__EMX__)
        !          1367:                    /* EMX kill() not working correctly, it seems */
        !          1368:                    if (kill(char_to_long(b0.b0_pid), 0) == 0)
        !          1369:                        MSG_OUTSTR(" (still running)");
        !          1370: #endif
        !          1371:                }
        !          1372:
        !          1373:                if (b0_magic_wrong(&b0))
        !          1374:                {
        !          1375: #if defined(MSDOS) || defined(WIN32)
        !          1376:                    if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
        !          1377:                        MSG_OUTSTR("\n         [not usable with this version of Vim]");
        !          1378:                    else
        !          1379: #endif
        !          1380:                        MSG_OUTSTR("\n         [not usable on this computer]");
        !          1381:                }
        !          1382:
        !          1383:            }
        !          1384:        }
        !          1385:        else
        !          1386:            MSG_OUTSTR("         [cannot be read]");
        !          1387:        close(fd);
        !          1388:    }
        !          1389:    else
        !          1390:        MSG_OUTSTR("         [cannot be opened]");
        !          1391:    msg_outchar('\n');
        !          1392: }
        !          1393:
        !          1394:    static int
        !          1395: get_names(names, fname)
        !          1396:    char_u **names;
        !          1397:    char_u  *fname;
        !          1398: {
        !          1399:    int     num_names;
        !          1400:
        !          1401: #ifdef SHORT_FNAME
        !          1402:    /*
        !          1403:     * (MS-DOS) always short names
        !          1404:     */
        !          1405:    names[0] = modname(fname, (char_u *)".sw?");
        !          1406:    num_names = 1;
        !          1407: #else /* !SHORT_FNAME */
        !          1408: # ifdef WIN32
        !          1409:    /*
        !          1410:     * (WIN32) never short names
        !          1411:     */
        !          1412:    num_names = 1;
        !          1413:    names[0] = concat_fnames(fname, (char_u *)".sw?", FALSE);
        !          1414: # else /* !WIN32 */
        !          1415:    /*
        !          1416:     * (Not MS-DOS or WIN32) maybe short name, maybe not: try both.
        !          1417:     * Only use the short name if it is different.
        !          1418:     */
        !          1419:
        !          1420:    int     i;
        !          1421:
        !          1422:    names[0] = concat_fnames(fname, (char_u *)".sw?", FALSE);
        !          1423:    i = curbuf->b_shortname;
        !          1424:    curbuf->b_shortname = TRUE;
        !          1425:    names[1] = modname(fname, (char_u *)".sw?");
        !          1426:    curbuf->b_shortname = i;
        !          1427:    if (STRCMP(names[0], names[1]) == 0)
        !          1428:    {
        !          1429:        vim_free(names[1]);
        !          1430:        num_names = 1;
        !          1431:    }
        !          1432:    else
        !          1433:        num_names = 2;
        !          1434: # endif /* !WIN32 */
        !          1435: #endif /* !SHORT_FNAME */
        !          1436:    return num_names;
        !          1437: }
        !          1438:
        !          1439: /*
        !          1440:  * sync all memlines
        !          1441:  *
        !          1442:  * If 'check_file' is TRUE, check if original file exists and was not changed.
        !          1443:  * If 'check_char' is TRUE, stop syncing when character becomes available, but
        !          1444:  * always sync at least one block.
        !          1445:  */
        !          1446:    void
        !          1447: ml_sync_all(check_file, check_char)
        !          1448:    int     check_file;
        !          1449:    int     check_char;
        !          1450: {
        !          1451:    BUF             *buf;
        !          1452:    struct stat     st;
        !          1453:
        !          1454:    for (buf = firstbuf; buf != NULL; buf = buf->b_next)
        !          1455:    {
        !          1456:        if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL)
        !          1457:            continue;                       /* no file */
        !          1458:
        !          1459:        ml_flush_line(buf);                 /* flush buffered line */
        !          1460:                                            /* flush locked block */
        !          1461:        (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH);
        !          1462:        if (buf->b_changed && check_file && mf_need_trans(buf->b_ml.ml_mfp) &&
        !          1463:                                    buf->b_filename != NULL)
        !          1464:        {
        !          1465:            /*
        !          1466:             * if original file does not exist anymore or has been changed
        !          1467:             * call ml_preserve to get rid of all negative numbered blocks
        !          1468:             */
        !          1469:            if (stat((char *)buf->b_filename, &st) == -1 ||
        !          1470:                                st.st_mtime != buf->b_mtime_read)
        !          1471:            {
        !          1472:                ml_preserve(buf, FALSE);
        !          1473:                need_check_timestamps = TRUE;   /* give message later */
        !          1474:            }
        !          1475:        }
        !          1476:        if (buf->b_ml.ml_mfp->mf_dirty)
        !          1477:        {
        !          1478:            mf_sync(buf->b_ml.ml_mfp, FALSE, check_char, buf->b_changed);
        !          1479:            if (check_char && mch_char_avail()) /* character available now */
        !          1480:                break;
        !          1481:        }
        !          1482:    }
        !          1483: }
        !          1484:
        !          1485: /*
        !          1486:  * sync one buffer, including negative blocks
        !          1487:  *
        !          1488:  * after this all the blocks are in the swap file
        !          1489:  *
        !          1490:  * Used for the :preserve command and when the original file has been
        !          1491:  * changed or deleted.
        !          1492:  *
        !          1493:  * when message is TRUE the success of preserving is reported
        !          1494:  */
        !          1495:    void
        !          1496: ml_preserve(buf, message)
        !          1497:    BUF     *buf;
        !          1498:    int     message;
        !          1499: {
        !          1500:    BHDR        *hp;
        !          1501:    linenr_t    lnum;
        !          1502:    MEMFILE     *mfp = buf->b_ml.ml_mfp;
        !          1503:    int         status;
        !          1504:
        !          1505:    if (mfp == NULL || mfp->mf_fname == NULL)
        !          1506:    {
        !          1507:        if (message)
        !          1508:            EMSG("Cannot preserve, there is no swap file");
        !          1509:        return;
        !          1510:    }
        !          1511:
        !          1512:    ml_flush_line(buf);                             /* flush buffered line */
        !          1513:    (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH); /* flush locked block */
        !          1514:    status = mf_sync(mfp, TRUE, FALSE, TRUE);
        !          1515:
        !          1516:            /* stack is invalid after mf_sync(.., TRUE, ..) */
        !          1517:    buf->b_ml.ml_stack_top = 0;
        !          1518:
        !          1519:    /*
        !          1520:     * Some of the data blocks may have been changed from negative to
        !          1521:     * positive block number. In that case the pointer blocks need to be
        !          1522:     * updated.
        !          1523:     *
        !          1524:     * We don't know in which pointer block the references are, so we visit
        !          1525:     * all data blocks until there are no more translations to be done (or
        !          1526:     * we hit the end of the file, which can only happen in case a write fails,
        !          1527:     * e.g. when file system if full).
        !          1528:     * ml_find_line() does the work by translating the negative block numbers
        !          1529:     * when getting the first line of each data block.
        !          1530:     */
        !          1531:    if (mf_need_trans(mfp))
        !          1532:    {
        !          1533:        lnum = 1;
        !          1534:        while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
        !          1535:        {
        !          1536:            hp = ml_find_line(buf, lnum, ML_FIND);
        !          1537:            if (hp == NULL)
        !          1538:            {
        !          1539:                status = FAIL;
        !          1540:                goto theend;
        !          1541:            }
        !          1542:            CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
        !          1543:            lnum = buf->b_ml.ml_locked_high + 1;
        !          1544:        }
        !          1545:        (void)ml_find_line(buf, (linenr_t)0, ML_FLUSH); /* flush locked block */
        !          1546:        if (mf_sync(mfp, TRUE, FALSE, TRUE) == FAIL)    /* sync the updated pointer blocks */
        !          1547:            status = FAIL;
        !          1548:        buf->b_ml.ml_stack_top = 0;         /* stack is invalid now */
        !          1549:    }
        !          1550: theend:
        !          1551:    if (message)
        !          1552:    {
        !          1553:        if (status == OK)
        !          1554:            MSG("File preserved");
        !          1555:        else
        !          1556:            EMSG("Preserve failed");
        !          1557:    }
        !          1558: }
        !          1559:
        !          1560: /*
        !          1561:  * get a pointer to a (read-only copy of a) line
        !          1562:  *
        !          1563:  * On failure an error message is given and IObuff is returned (to avoid
        !          1564:  * having to check for error everywhere).
        !          1565:  */
        !          1566:    char_u  *
        !          1567: ml_get(lnum)
        !          1568:    linenr_t    lnum;
        !          1569: {
        !          1570:    return ml_get_buf(curbuf, lnum, FALSE);
        !          1571: }
        !          1572:
        !          1573: /*
        !          1574:  * ml_get_pos: get pointer to position 'pos'
        !          1575:  */
        !          1576:     char_u *
        !          1577: ml_get_pos(pos)
        !          1578:     FPOS   *pos;
        !          1579: {
        !          1580:    return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
        !          1581: }
        !          1582:
        !          1583: /*
        !          1584:  * ml_get_pos: get pointer to cursor line.
        !          1585:  */
        !          1586:    char_u *
        !          1587: ml_get_curline()
        !          1588: {
        !          1589:    return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
        !          1590: }
        !          1591:
        !          1592: /*
        !          1593:  * ml_get_pos: get pointer to cursor position
        !          1594:  */
        !          1595:    char_u *
        !          1596: ml_get_cursor()
        !          1597: {
        !          1598:    return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
        !          1599:                                                        curwin->w_cursor.col);
        !          1600: }
        !          1601:
        !          1602: /*
        !          1603:  * get a pointer to a line in a specific buffer
        !          1604:  *
        !          1605:  *  will_change: if TRUE mark the buffer dirty (chars in the line will be
        !          1606:  *  changed)
        !          1607:  */
        !          1608:    char_u  *
        !          1609: ml_get_buf(buf, lnum, will_change)
        !          1610:    BUF         *buf;
        !          1611:    linenr_t    lnum;
        !          1612:    int         will_change;            /* line will be changed */
        !          1613: {
        !          1614:    BHDR    *hp;
        !          1615:    DATA_BL *dp;
        !          1616:    char_u  *ptr;
        !          1617:
        !          1618:    if (lnum > buf->b_ml.ml_line_count) /* invalid line number */
        !          1619:    {
        !          1620:        EMSGN("ml_get: invalid lnum: %ld", lnum);
        !          1621: errorret:
        !          1622:        STRCPY(IObuff, "???");
        !          1623:        return IObuff;
        !          1624:    }
        !          1625:    if (lnum <= 0)                      /* pretend line 0 is line 1 */
        !          1626:        lnum = 1;
        !          1627:
        !          1628:    if (buf->b_ml.ml_mfp == NULL)       /* there are no lines */
        !          1629:        return (char_u *)"";
        !          1630:
        !          1631: /*
        !          1632:  * See if it is the same line as requested last time.
        !          1633:  * Otherwise may need to flush last used line.
        !          1634:  */
        !          1635:    if (buf->b_ml.ml_line_lnum != lnum)
        !          1636:    {
        !          1637:        ml_flush_line(buf);
        !          1638:
        !          1639:        /*
        !          1640:         * find the data block containing the line
        !          1641:         * This also fills the stack with the blocks from the root to the data block
        !          1642:         * This also releases any locked block.
        !          1643:         */
        !          1644:        if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
        !          1645:        {
        !          1646:            EMSGN("ml_get: cannot find line %ld", lnum);
        !          1647:            goto errorret;
        !          1648:        }
        !          1649:
        !          1650:        dp = (DATA_BL *)(hp->bh_data);
        !          1651:
        !          1652:        ptr = (char_u *)dp + ((dp->db_index[lnum - buf->b_ml.ml_locked_low]) & DB_INDEX_MASK);
        !          1653:        buf->b_ml.ml_line_ptr = ptr;
        !          1654:        buf->b_ml.ml_line_lnum = lnum;
        !          1655:        buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
        !          1656:    }
        !          1657:    if (will_change)
        !          1658:        buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
        !          1659:
        !          1660:    return buf->b_ml.ml_line_ptr;
        !          1661: }
        !          1662:
        !          1663: /*
        !          1664:  * Check if a line that was just obtained by a call to ml_get
        !          1665:  * is in allocated memory.
        !          1666:  */
        !          1667:    int
        !          1668: ml_line_alloced()
        !          1669: {
        !          1670:    return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
        !          1671: }
        !          1672:
        !          1673: /*
        !          1674:  * append a line after lnum (may be 0 to insert a line in front of the file)
        !          1675:  *
        !          1676:  *   newfile: TRUE when starting to edit a new file, meaning that pe_old_lnum
        !          1677:  *             will be set for recovery
        !          1678:  *
        !          1679:  * return FAIL for failure, OK otherwise
        !          1680:  */
        !          1681:    int
        !          1682: ml_append(lnum, line, len, newfile)
        !          1683:    linenr_t    lnum;           /* append after this line (can be 0) */
        !          1684:    char_u      *line;          /* text of the new line */
        !          1685:    colnr_t     len;            /* length of new line, including NUL, or 0 */
        !          1686:    int         newfile;        /* flag, see above */
        !          1687: {
        !          1688:    if (curbuf->b_ml.ml_line_lnum != 0)
        !          1689:        ml_flush_line(curbuf);
        !          1690:    return ml_append_int(curbuf, lnum, line, len, newfile);
        !          1691: }
        !          1692:
        !          1693:    static int
        !          1694: ml_append_int(buf, lnum, line, len, newfile)
        !          1695:    BUF         *buf;
        !          1696:    linenr_t    lnum;           /* append after this line (can be 0) */
        !          1697:    char_u      *line;          /* text of the new line */
        !          1698:    colnr_t     len;            /* length of line, including NUL, or 0 */
        !          1699:    int         newfile;        /* flag, see above */
        !          1700: {
        !          1701:    int         i;
        !          1702:    int         line_count;     /* number of indexes in current block */
        !          1703:    int         offset;
        !          1704:    int         from, to;
        !          1705:    int         space_needed;   /* space needed for new line */
        !          1706:    int         page_size;
        !          1707:    int         page_count;
        !          1708:    int         db_idx;         /* index for lnum in data block */
        !          1709:    BHDR        *hp;
        !          1710:    MEMFILE     *mfp;
        !          1711:    DATA_BL     *dp;
        !          1712:    PTR_BL      *pp;
        !          1713:    IPTR        *ip;
        !          1714:
        !          1715:                                        /* lnum out of range */
        !          1716:    if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
        !          1717:        return FAIL;
        !          1718:
        !          1719:    if (lowest_marked && lowest_marked > lnum)
        !          1720:        lowest_marked = lnum + 1;
        !          1721:
        !          1722:    if (len == 0)
        !          1723:        len = STRLEN(line) + 1;         /* space needed for the text */
        !          1724:    space_needed = len + INDEX_SIZE;    /* space needed for text + index */
        !          1725:
        !          1726:    mfp = buf->b_ml.ml_mfp;
        !          1727:    page_size = mfp->mf_page_size;
        !          1728:
        !          1729: /*
        !          1730:  * find the data block containing the previous line
        !          1731:  * This also fills the stack with the blocks from the root to the data block
        !          1732:  * This also releases any locked block.
        !          1733:  */
        !          1734:    if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_t)1 : lnum,
        !          1735:                                                    ML_INSERT)) == NULL)
        !          1736:        return FAIL;
        !          1737:
        !          1738:    buf->b_ml.ml_flags &= ~ML_EMPTY;
        !          1739:
        !          1740:    if (lnum == 0)              /* got line one instead, correct db_idx */
        !          1741:        db_idx = -1;            /* careful, it is negative! */
        !          1742:    else
        !          1743:        db_idx = lnum - buf->b_ml.ml_locked_low;
        !          1744:                /* get line count before the insertion */
        !          1745:    line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
        !          1746:
        !          1747:    dp = (DATA_BL *)(hp->bh_data);
        !          1748:
        !          1749: /*
        !          1750:  * If
        !          1751:  * - there is not enough room in the current block
        !          1752:  * - appending to the last line in the block
        !          1753:  * - not appending to the last line in the file
        !          1754:  * insert in front of the next block.
        !          1755:  */
        !          1756:    if ((int)dp->db_free < space_needed && db_idx == line_count - 1 &&
        !          1757:                                            lnum < buf->b_ml.ml_line_count)
        !          1758:    {
        !          1759:        /*
        !          1760:         * Now that the line is not going to be inserted in the block that we
        !          1761:         * expected, the line count has to be adjusted in the pointer blocks
        !          1762:         * by using ml_locked_lineadd.
        !          1763:         */
        !          1764:        --(buf->b_ml.ml_locked_lineadd);
        !          1765:        --(buf->b_ml.ml_locked_high);
        !          1766:        if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
        !          1767:            return FAIL;
        !          1768:
        !          1769:        db_idx = -1;                /* careful, it is negative! */
        !          1770:                    /* get line count before the insertion */
        !          1771:        line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
        !          1772:        CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
        !          1773:
        !          1774:        dp = (DATA_BL *)(hp->bh_data);
        !          1775:    }
        !          1776:
        !          1777:    ++buf->b_ml.ml_line_count;
        !          1778:
        !          1779:    if ((int)dp->db_free >= space_needed)       /* enough room in data block */
        !          1780:    {
        !          1781: /*
        !          1782:  * Insert new line in existing data block, or in data block allocated above.
        !          1783:  */
        !          1784:        dp->db_txt_start -= len;
        !          1785:        dp->db_free -= space_needed;
        !          1786:        ++(dp->db_line_count);
        !          1787:
        !          1788:        /*
        !          1789:         * move the text of the lines that follow to the front
        !          1790:         * adjust the indexes of the lines that follow
        !          1791:         */
        !          1792:        if (line_count > db_idx + 1)        /* if there are following lines */
        !          1793:        {
        !          1794:            /*
        !          1795:             * Offset is the start of the previous line.
        !          1796:             * This will become the character just after the new line.
        !          1797:             */
        !          1798:            if (db_idx < 0)
        !          1799:                offset = dp->db_txt_end;
        !          1800:            else
        !          1801:                offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
        !          1802:            vim_memmove((char *)dp + dp->db_txt_start,
        !          1803:                                          (char *)dp + dp->db_txt_start + len,
        !          1804:                                 (size_t)(offset - (dp->db_txt_start + len)));
        !          1805:            for (i = line_count - 1; i > db_idx; --i)
        !          1806:                dp->db_index[i + 1] = dp->db_index[i] - len;
        !          1807:            dp->db_index[db_idx + 1] = offset - len;
        !          1808:        }
        !          1809:        else                                /* add line at the end */
        !          1810:            dp->db_index[db_idx + 1] = dp->db_txt_start;
        !          1811:
        !          1812:        /*
        !          1813:         * copy the text into the block
        !          1814:         */
        !          1815:        vim_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
        !          1816:
        !          1817:        /*
        !          1818:         * Mark the block dirty.
        !          1819:         */
        !          1820:        buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
        !          1821:        if (!newfile)
        !          1822:            buf->b_ml.ml_flags |= ML_LOCKED_POS;
        !          1823:    }
        !          1824:    else            /* not enough space in data block */
        !          1825:    {
        !          1826: /*
        !          1827:  * If there is not enough room we have to create a new data block and copy some
        !          1828:  * lines into it.
        !          1829:  * Then we have to insert an entry in the pointer block.
        !          1830:  * If this pointer block also is full, we go up another block, and so on, up
        !          1831:  * to the root if necessary.
        !          1832:  * The line counts in the pointer blocks have already been adjusted by
        !          1833:  * ml_find_line().
        !          1834:  */
        !          1835:        long        line_count_left, line_count_right;
        !          1836:        int         page_count_left, page_count_right;
        !          1837:        BHDR        *hp_left;
        !          1838:        BHDR        *hp_right;
        !          1839:        BHDR        *hp_new;
        !          1840:        int         lines_moved;
        !          1841:        int         data_moved = 0;         /* init to shut up gcc */
        !          1842:        int         total_moved = 0;        /* init to shut up gcc */
        !          1843:        DATA_BL     *dp_right, *dp_left;
        !          1844:        int         stack_idx;
        !          1845:        int         in_left;
        !          1846:        int         lineadd;
        !          1847:        blocknr_t   bnum_left, bnum_right;
        !          1848:        linenr_t    lnum_left, lnum_right;
        !          1849:        int         pb_idx;
        !          1850:        PTR_BL      *pp_new;
        !          1851:
        !          1852:        /*
        !          1853:         * We are going to allocate a new data block. Depending on the
        !          1854:         * situation it will be put to the left or right of the existing
        !          1855:         * block.  If possible we put the new line in the left block and move
        !          1856:         * the lines after it to the right block. Otherwise the new line is
        !          1857:         * also put in the right block. This method is more efficient when
        !          1858:         * inserting a lot of lines at one place.
        !          1859:         */
        !          1860:        if (db_idx < 0)         /* left block is new, right block is existing */
        !          1861:        {
        !          1862:            lines_moved = 0;
        !          1863:            in_left = TRUE;
        !          1864:            /* space_needed does not change */
        !          1865:        }
        !          1866:        else                    /* left block is existing, right block is new */
        !          1867:        {
        !          1868:            lines_moved = line_count - db_idx - 1;
        !          1869:            if (lines_moved == 0)
        !          1870:                in_left = FALSE;        /* put new line in right block */
        !          1871:                                        /* space_needed does not change */
        !          1872:            else
        !          1873:            {
        !          1874:                data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
        !          1875:                                                            dp->db_txt_start;
        !          1876:                total_moved = data_moved + lines_moved * INDEX_SIZE;
        !          1877:                if ((int)dp->db_free + total_moved >= space_needed)
        !          1878:                {
        !          1879:                    in_left = TRUE;     /* put new line in left block */
        !          1880:                    space_needed = total_moved;
        !          1881:                }
        !          1882:                else
        !          1883:                {
        !          1884:                    in_left = FALSE;        /* put new line in right block */
        !          1885:                    space_needed += total_moved;
        !          1886:                }
        !          1887:            }
        !          1888:        }
        !          1889:
        !          1890:        page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
        !          1891:        if ((hp_new = ml_new_data(mfp, newfile, page_count)) == NULL)
        !          1892:        {
        !          1893:                        /* correct line counts in pointer blocks */
        !          1894:            --(buf->b_ml.ml_locked_lineadd);
        !          1895:            --(buf->b_ml.ml_locked_high);
        !          1896:            return FAIL;
        !          1897:        }
        !          1898:        if (db_idx < 0)         /* left block is new */
        !          1899:        {
        !          1900:            hp_left = hp_new;
        !          1901:            hp_right = hp;
        !          1902:            line_count_left = 0;
        !          1903:            line_count_right = line_count;
        !          1904:        }
        !          1905:        else                    /* right block is new */
        !          1906:        {
        !          1907:            hp_left = hp;
        !          1908:            hp_right = hp_new;
        !          1909:            line_count_left = line_count;
        !          1910:            line_count_right = 0;
        !          1911:        }
        !          1912:        dp_right = (DATA_BL *)(hp_right->bh_data);
        !          1913:        dp_left = (DATA_BL *)(hp_left->bh_data);
        !          1914:        bnum_left = hp_left->bh_bnum;
        !          1915:        bnum_right = hp_right->bh_bnum;
        !          1916:        page_count_left = hp_left->bh_page_count;
        !          1917:        page_count_right = hp_right->bh_page_count;
        !          1918:
        !          1919:        /*
        !          1920:         * May move the new line into the right/new block.
        !          1921:         */
        !          1922:        if (!in_left)
        !          1923:        {
        !          1924:            dp_right->db_txt_start -= len;
        !          1925:            dp_right->db_free -= len + INDEX_SIZE;
        !          1926:            dp_right->db_index[0] = dp_right->db_txt_start;
        !          1927:            vim_memmove((char *)dp_right + dp_right->db_txt_start,
        !          1928:                                                           line, (size_t)len);
        !          1929:            ++line_count_right;
        !          1930:        }
        !          1931:        /*
        !          1932:         * may move lines from the left/old block to the right/new one.
        !          1933:         */
        !          1934:        if (lines_moved)
        !          1935:        {
        !          1936:            /*
        !          1937:             */
        !          1938:            dp_right->db_txt_start -= data_moved;
        !          1939:            dp_right->db_free -= total_moved;
        !          1940:            vim_memmove((char *)dp_right + dp_right->db_txt_start,
        !          1941:                        (char *)dp_left + dp_left->db_txt_start,
        !          1942:                        (size_t)data_moved);
        !          1943:            offset = dp_right->db_txt_start - dp_left->db_txt_start;
        !          1944:            dp_left->db_txt_start += data_moved;
        !          1945:            dp_left->db_free += total_moved;
        !          1946:
        !          1947:            /*
        !          1948:             * update indexes in the new block
        !          1949:             */
        !          1950:            for (to = line_count_right, from = db_idx + 1;
        !          1951:                                         from < line_count_left; ++from, ++to)
        !          1952:                dp_right->db_index[to] = dp->db_index[from] + offset;
        !          1953:            line_count_right += lines_moved;
        !          1954:            line_count_left -= lines_moved;
        !          1955:        }
        !          1956:
        !          1957:        /*
        !          1958:         * May move the new line into the left (old or new) block.
        !          1959:         */
        !          1960:        if (in_left)
        !          1961:        {
        !          1962:            dp_left->db_txt_start -= len;
        !          1963:            dp_left->db_free -= len + INDEX_SIZE;
        !          1964:            dp_left->db_index[line_count_left] = dp_left->db_txt_start;
        !          1965:            vim_memmove((char *)dp_left + dp_left->db_txt_start,
        !          1966:                                                           line, (size_t)len);
        !          1967:            ++line_count_left;
        !          1968:        }
        !          1969:
        !          1970:        if (db_idx < 0)         /* left block is new */
        !          1971:        {
        !          1972:            lnum_left = lnum + 1;
        !          1973:            lnum_right = 0;
        !          1974:        }
        !          1975:        else                    /* right block is new */
        !          1976:        {
        !          1977:            lnum_left = 0;
        !          1978:            if (in_left)
        !          1979:                lnum_right = lnum + 2;
        !          1980:            else
        !          1981:                lnum_right = lnum + 1;
        !          1982:        }
        !          1983:        dp_left->db_line_count = line_count_left;
        !          1984:        dp_right->db_line_count = line_count_right;
        !          1985:
        !          1986:        /*
        !          1987:         * release the two data blocks
        !          1988:         * The new one (hp_new) already has a correct blocknumber.
        !          1989:         * The old one (hp, in ml_locked) gets a positive blocknumber if
        !          1990:         * we changed it and we are not editing a new file.
        !          1991:         */
        !          1992:        if (lines_moved || in_left)
        !          1993:            buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
        !          1994:        if (!newfile && db_idx >= 0 && in_left)
        !          1995:            buf->b_ml.ml_flags |= ML_LOCKED_POS;
        !          1996:        mf_put(mfp, hp_new, TRUE, FALSE);
        !          1997:
        !          1998:        /*
        !          1999:         * flush the old data block
        !          2000:         * set ml_locked_lineadd to 0, because the updating of the
        !          2001:         * pointer blocks is done below
        !          2002:         */
        !          2003:        lineadd = buf->b_ml.ml_locked_lineadd;
        !          2004:        buf->b_ml.ml_locked_lineadd = 0;
        !          2005:        ml_find_line(buf, (linenr_t)0, ML_FLUSH);   /* flush data block */
        !          2006:
        !          2007:        /*
        !          2008:         * update pointer blocks for the new data block
        !          2009:         */
        !          2010:        for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; --stack_idx)
        !          2011:        {
        !          2012:            ip = &(buf->b_ml.ml_stack[stack_idx]);
        !          2013:            pb_idx = ip->ip_index;
        !          2014:            if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
        !          2015:                return FAIL;
        !          2016:            pp = (PTR_BL *)(hp->bh_data);   /* must be pointer block */
        !          2017:            if (pp->pb_id != PTR_ID)
        !          2018:            {
        !          2019:                EMSG("pointer block id wrong 3");
        !          2020:                mf_put(mfp, hp, FALSE, FALSE);
        !          2021:                return FAIL;
        !          2022:            }
        !          2023:            /*
        !          2024:             * TODO: If the pointer block is full and we are adding at the end
        !          2025:             * try to insert in front of the next block
        !          2026:             */
        !          2027:            if (pp->pb_count < pp->pb_count_max)    /* block not full, add one entry */
        !          2028:            {
        !          2029:                if (pb_idx + 1 < (int)pp->pb_count)
        !          2030:                    vim_memmove(&pp->pb_pointer[pb_idx + 2],
        !          2031:                                &pp->pb_pointer[pb_idx + 1],
        !          2032:                        (size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
        !          2033:                ++pp->pb_count;
        !          2034:                pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
        !          2035:                pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
        !          2036:                pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
        !          2037:                pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
        !          2038:                pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
        !          2039:                pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
        !          2040:
        !          2041:                if (lnum_left != 0)
        !          2042:                    pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
        !          2043:                if (lnum_right != 0)
        !          2044:                    pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
        !          2045:
        !          2046:                mf_put(mfp, hp, TRUE, FALSE);
        !          2047:                buf->b_ml.ml_stack_top = stack_idx + 1;     /* truncate stack */
        !          2048:
        !          2049:                if (lineadd)
        !          2050:                {
        !          2051:                    --(buf->b_ml.ml_stack_top);
        !          2052:                        /* fix line count for rest of blocks in the stack */
        !          2053:                    ml_lineadd(buf, lineadd);
        !          2054:                                                        /* fix stack itself */
        !          2055:                    buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
        !          2056:                                                                      lineadd;
        !          2057:                    ++(buf->b_ml.ml_stack_top);
        !          2058:                }
        !          2059:
        !          2060:                return OK;
        !          2061:            }
        !          2062:            else                        /* pointer block full */
        !          2063:            {
        !          2064:                /*
        !          2065:                 * split the pointer block
        !          2066:                 * allocate a new pointer block
        !          2067:                 * move some of the pointer into the new block
        !          2068:                 * prepare for updating the parent block
        !          2069:                 */
        !          2070:                for (;;)        /* do this twice when splitting block 1 */
        !          2071:                {
        !          2072:                    hp_new = ml_new_ptr(mfp);
        !          2073:                    if (hp_new == NULL)     /* TODO: try to fix tree */
        !          2074:                        return FAIL;
        !          2075:                    pp_new = (PTR_BL *)(hp_new->bh_data);
        !          2076:
        !          2077:                    if (hp->bh_bnum != 1)
        !          2078:                        break;
        !          2079:
        !          2080:                    /*
        !          2081:                     * if block 1 becomes full the tree is given an extra level
        !          2082:                     * The pointers from block 1 are moved into the new block.
        !          2083:                     * block 1 is updated to point to the new block
        !          2084:                     * then continue to split the new block
        !          2085:                     */
        !          2086:                    vim_memmove(pp_new, pp, (size_t)page_size);
        !          2087:                    pp->pb_count = 1;
        !          2088:                    pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
        !          2089:                    pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
        !          2090:                    pp->pb_pointer[0].pe_old_lnum = 1;
        !          2091:                    pp->pb_pointer[0].pe_page_count = 1;
        !          2092:                    mf_put(mfp, hp, TRUE, FALSE);   /* release block 1 */
        !          2093:                    hp = hp_new;                    /* new block is to be split */
        !          2094:                    pp = pp_new;
        !          2095:                    CHECK(stack_idx != 0, "stack_idx should be 0");
        !          2096:                    ip->ip_index = 0;
        !          2097:                    ++stack_idx;        /* do block 1 again later */
        !          2098:                }
        !          2099:                /*
        !          2100:                 * move the pointers after the current one to the new block
        !          2101:                 * If there are none, the new entry will be in the new block.
        !          2102:                 */
        !          2103:                total_moved = pp->pb_count - pb_idx - 1;
        !          2104:                if (total_moved)
        !          2105:                {
        !          2106:                    vim_memmove(&pp_new->pb_pointer[0],
        !          2107:                                &pp->pb_pointer[pb_idx + 1],
        !          2108:                                (size_t)(total_moved) * sizeof(PTR_EN));
        !          2109:                    pp_new->pb_count = total_moved;
        !          2110:                    pp->pb_count -= total_moved - 1;
        !          2111:                    pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
        !          2112:                    pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
        !          2113:                    pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
        !          2114:                    if (lnum_right)
        !          2115:                        pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
        !          2116:                }
        !          2117:                else
        !          2118:                {
        !          2119:                    pp_new->pb_count = 1;
        !          2120:                    pp_new->pb_pointer[0].pe_bnum = bnum_right;
        !          2121:                    pp_new->pb_pointer[0].pe_line_count = line_count_right;
        !          2122:                    pp_new->pb_pointer[0].pe_page_count = page_count_right;
        !          2123:                    pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
        !          2124:                }
        !          2125:                pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
        !          2126:                pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
        !          2127:                pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
        !          2128:                if (lnum_left)
        !          2129:                    pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
        !          2130:                lnum_left = 0;
        !          2131:                lnum_right = 0;
        !          2132:
        !          2133:                /*
        !          2134:                 * recompute line counts
        !          2135:                 */
        !          2136:                line_count_right = 0;
        !          2137:                for (i = 0; i < (int)pp_new->pb_count; ++i)
        !          2138:                    line_count_right += pp_new->pb_pointer[i].pe_line_count;
        !          2139:                line_count_left = 0;
        !          2140:                for (i = 0; i < (int)pp->pb_count; ++i)
        !          2141:                    line_count_left += pp->pb_pointer[i].pe_line_count;
        !          2142:
        !          2143:                bnum_left = hp->bh_bnum;
        !          2144:                bnum_right = hp_new->bh_bnum;
        !          2145:                page_count_left = 1;
        !          2146:                page_count_right = 1;
        !          2147:                mf_put(mfp, hp, TRUE, FALSE);
        !          2148:                mf_put(mfp, hp_new, TRUE, FALSE);
        !          2149:            }
        !          2150:        }
        !          2151:        EMSG("Updated too many blocks?");
        !          2152:        buf->b_ml.ml_stack_top = 0;     /* invalidate stack */
        !          2153:    }
        !          2154:    return OK;
        !          2155: }
        !          2156:
        !          2157: /*
        !          2158:  * replace line lnum, with buffering, in current buffer
        !          2159:  *
        !          2160:  * If copy is TRUE, make a copy of the line, otherwise the line has been
        !          2161:  * copied to allocated memory already.
        !          2162:  *
        !          2163:  * return FAIL for failure, OK otherwise
        !          2164:  */
        !          2165:    int
        !          2166: ml_replace(lnum, line, copy)
        !          2167:    linenr_t    lnum;
        !          2168:    char_u      *line;
        !          2169:    int         copy;
        !          2170: {
        !          2171:    if (line == NULL)           /* just checking... */
        !          2172:        return FAIL;
        !          2173:
        !          2174:    if (curbuf->b_ml.ml_line_lnum != lnum)          /* other line buffered */
        !          2175:        ml_flush_line(curbuf);                      /* flush it */
        !          2176:    else if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) /* same line allocated */
        !          2177:        vim_free(curbuf->b_ml.ml_line_ptr);         /* free it */
        !          2178:    if (copy && (line = strsave(line)) == NULL)     /* allocate memory */
        !          2179:        return FAIL;
        !          2180:    curbuf->b_ml.ml_line_ptr = line;
        !          2181:    curbuf->b_ml.ml_line_lnum = lnum;
        !          2182:    curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
        !          2183:
        !          2184:    return OK;
        !          2185: }
        !          2186:
        !          2187: /*
        !          2188:  * delete line 'lnum'
        !          2189:  *
        !          2190:  * return FAIL for failure, OK otherwise
        !          2191:  */
        !          2192:    int
        !          2193: ml_delete(lnum, message)
        !          2194:    linenr_t    lnum;
        !          2195:    int         message;
        !          2196: {
        !          2197:    ml_flush_line(curbuf);
        !          2198:    return ml_delete_int(curbuf, lnum, message);
        !          2199: }
        !          2200:
        !          2201:    static int
        !          2202: ml_delete_int(buf, lnum, message)
        !          2203:    BUF         *buf;
        !          2204:    linenr_t    lnum;
        !          2205:    int         message;
        !          2206: {
        !          2207:    BHDR    *hp;
        !          2208:    MEMFILE *mfp;
        !          2209:    DATA_BL *dp;
        !          2210:    PTR_BL  *pp;
        !          2211:    IPTR    *ip;
        !          2212:    int     count;          /* number of entries in block */
        !          2213:    int     idx;
        !          2214:    int     stack_idx;
        !          2215:    int     text_start;
        !          2216:    int     line_start;
        !          2217:    int     line_size;
        !          2218:    int     i;
        !          2219:
        !          2220:    if (lnum < 1 || lnum > buf->b_ml.ml_line_count)
        !          2221:        return FAIL;
        !          2222:
        !          2223:    if (lowest_marked && lowest_marked > lnum)
        !          2224:        lowest_marked--;
        !          2225:
        !          2226: /*
        !          2227:  * If the file becomes empty the last line is replaced by an empty line.
        !          2228:  */
        !          2229:    if (buf->b_ml.ml_line_count == 1)       /* file becomes empty */
        !          2230:    {
        !          2231:        if (message)
        !          2232:            keep_msg = no_lines_msg;
        !          2233:        i = ml_replace((linenr_t)1, (char_u *)"", TRUE);
        !          2234:        buf->b_ml.ml_flags |= ML_EMPTY;
        !          2235:        return i;
        !          2236:    }
        !          2237:
        !          2238: /*
        !          2239:  * find the data block containing the line
        !          2240:  * This also fills the stack with the blocks from the root to the data block
        !          2241:  * This also releases any locked block.
        !          2242:  */
        !          2243:    mfp = buf->b_ml.ml_mfp;
        !          2244:    if (mfp == NULL)
        !          2245:        return FAIL;
        !          2246:
        !          2247:    if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
        !          2248:        return FAIL;
        !          2249:
        !          2250:    dp = (DATA_BL *)(hp->bh_data);
        !          2251:            /* compute line count before the delete */
        !          2252:    count = (long)(buf->b_ml.ml_locked_high) - (long)(buf->b_ml.ml_locked_low) + 2;
        !          2253:    idx = lnum - buf->b_ml.ml_locked_low;
        !          2254:
        !          2255:    --buf->b_ml.ml_line_count;
        !          2256:
        !          2257: /*
        !          2258:  * special case: If there is only one line in the data block it becomes empty.
        !          2259:  * Then we have to remove the entry, pointing to this data block, from the
        !          2260:  * pointer block. If this pointer block also becomes empty, we go up another
        !          2261:  * block, and so on, up to the root if necessary.
        !          2262:  * The line counts in the pointer blocks have already been adjusted by
        !          2263:  * ml_find_line().
        !          2264:  */
        !          2265:    if (count == 1)
        !          2266:    {
        !          2267:        mf_free(mfp, hp);       /* free the data block */
        !          2268:        buf->b_ml.ml_locked = NULL;
        !          2269:
        !          2270:        for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0; --stack_idx)
        !          2271:        {
        !          2272:            buf->b_ml.ml_stack_top = 0;     /* stack is invalid when failing */
        !          2273:            ip = &(buf->b_ml.ml_stack[stack_idx]);
        !          2274:            idx = ip->ip_index;
        !          2275:            if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
        !          2276:                return FAIL;
        !          2277:            pp = (PTR_BL *)(hp->bh_data);   /* must be pointer block */
        !          2278:            if (pp->pb_id != PTR_ID)
        !          2279:            {
        !          2280:                EMSG("pointer block id wrong 4");
        !          2281:                mf_put(mfp, hp, FALSE, FALSE);
        !          2282:                return FAIL;
        !          2283:            }
        !          2284:            count = --(pp->pb_count);
        !          2285:            if (count == 0)         /* the pointer block becomes empty! */
        !          2286:                mf_free(mfp, hp);
        !          2287:            else
        !          2288:            {
        !          2289:                if (count != idx)       /* move entries after the deleted one */
        !          2290:                    vim_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
        !          2291:                                      (size_t)(count - idx) * sizeof(PTR_EN));
        !          2292:                mf_put(mfp, hp, TRUE, FALSE);
        !          2293:
        !          2294:                buf->b_ml.ml_stack_top = stack_idx;     /* truncate stack */
        !          2295:                    /* fix line count for rest of blocks in the stack */
        !          2296:                if (buf->b_ml.ml_locked_lineadd)
        !          2297:                {
        !          2298:                    ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
        !          2299:                    buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
        !          2300:                                                buf->b_ml.ml_locked_lineadd;
        !          2301:                }
        !          2302:                ++(buf->b_ml.ml_stack_top);
        !          2303:
        !          2304:                return OK;
        !          2305:            }
        !          2306:        }
        !          2307:        CHECK(1, "deleted block 1?");
        !          2308:
        !          2309:        return OK;
        !          2310:    }
        !          2311:
        !          2312:    /*
        !          2313:     * delete the text by moving the next lines forwards
        !          2314:     */
        !          2315:    text_start = dp->db_txt_start;
        !          2316:    line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
        !          2317:    if (idx == 0)               /* first line in block, text at the end */
        !          2318:        line_size = dp->db_txt_end - line_start;
        !          2319:    else
        !          2320:        line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
        !          2321:    vim_memmove((char *)dp + text_start + line_size, (char *)dp + text_start,
        !          2322:                                           (size_t)(line_start - text_start));
        !          2323:
        !          2324:    /*
        !          2325:     * delete the index by moving the next indexes backwards
        !          2326:     * Adjust the indexes for the text movement.
        !          2327:     */
        !          2328:    for (i = idx; i < count - 1; ++i)
        !          2329:        dp->db_index[i] = dp->db_index[i + 1] + line_size;
        !          2330:
        !          2331:    dp->db_free += line_size + INDEX_SIZE;
        !          2332:    dp->db_txt_start += line_size;
        !          2333:    --(dp->db_line_count);
        !          2334:
        !          2335:    /*
        !          2336:     * mark the block dirty and make sure it is in the file (for recovery)
        !          2337:     */
        !          2338:    buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
        !          2339:
        !          2340:    return OK;
        !          2341: }
        !          2342:
        !          2343: /*
        !          2344:  * set the B_MARKED flag for line 'lnum'
        !          2345:  */
        !          2346:    void
        !          2347: ml_setmarked(lnum)
        !          2348:    linenr_t lnum;
        !          2349: {
        !          2350:    BHDR    *hp;
        !          2351:    DATA_BL *dp;
        !          2352:                                    /* invalid line number */
        !          2353:    if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count ||
        !          2354:                                                curbuf->b_ml.ml_mfp == NULL)
        !          2355:        return;                     /* give error message? */
        !          2356:
        !          2357:    if (lowest_marked == 0 || lowest_marked > lnum)
        !          2358:        lowest_marked = lnum;
        !          2359:
        !          2360:    /*
        !          2361:     * find the data block containing the line
        !          2362:     * This also fills the stack with the blocks from the root to the data block
        !          2363:     * This also releases any locked block.
        !          2364:     */
        !          2365:    if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
        !          2366:        return;             /* give error message? */
        !          2367:
        !          2368:    dp = (DATA_BL *)(hp->bh_data);
        !          2369:    dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
        !          2370:    curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
        !          2371: }
        !          2372:
        !          2373: /*
        !          2374:  * find the first line with its B_MARKED flag set
        !          2375:  */
        !          2376:    linenr_t
        !          2377: ml_firstmarked()
        !          2378: {
        !          2379:    BHDR        *hp;
        !          2380:    DATA_BL     *dp;
        !          2381:    linenr_t    lnum;
        !          2382:    int         i;
        !          2383:
        !          2384:    if (curbuf->b_ml.ml_mfp == NULL)
        !          2385:        return (linenr_t) 0;
        !          2386:
        !          2387:    /*
        !          2388:     * The search starts with lowest_marked line. This is the last line where
        !          2389:     * a mark was found, adjusted by inserting/deleting lines.
        !          2390:     */
        !          2391:    for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
        !          2392:    {
        !          2393:        /*
        !          2394:         * Find the data block containing the line.
        !          2395:         * This also fills the stack with the blocks from the root to the data
        !          2396:         * block This also releases any locked block.
        !          2397:         */
        !          2398:        if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
        !          2399:            return (linenr_t)0;             /* give error message? */
        !          2400:
        !          2401:        dp = (DATA_BL *)(hp->bh_data);
        !          2402:
        !          2403:        for (i = lnum - curbuf->b_ml.ml_locked_low;
        !          2404:                            lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
        !          2405:            if ((dp->db_index[i]) & DB_MARKED)
        !          2406:            {
        !          2407:                (dp->db_index[i]) &= DB_INDEX_MASK;
        !          2408:                curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
        !          2409:                lowest_marked = lnum + 1;
        !          2410:                return lnum;
        !          2411:            }
        !          2412:    }
        !          2413:
        !          2414:    return (linenr_t) 0;
        !          2415: }
        !          2416:
        !          2417: /*
        !          2418:  * return TRUE if line 'lnum' has a mark
        !          2419:  */
        !          2420:    int
        !          2421: ml_has_mark(lnum)
        !          2422:    linenr_t    lnum;
        !          2423: {
        !          2424:    BHDR        *hp;
        !          2425:    DATA_BL     *dp;
        !          2426:
        !          2427:    if (curbuf->b_ml.ml_mfp == NULL ||
        !          2428:                        (hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
        !          2429:        return FALSE;
        !          2430:
        !          2431:    dp = (DATA_BL *)(hp->bh_data);
        !          2432:    return (int)((dp->db_index[lnum - curbuf->b_ml.ml_locked_low]) & DB_MARKED);
        !          2433: }
        !          2434:
        !          2435: /*
        !          2436:  * clear all DB_MARKED flags
        !          2437:  */
        !          2438:    void
        !          2439: ml_clearmarked()
        !          2440: {
        !          2441:    BHDR        *hp;
        !          2442:    DATA_BL     *dp;
        !          2443:    linenr_t    lnum;
        !          2444:    int         i;
        !          2445:
        !          2446:    if (curbuf->b_ml.ml_mfp == NULL)        /* nothing to do */
        !          2447:        return;
        !          2448:
        !          2449:    /*
        !          2450:     * The search starts with line lowest_marked.
        !          2451:     */
        !          2452:    for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
        !          2453:    {
        !          2454:        /*
        !          2455:         * Find the data block containing the line.
        !          2456:         * This also fills the stack with the blocks from the root to the data block
        !          2457:         * This also releases any locked block.
        !          2458:         */
        !          2459:        if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
        !          2460:            return;             /* give error message? */
        !          2461:
        !          2462:        dp = (DATA_BL *)(hp->bh_data);
        !          2463:
        !          2464:        for (i = lnum - curbuf->b_ml.ml_locked_low;
        !          2465:                            lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
        !          2466:            if ((dp->db_index[i]) & DB_MARKED)
        !          2467:            {
        !          2468:                (dp->db_index[i]) &= DB_INDEX_MASK;
        !          2469:                curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
        !          2470:            }
        !          2471:    }
        !          2472:
        !          2473:    lowest_marked = 0;
        !          2474:    return;
        !          2475: }
        !          2476:
        !          2477: /*
        !          2478:  * flush ml_line if necessary
        !          2479:  */
        !          2480:    static void
        !          2481: ml_flush_line(buf)
        !          2482:    BUF     *buf;
        !          2483: {
        !          2484:    BHDR        *hp;
        !          2485:    DATA_BL     *dp;
        !          2486:    linenr_t    lnum;
        !          2487:    char_u      *new_line;
        !          2488:    char_u      *old_line;
        !          2489:    colnr_t     new_len;
        !          2490:    int         old_len;
        !          2491:    int         extra;
        !          2492:    int         idx;
        !          2493:    int         start;
        !          2494:    int         count;
        !          2495:    int         i;
        !          2496:
        !          2497:    if (buf->b_ml.ml_line_lnum == 0 ||
        !          2498:                        buf->b_ml.ml_mfp == NULL)       /* nothing to do */
        !          2499:        return;
        !          2500:
        !          2501:    if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
        !          2502:    {
        !          2503:        lnum = buf->b_ml.ml_line_lnum;
        !          2504:        new_line = buf->b_ml.ml_line_ptr;
        !          2505:
        !          2506:        hp = ml_find_line(buf, lnum, ML_FIND);
        !          2507:        if (hp == NULL)
        !          2508:            EMSGN("Cannot find line %ld", lnum);
        !          2509:        else
        !          2510:        {
        !          2511:            dp = (DATA_BL *)(hp->bh_data);
        !          2512:            idx = lnum - buf->b_ml.ml_locked_low;
        !          2513:            start = ((dp->db_index[idx]) & DB_INDEX_MASK);
        !          2514:            old_line = (char_u *)dp + start;
        !          2515:            if (idx == 0)       /* line is last in block */
        !          2516:                old_len = dp->db_txt_end - start;
        !          2517:            else                /* text of previous line follows */
        !          2518:                old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
        !          2519:            new_len = STRLEN(new_line) + 1;
        !          2520:            extra = new_len - old_len;      /* negative if lines gets smaller */
        !          2521:
        !          2522:            /*
        !          2523:             * if new line fits in data block, replace directly
        !          2524:             */
        !          2525:            if ((int)dp->db_free >= extra)
        !          2526:            {
        !          2527:                    /* if the length changes and there are following lines */
        !          2528:                count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
        !          2529:                if (extra != 0 && idx < count - 1)
        !          2530:                {
        !          2531:                        /* move text of following lines */
        !          2532:                    vim_memmove((char *)dp + dp->db_txt_start - extra,
        !          2533:                                (char *)dp + dp->db_txt_start,
        !          2534:                                (size_t)(start - dp->db_txt_start));
        !          2535:
        !          2536:                        /* adjust pointers of this and following lines */
        !          2537:                    for (i = idx + 1; i < count; ++i)
        !          2538:                        dp->db_index[i] -= extra;
        !          2539:                }
        !          2540:                dp->db_index[idx] -= extra;
        !          2541:
        !          2542:                    /* adjust free space */
        !          2543:                dp->db_free -= extra;
        !          2544:                dp->db_txt_start -= extra;
        !          2545:
        !          2546:                    /* copy new line into the data block */
        !          2547:                vim_memmove(old_line - extra, new_line, (size_t)new_len);
        !          2548:                buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
        !          2549:            }
        !          2550:            else
        !          2551:            {
        !          2552:                /*
        !          2553:                 * Cannot do it in one data block: delete and append.
        !          2554:                 */
        !          2555:                    /* How about handling errors??? */
        !          2556:                (void)ml_delete_int(buf, lnum, FALSE);
        !          2557:                (void)ml_append_int(buf, lnum - 1, new_line, new_len, FALSE);
        !          2558:            }
        !          2559:        }
        !          2560:        vim_free(new_line);
        !          2561:    }
        !          2562:
        !          2563:    buf->b_ml.ml_line_lnum = 0;
        !          2564: }
        !          2565:
        !          2566: /*
        !          2567:  * create a new, empty, data block
        !          2568:  */
        !          2569:    static BHDR *
        !          2570: ml_new_data(mfp, negative, page_count)
        !          2571:    MEMFILE     *mfp;
        !          2572:    int         negative;
        !          2573:    int         page_count;
        !          2574: {
        !          2575:    BHDR        *hp;
        !          2576:    DATA_BL     *dp;
        !          2577:
        !          2578:    if ((hp = mf_new(mfp, negative, page_count)) == NULL)
        !          2579:        return NULL;
        !          2580:
        !          2581:    dp = (DATA_BL *)(hp->bh_data);
        !          2582:    dp->db_id = DATA_ID;
        !          2583:    dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
        !          2584:    dp->db_free = dp->db_txt_start - HEADER_SIZE;
        !          2585:    dp->db_line_count = 0;
        !          2586:
        !          2587:    return hp;
        !          2588: }
        !          2589:
        !          2590: /*
        !          2591:  * create a new, empty, pointer block
        !          2592:  */
        !          2593:    static BHDR *
        !          2594: ml_new_ptr(mfp)
        !          2595:    MEMFILE     *mfp;
        !          2596: {
        !          2597:    BHDR        *hp;
        !          2598:    PTR_BL      *pp;
        !          2599:
        !          2600:    if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
        !          2601:        return NULL;
        !          2602:
        !          2603:    pp = (PTR_BL *)(hp->bh_data);
        !          2604:    pp->pb_id = PTR_ID;
        !          2605:    pp->pb_count = 0;
        !          2606:    pp->pb_count_max = (mfp->mf_page_size - sizeof(PTR_BL)) / sizeof(PTR_EN) + 1;
        !          2607:
        !          2608:    return hp;
        !          2609: }
        !          2610:
        !          2611: /*
        !          2612:  * lookup line 'lnum' in a memline
        !          2613:  *
        !          2614:  *   action: if ML_DELETE or ML_INSERT the line count is updated while searching
        !          2615:  *          if ML_FLUSH only flush a locked block
        !          2616:  *          if ML_FIND just find the line
        !          2617:  *
        !          2618:  * If the block was found it is locked and put in ml_locked.
        !          2619:  * The stack is updated to lead to the locked block. The ip_high field in
        !          2620:  * the stack is updated to reflect the last line in the block AFTER the
        !          2621:  * insert or delete, also if the pointer block has not been updated yet. But
        !          2622:  * if if ml_locked != NULL ml_locked_lineadd must be added to ip_high.
        !          2623:  *
        !          2624:  * return: NULL for failure, pointer to block header otherwise
        !          2625:  */
        !          2626:    static BHDR *
        !          2627: ml_find_line(buf, lnum, action)
        !          2628:    BUF         *buf;
        !          2629:    linenr_t    lnum;
        !          2630:    int         action;
        !          2631: {
        !          2632:    DATA_BL     *dp;
        !          2633:    PTR_BL      *pp;
        !          2634:    IPTR        *ip;
        !          2635:    BHDR        *hp;
        !          2636:    MEMFILE     *mfp;
        !          2637:    linenr_t    t;
        !          2638:    blocknr_t   bnum, bnum2;
        !          2639:    int         dirty;
        !          2640:    linenr_t    low, high;
        !          2641:    int         top;
        !          2642:    int         page_count;
        !          2643:    int         idx;
        !          2644:
        !          2645:    mfp = buf->b_ml.ml_mfp;
        !          2646:
        !          2647:    /*
        !          2648:     * If there is a locked block check if the wanted line is in it.
        !          2649:     * If not, flush and release the locked block.
        !          2650:     * Don't do this for ML_INSERT_SAME, because the stack need to be updated.
        !          2651:     * Don't do this for ML_FLUSH, because we want to flush the locked block.
        !          2652:     */
        !          2653:    if (buf->b_ml.ml_locked)
        !          2654:    {
        !          2655:        if (ML_SIMPLE(action) && buf->b_ml.ml_locked_low <= lnum &&
        !          2656:                                    buf->b_ml.ml_locked_high >= lnum)
        !          2657:        {
        !          2658:                /* remember to update pointer blocks and stack later */
        !          2659:            if (action == ML_INSERT)
        !          2660:            {
        !          2661:                ++(buf->b_ml.ml_locked_lineadd);
        !          2662:                ++(buf->b_ml.ml_locked_high);
        !          2663:            }
        !          2664:            else if (action == ML_DELETE)
        !          2665:            {
        !          2666:                --(buf->b_ml.ml_locked_lineadd);
        !          2667:                --(buf->b_ml.ml_locked_high);
        !          2668:            }
        !          2669:            return (buf->b_ml.ml_locked);
        !          2670:        }
        !          2671:
        !          2672:        mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
        !          2673:                                            buf->b_ml.ml_flags & ML_LOCKED_POS);
        !          2674:        buf->b_ml.ml_locked = NULL;
        !          2675:
        !          2676:            /*
        !          2677:             * if lines have been added or deleted in the locked block, need to
        !          2678:             * update the line count in pointer blocks
        !          2679:             */
        !          2680:        if (buf->b_ml.ml_locked_lineadd)
        !          2681:            ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
        !          2682:    }
        !          2683:
        !          2684:    if (action == ML_FLUSH)         /* nothing else to do */
        !          2685:        return NULL;
        !          2686:
        !          2687:    bnum = 1;                       /* start at the root of the tree */
        !          2688:    page_count = 1;
        !          2689:    low = 1;
        !          2690:    high = buf->b_ml.ml_line_count;
        !          2691:
        !          2692:    if (action == ML_FIND)      /* first try stack entries */
        !          2693:    {
        !          2694:        for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
        !          2695:        {
        !          2696:            ip = &(buf->b_ml.ml_stack[top]);
        !          2697:            if (ip->ip_low <= lnum && ip->ip_high >= lnum)
        !          2698:            {
        !          2699:                bnum = ip->ip_bnum;
        !          2700:                low = ip->ip_low;
        !          2701:                high = ip->ip_high;
        !          2702:                buf->b_ml.ml_stack_top = top;   /* truncate stack at prev entry */
        !          2703:                break;
        !          2704:            }
        !          2705:        }
        !          2706:        if (top < 0)
        !          2707:            buf->b_ml.ml_stack_top = 0;         /* not found, start at the root */
        !          2708:    }
        !          2709:    else        /* ML_DELETE or ML_INSERT */
        !          2710:        buf->b_ml.ml_stack_top = 0;     /* start at the root */
        !          2711:
        !          2712: /*
        !          2713:  * search downwards in the tree until a data block is found
        !          2714:  */
        !          2715:    for (;;)
        !          2716:    {
        !          2717:        if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
        !          2718:            goto error_noblock;
        !          2719:
        !          2720:        /*
        !          2721:         * update high for insert/delete
        !          2722:         */
        !          2723:        if (action == ML_INSERT)
        !          2724:            ++high;
        !          2725:        else if (action == ML_DELETE)
        !          2726:            --high;
        !          2727:
        !          2728:        dp = (DATA_BL *)(hp->bh_data);
        !          2729:        if (dp->db_id == DATA_ID)       /* data block */
        !          2730:        {
        !          2731:            buf->b_ml.ml_locked = hp;
        !          2732:            buf->b_ml.ml_locked_low = low;
        !          2733:            buf->b_ml.ml_locked_high = high;
        !          2734:            buf->b_ml.ml_locked_lineadd = 0;
        !          2735:            buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
        !          2736:            return hp;
        !          2737:        }
        !          2738:
        !          2739:        pp = (PTR_BL *)(dp);            /* must be pointer block */
        !          2740:        if (pp->pb_id != PTR_ID)
        !          2741:        {
        !          2742:            EMSG("pointer block id wrong");
        !          2743:            goto error_block;
        !          2744:        }
        !          2745:
        !          2746:        if ((top = ml_add_stack(buf)) < 0)      /* add new entry to stack */
        !          2747:            goto error_block;
        !          2748:        ip = &(buf->b_ml.ml_stack[top]);
        !          2749:        ip->ip_bnum = bnum;
        !          2750:        ip->ip_low = low;
        !          2751:        ip->ip_high = high;
        !          2752:        ip->ip_index = -1;              /* index not known yet */
        !          2753:
        !          2754:        dirty = FALSE;
        !          2755:        for (idx = 0; idx < (int)pp->pb_count; ++idx)
        !          2756:        {
        !          2757:            t = pp->pb_pointer[idx].pe_line_count;
        !          2758:            CHECK(t == 0, "pe_line_count is zero");
        !          2759:            if ((low += t) > lnum)
        !          2760:            {
        !          2761:                ip->ip_index = idx;
        !          2762:                bnum = pp->pb_pointer[idx].pe_bnum;
        !          2763:                page_count = pp->pb_pointer[idx].pe_page_count;
        !          2764:                high = low - 1;
        !          2765:                low -= t;
        !          2766:
        !          2767:                /*
        !          2768:                 * a negative block number may have been changed
        !          2769:                 */
        !          2770:                if (bnum < 0)
        !          2771:                {
        !          2772:                    bnum2 = mf_trans_del(mfp, bnum);
        !          2773:                    if (bnum != bnum2)
        !          2774:                    {
        !          2775:                        bnum = bnum2;
        !          2776:                        pp->pb_pointer[idx].pe_bnum = bnum;
        !          2777:                        dirty = TRUE;
        !          2778:                    }
        !          2779:                }
        !          2780:
        !          2781:                break;
        !          2782:            }
        !          2783:        }
        !          2784:        if (idx >= (int)pp->pb_count)       /* past the end: something wrong! */
        !          2785:        {
        !          2786:            if (lnum > buf->b_ml.ml_line_count)
        !          2787:                EMSGN("line number out of range: %ld past the end",
        !          2788:                                              lnum - buf->b_ml.ml_line_count);
        !          2789:
        !          2790:            else
        !          2791:                EMSGN("line count wrong in block %ld", bnum);
        !          2792:            goto error_block;
        !          2793:        }
        !          2794:        if (action == ML_DELETE)
        !          2795:        {
        !          2796:            pp->pb_pointer[idx].pe_line_count--;
        !          2797:            dirty = TRUE;
        !          2798:        }
        !          2799:        else if (action == ML_INSERT)
        !          2800:        {
        !          2801:            pp->pb_pointer[idx].pe_line_count++;
        !          2802:            dirty = TRUE;
        !          2803:        }
        !          2804:        mf_put(mfp, hp, dirty, FALSE);
        !          2805:    }
        !          2806:
        !          2807: error_block:
        !          2808:    mf_put(mfp, hp, FALSE, FALSE);
        !          2809: error_noblock:
        !          2810: /*
        !          2811:  * If action is ML_DELETE or ML_INSERT we have to correct the tree for
        !          2812:  * the incremented/decremented line counts, because there won't be a line
        !          2813:  * inserted/deleted after all.
        !          2814:  */
        !          2815:    if (action == ML_DELETE)
        !          2816:        ml_lineadd(buf, 1);
        !          2817:    else if (action == ML_INSERT)
        !          2818:        ml_lineadd(buf, -1);
        !          2819:    buf->b_ml.ml_stack_top = 0;
        !          2820:    return NULL;
        !          2821: }
        !          2822:
        !          2823: /*
        !          2824:  * add an entry to the info pointer stack
        !          2825:  *
        !          2826:  * return -1 for failure, number of the new entry otherwise
        !          2827:  */
        !          2828:    static int
        !          2829: ml_add_stack(buf)
        !          2830:    BUF     *buf;
        !          2831: {
        !          2832:    int     top;
        !          2833:    IPTR    *newstack;
        !          2834:
        !          2835:    top = buf->b_ml.ml_stack_top;
        !          2836:
        !          2837:        /* may have to increase the stack size */
        !          2838:    if (top == buf->b_ml.ml_stack_size)
        !          2839:    {
        !          2840:        CHECK(top > 0, "Stack size increases"); /* more than 5 levels??? */
        !          2841:
        !          2842:        newstack = (IPTR *)alloc((unsigned)sizeof(IPTR) *
        !          2843:                                        (buf->b_ml.ml_stack_size + STACK_INCR));
        !          2844:        if (newstack == NULL)
        !          2845:            return -1;
        !          2846:        vim_memmove(newstack, buf->b_ml.ml_stack, (size_t)top * sizeof(IPTR));
        !          2847:        vim_free(buf->b_ml.ml_stack);
        !          2848:        buf->b_ml.ml_stack = newstack;
        !          2849:        buf->b_ml.ml_stack_size += STACK_INCR;
        !          2850:    }
        !          2851:
        !          2852:    buf->b_ml.ml_stack_top++;
        !          2853:    return top;
        !          2854: }
        !          2855:
        !          2856: /*
        !          2857:  * Update the pointer blocks on the stack for inserted/deleted lines.
        !          2858:  * The stack itself is also updated.
        !          2859:  *
        !          2860:  * When a insert/delete line action fails, the line is not inserted/deleted,
        !          2861:  * but the pointer blocks have already been updated. That is fixed here by
        !          2862:  * walking through the stack.
        !          2863:  *
        !          2864:  * Count is the number of lines added, negative if lines have been deleted.
        !          2865:  */
        !          2866:    static void
        !          2867: ml_lineadd(buf, count)
        !          2868:    BUF         *buf;
        !          2869:    int         count;
        !          2870: {
        !          2871:    int         idx;
        !          2872:    IPTR        *ip;
        !          2873:    PTR_BL      *pp;
        !          2874:    MEMFILE     *mfp = buf->b_ml.ml_mfp;
        !          2875:    BHDR        *hp;
        !          2876:
        !          2877:    for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
        !          2878:    {
        !          2879:        ip = &(buf->b_ml.ml_stack[idx]);
        !          2880:        if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
        !          2881:            break;
        !          2882:        pp = (PTR_BL *)(hp->bh_data);   /* must be pointer block */
        !          2883:        if (pp->pb_id != PTR_ID)
        !          2884:        {
        !          2885:            mf_put(mfp, hp, FALSE, FALSE);
        !          2886:            EMSG("pointer block id wrong 2");
        !          2887:            break;
        !          2888:        }
        !          2889:        pp->pb_pointer[ip->ip_index].pe_line_count += count;
        !          2890:        ip->ip_high += count;
        !          2891:        mf_put(mfp, hp, TRUE, FALSE);
        !          2892:    }
        !          2893: }
        !          2894:
        !          2895: /*
        !          2896:  * make swap file name out of the filename and a directory name
        !          2897:  */
        !          2898:    static char_u *
        !          2899: makeswapname(buf, dir_name)
        !          2900:    BUF     *buf;
        !          2901:    char_u  *dir_name;
        !          2902: {
        !          2903:    char_u      *r, *s, *fname;
        !          2904:
        !          2905: #ifdef VMS
        !          2906:    r = modname(buf->b_xfilename, (char_u *)"_swp");
        !          2907: #else
        !          2908:    r = modname(buf->b_xfilename, (char_u *)".swp");
        !          2909: #endif
        !          2910:    /*
        !          2911:     * do not use dir_name
        !          2912:     * - if dir_name starts with '.' (use current directory)
        !          2913:     * - if out of memory
        !          2914:     */
        !          2915:    if (*dir_name == '.' || r == NULL)
        !          2916:        return r;
        !          2917:
        !          2918:    fname = gettail(r);
        !          2919:    s = concat_fnames(dir_name, fname, TRUE);
        !          2920:    vim_free(r);
        !          2921:    return s;
        !          2922: }
        !          2923:
        !          2924: /*
        !          2925:  * Find out what name to use for the swap file for buffer 'buf'.
        !          2926:  *
        !          2927:  * Several names are tried to find one that does not exist
        !          2928:  *
        !          2929:  * Note: if MAXNAMLEN is not correct, you will get error messages for
        !          2930:  *       not being able to open the swapfile
        !          2931:  */
        !          2932:    static char_u *
        !          2933: findswapname(buf, dirp, old_fname)
        !          2934:    BUF     *buf;
        !          2935:    char_u  **dirp;         /* pointer to list of directories */
        !          2936:    char_u  *old_fname;     /* don't give warning for this filename */
        !          2937: {
        !          2938:    char_u      *fname;
        !          2939:    int         n;
        !          2940:    time_t      x;
        !          2941:    char_u      *dir_name;
        !          2942:
        !          2943: #ifdef AMIGA
        !          2944:    BPTR        fh;
        !          2945: #endif
        !          2946:
        !          2947: #ifndef SHORT_FNAME
        !          2948:    int         r;
        !          2949:    FILE        *dummyfd = NULL;
        !          2950:
        !          2951: /*
        !          2952:  * If we start editing a new file, e.g. "test.doc", which resides on an MSDOS
        !          2953:  * compatible filesystem, it is possible that the file "test.doc.swp" which we
        !          2954:  * create will be exactly the same file. To avoid this problem we temporarily
        !          2955:  * create "test.doc".
        !          2956:  */
        !          2957:    if (!(buf->b_p_sn || buf->b_shortname) && buf->b_xfilename &&
        !          2958:                                                getperm(buf->b_xfilename) < 0)
        !          2959:        dummyfd = fopen((char *)buf->b_xfilename, "w");
        !          2960: #endif
        !          2961:
        !          2962: /*
        !          2963:  * Isolate a directory name from *dirp and put it in dir_name.
        !          2964:  * First allocate some memore to put the directory name in.
        !          2965:  */
        !          2966:    dir_name = alloc((unsigned)STRLEN(*dirp) + 1);
        !          2967:    if (dir_name != NULL)
        !          2968:        (void)copy_option_part(dirp, dir_name, 31000, ",");
        !          2969:
        !          2970: /*
        !          2971:  * we try different names until we find one that does not exist yet
        !          2972:  */
        !          2973:    if (dir_name == NULL)           /* out of memory */
        !          2974:        fname = NULL;
        !          2975:    else
        !          2976:        fname = makeswapname(buf, dir_name);
        !          2977:
        !          2978:    for (;;)
        !          2979:    {
        !          2980:        if (fname == NULL)      /* must be out of memory */
        !          2981:            break;
        !          2982:        if ((n = STRLEN(fname)) == 0)   /* safety check */
        !          2983:        {
        !          2984:            vim_free(fname);
        !          2985:            fname = NULL;
        !          2986:            break;
        !          2987:        }
        !          2988: #if (defined(UNIX) || defined(OS2)) && !defined(ARCHIE) && !defined(SHORT_FNAME)
        !          2989: /*
        !          2990:  * Some systems have a MS-DOS compatible filesystem that use 8.3 character
        !          2991:  * file names. If this is the first try and the swap file name does not fit in
        !          2992:  * 8.3, detect if this is the case, set shortname and try again.
        !          2993:  */
        !          2994:        if (fname[n - 1] == 'p' && !(buf->b_p_sn || buf->b_shortname))
        !          2995:        {
        !          2996:            char_u          *tail;
        !          2997:            char_u          *fname2;
        !          2998:            struct stat     s1, s2;
        !          2999:            int             f1, f2;
        !          3000:            int             created1 = FALSE, created2 = FALSE;
        !          3001:            int             same = FALSE;
        !          3002:
        !          3003:            /*
        !          3004:             * Check if swapfilename does not fit in 8.3:
        !          3005:             * It either contains two dots or it is longer than 8 chars.
        !          3006:             */
        !          3007:            tail = gettail(buf->b_xfilename);
        !          3008:            if (vim_strchr(tail, '.') != NULL || STRLEN(tail) > (size_t)8)
        !          3009:            {
        !          3010:                fname2 = alloc(n + 1);
        !          3011:                if (fname2 != NULL)
        !          3012:                {
        !          3013:                    STRCPY(fname2, fname);
        !          3014:                    if (vim_strchr(tail, '.') != NULL)
        !          3015:                        fname2[n - 1] = 'x';    /* change ".swp" to ".swx" */
        !          3016:                    else
        !          3017:                        fname2[n - 5] += 1;     /* change "x.swp" to "y.swp" */
        !          3018:                    /*
        !          3019:                     * may need to create the files to be able to use stat()
        !          3020:                     */
        !          3021:                    f1 = open((char *)fname, O_RDONLY | O_EXTRA);
        !          3022:                    if (f1 < 0)
        !          3023:                    {
        !          3024:                        f1 = open((char *)fname, O_RDWR|O_CREAT|O_EXCL|O_EXTRA
        !          3025: #ifdef AMIGA               /* Amiga has no mode argument */
        !          3026:                                                                            );
        !          3027: #endif
        !          3028: #ifdef UNIX                    /* open in rw------- mode */
        !          3029:                                                              , (mode_t)0600);
        !          3030: #endif
        !          3031: #if defined(MSDOS) || defined(WIN32) || defined(OS2)  /* open read/write */
        !          3032:                                                        , S_IREAD | S_IWRITE);
        !          3033: #endif
        !          3034: #if defined(OS2)
        !          3035:                        if (f1 < 0 && errno == ENOENT)
        !          3036:                            same = TRUE;
        !          3037: #endif
        !          3038:                        created1 = TRUE;
        !          3039:                    }
        !          3040:                    if (f1 >= 0)
        !          3041:                    {
        !          3042:                        f2 = open((char *)fname2, O_RDONLY | O_EXTRA);
        !          3043:                        if (f2 < 0)
        !          3044:                        {
        !          3045:                            f2 = open((char *)fname2,
        !          3046:                                                 O_RDWR|O_CREAT|O_EXCL|O_EXTRA
        !          3047: #ifdef AMIGA               /* Amiga has no mode argument */
        !          3048:                                                                            );
        !          3049: #endif
        !          3050: #ifdef UNIX                    /* open in rw------- mode */
        !          3051:                                                              , (mode_t)0600);
        !          3052: #endif
        !          3053: #if defined(MSDOS) || defined(WIN32) || defined(OS2)  /* open read/write */
        !          3054:                                                        , S_IREAD | S_IWRITE);
        !          3055: #endif
        !          3056:                            created2 = TRUE;
        !          3057:                        }
        !          3058:                        if (f2 >= 0)
        !          3059:                        {
        !          3060:                            /*
        !          3061:                             * Both files exist now. If stat() returns the
        !          3062:                             * same device and inode they are the same file.
        !          3063:                             */
        !          3064:                            if (fstat(f1, &s1) != -1 &&
        !          3065:                                        fstat(f2, &s2) != -1 &&
        !          3066:                                        s1.st_dev == s2.st_dev &&
        !          3067:                                        s1.st_ino == s2.st_ino)
        !          3068:                                same = TRUE;
        !          3069:                            close(f2);
        !          3070:                            if (created2)
        !          3071:                                vim_remove(fname2);
        !          3072:                        }
        !          3073:                        close(f1);
        !          3074:                        if (created1)
        !          3075:                            vim_remove(fname);
        !          3076:                    }
        !          3077:                    vim_free(fname2);
        !          3078:                    if (same)
        !          3079:                    {
        !          3080:                        buf->b_shortname = TRUE;
        !          3081:                        vim_free(fname);
        !          3082:                        fname = makeswapname(buf, dir_name);
        !          3083:                        continue;       /* try again with b_shortname set */
        !          3084:                    }
        !          3085:                }
        !          3086:            }
        !          3087:        }
        !          3088: #endif
        !          3089:        /*
        !          3090:         * check if the swapfile already exists
        !          3091:         */
        !          3092:        if (getperm(fname) < 0)     /* it does not exist */
        !          3093:        {
        !          3094: #ifdef AMIGA
        !          3095:            fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
        !          3096:            /*
        !          3097:             * on the Amiga getperm() will return -1 when the file exists but
        !          3098:             * is being used by another program. This happens if you edit
        !          3099:             * a file twice.
        !          3100:             */
        !          3101:            if (fh != (BPTR)NULL)       /* can open file, OK */
        !          3102:            {
        !          3103:                Close(fh);
        !          3104:                break;
        !          3105:            }
        !          3106:            if (IoErr() != ERROR_OBJECT_IN_USE &&
        !          3107:                                               IoErr() != ERROR_OBJECT_EXISTS)
        !          3108: #endif
        !          3109:                break;
        !          3110:        }
        !          3111:        /*
        !          3112:         * A file name equal to old_fname is OK to use.
        !          3113:         */
        !          3114:        if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
        !          3115:            break;
        !          3116:
        !          3117:        /*
        !          3118:         * get here when file already exists
        !          3119:         */
        !          3120:        if (fname[n - 1] == 'p')        /* first try */
        !          3121:        {
        !          3122: #ifndef SHORT_FNAME
        !          3123:            /*
        !          3124:             * on MS-DOS compatible filesystems (e.g. messydos) file.doc.swp
        !          3125:             * and file.doc are the same file. To guess if this problem is
        !          3126:             * present try if file.doc.swx exists. If it does, we set
        !          3127:             * buf->b_shortname and try file_doc.swp (dots replaced by
        !          3128:             * underscores for this file), and try again. If it doesn't we
        !          3129:             * assume that "file.doc.swp" already exists.
        !          3130:             */
        !          3131:            if (!(buf->b_p_sn || buf->b_shortname))     /* not tried yet */
        !          3132:            {
        !          3133:                fname[n - 1] = 'x';
        !          3134:                r = getperm(fname);         /* try "file.swx" */
        !          3135:                fname[n - 1] = 'p';
        !          3136:                if (r >= 0)                 /* "file.swx" seems to exist */
        !          3137:                {
        !          3138:                    buf->b_shortname = TRUE;
        !          3139:                    vim_free(fname);
        !          3140:                    fname = makeswapname(buf, dir_name);
        !          3141:                    continue;       /* try again with '.' replaced by '_' */
        !          3142:                }
        !          3143:            }
        !          3144: #endif
        !          3145:            /*
        !          3146:             * If we get here the ".swp" file really exists.
        !          3147:             * Give an error message, unless recovering, no file name, we are
        !          3148:             * viewing a help file or when the path of the file is different
        !          3149:             * (happens when all .swp files are in one directory).
        !          3150:             */
        !          3151:            if (!recoverymode && buf->b_xfilename != NULL && !buf->b_help)
        !          3152:            {
        !          3153:                int             fd;
        !          3154:                struct block0   b0;
        !          3155:                int             differ = FALSE;
        !          3156:
        !          3157:                /*
        !          3158:                 * Try to read block 0 from the swap file to get the original
        !          3159:                 * file name (and inode number).
        !          3160:                 */
        !          3161:                fd = open((char *)fname, O_RDONLY | O_EXTRA);
        !          3162:                if (fd >= 0)
        !          3163:                {
        !          3164:                    if (read(fd, (char *)&b0, sizeof(b0)) == sizeof(b0))
        !          3165:                    {
        !          3166:                        /*
        !          3167:                         * The name in the swap file may be "~user/path/file".
        !          3168:                         * Expand it first.
        !          3169:                         */
        !          3170:                        expand_env(b0.b0_fname, NameBuff, MAXPATHL);
        !          3171: #ifdef CHECK_INODE
        !          3172:                        if (fnamecmp_ino(buf->b_filename, NameBuff,
        !          3173:                                                     char_to_long(b0.b0_ino)))
        !          3174:                            differ = TRUE;
        !          3175: #else
        !          3176:                        if (fnamecmp(NameBuff, buf->b_filename) != 0)
        !          3177:                            differ = TRUE;
        !          3178: #endif
        !          3179:                    }
        !          3180:                    close(fd);
        !          3181:                }
        !          3182:                if (differ == FALSE)
        !          3183:                {
        !          3184:                    struct stat st;
        !          3185:
        !          3186:                    ++no_wait_return;
        !          3187: #ifdef SLEEP_IN_EMSG
        !          3188:                    ++dont_sleep;
        !          3189: #endif
        !          3190:                    (void)EMSG("ATTENTION");
        !          3191: #ifdef SLEEP_IN_EMSG
        !          3192:                    --dont_sleep;
        !          3193: #endif
        !          3194:                    MSG_OUTSTR("\nFound a swap file by the name \"");
        !          3195:                    msg_home_replace(fname);
        !          3196:                    MSG_OUTSTR("\"\n");
        !          3197:                    swapfile_info(fname);
        !          3198:                    MSG_OUTSTR("While opening file \"");
        !          3199:                    msg_outtrans(buf->b_xfilename);
        !          3200:                    MSG_OUTSTR("\"\n");
        !          3201:                    if (stat((char *)buf->b_xfilename, &st) != -1)
        !          3202:                    {
        !          3203:                        MSG_OUTSTR("             dated: ");
        !          3204:                        x = st.st_mtime;    /* Manx C can't do &st.st_mtime */
        !          3205:                        MSG_OUTSTR(ctime(&x));
        !          3206:                    }
        !          3207:                    MSG_OUTSTR("\n(1) Another program may be editing the same file.\n");
        !          3208:                    MSG_OUTSTR("    If this is the case, quit this edit session to avoid having\n");
        !          3209:                    MSG_OUTSTR("    two different instances of the same file when making changes.\n");
        !          3210:                    MSG_OUTSTR("\n(2) An edit session for this file crashed.\n");
        !          3211:                    MSG_OUTSTR("    If this is the case, use \":recover\" or \"vim -r ");
        !          3212:                    msg_outtrans(buf->b_xfilename);
        !          3213:                    MSG_OUTSTR("\"\n    to recover the changes (see \":help recovery)\".\n");
        !          3214:                    MSG_OUTSTR("    If you did this already, delete the swap file \"");
        !          3215:                    msg_outtrans(fname);
        !          3216:                    MSG_OUTSTR("\"\n    to avoid this message.\n\n");
        !          3217:                    cmdline_row = msg_row;
        !          3218:                    --no_wait_return;
        !          3219:                    need_wait_return = TRUE;        /* call wait_return later */
        !          3220:                }
        !          3221:            }
        !          3222:        }
        !          3223:
        !          3224:        if (fname[n - 1] == 'a')    /* tried enough names, give up */
        !          3225:        {
        !          3226:            vim_free(fname);
        !          3227:            fname = NULL;
        !          3228:            break;
        !          3229:        }
        !          3230:        --fname[n - 1];             /* change last char of the name */
        !          3231:    }
        !          3232:
        !          3233:    vim_free(dir_name);
        !          3234: #ifndef SHORT_FNAME
        !          3235:    if (dummyfd)        /* file has been created temporarily */
        !          3236:    {
        !          3237:        fclose(dummyfd);
        !          3238:        vim_remove(buf->b_xfilename);
        !          3239:    }
        !          3240: #endif
        !          3241:    return fname;
        !          3242: }
        !          3243:
        !          3244:    static int
        !          3245: b0_magic_wrong(b0p)
        !          3246:    ZERO_BL *b0p;
        !          3247: {
        !          3248:    return (b0p->b0_magic_long != (long)B0_MAGIC_LONG ||
        !          3249:            b0p->b0_magic_int != (int)B0_MAGIC_INT ||
        !          3250:            b0p->b0_magic_short != (short)B0_MAGIC_SHORT ||
        !          3251:            b0p->b0_magic_char != B0_MAGIC_CHAR);
        !          3252: }
        !          3253:
        !          3254: #ifdef CHECK_INODE
        !          3255: /*
        !          3256:  * Compare current file name with file name from swap file.
        !          3257:  * Try to use inode numbers when possible.
        !          3258:  * Return non-zero when files are different.
        !          3259:  *
        !          3260:  * When comparing file names a few things have to be taken into consideration:
        !          3261:  * - When working over a network the full path of a file depends on the host.
        !          3262:  *   We check the inode number if possible.  It is not 100% reliable though,
        !          3263:  *   because the device number cannot be used over a network.
        !          3264:  * - When a file does not exist yet (editing a new file) there is no inode
        !          3265:  *   number.
        !          3266:  * - The file name in a swap file may not be valid on the current host.  The
        !          3267:  *  "~user" form is used whenever possible to avoid this.
        !          3268:  *
        !          3269:  * This is getting complicated, let's make a table:
        !          3270:  *
        !          3271:  *             ino_c  ino_s  fname_c  fname_s  differ =
        !          3272:  *
        !          3273:  * both files exist -> compare inode numbers:
        !          3274:  *             != 0   != 0     X        X      ino_c != ino_s
        !          3275:  *
        !          3276:  * inode number(s) unknown, file names available -> compare file names
        !          3277:  *             == 0    X       OK       OK     fname_c != fname_s
        !          3278:  *              X     == 0     OK       OK     fname_c != fname_s
        !          3279:  *
        !          3280:  * current file doesn't exist, file for swap file exist, file name(s) not
        !          3281:  * available -> probably different
        !          3282:  *             == 0   != 0    FAIL      X      TRUE
        !          3283:  *             == 0   != 0     X       FAIL    TRUE
        !          3284:  *
        !          3285:  * current file exists, inode for swap unknown, file name(s) not
        !          3286:  * available -> probably different
        !          3287:  *             != 0   == 0    FAIL      X      TRUE
        !          3288:  *             != 0   == 0     X       FAIL    TRUE
        !          3289:  *
        !          3290:  * current file doesn't exist, inode for swap unknown, one file name not
        !          3291:  * available -> probably different
        !          3292:  *             == 0   == 0    FAIL      OK     TRUE
        !          3293:  *             == 0   == 0     OK      FAIL    TRUE
        !          3294:  *
        !          3295:  * current file doesn't exist, inode for swap unknown, both file names not
        !          3296:  * available -> probably same file
        !          3297:  *             == 0   == 0    FAIL     FAIL    FALSE
        !          3298:  */
        !          3299:
        !          3300:    static int
        !          3301: fnamecmp_ino(fname_c, fname_s, ino_block0)
        !          3302:    char_u      *fname_c;           /* current file name */
        !          3303:    char_u      *fname_s;           /* file name from swap file */
        !          3304:    long        ino_block0;
        !          3305: {
        !          3306:    struct stat st;
        !          3307:    long        ino_c = 0;          /* ino of current file */
        !          3308:    long        ino_s;              /* ino of file from swap file */
        !          3309:    char_u      buf_c[MAXPATHL];    /* full path of fname_c */
        !          3310:    char_u      buf_s[MAXPATHL];    /* full path of fname_s */
        !          3311:    int         retval_c;           /* flag: buf_c valid */
        !          3312:    int         retval_s;           /* flag: buf_s valid */
        !          3313:
        !          3314:
        !          3315:    if (stat((char *)fname_c, &st) == 0)
        !          3316:        ino_c = st.st_ino;
        !          3317:
        !          3318:    /*
        !          3319:     * First we try to get the inode from the file name, because the inode in
        !          3320:     * the swap file may be outdated.  If that fails (e.g. this path is not
        !          3321:     * valid on this machine), use the inode from block 0.
        !          3322:     */
        !          3323:    if (stat((char *)fname_s, &st) == 0)
        !          3324:        ino_s = st.st_ino;
        !          3325:    else
        !          3326:        ino_s = ino_block0;
        !          3327:
        !          3328:    if (ino_c && ino_s)
        !          3329:        return (ino_c != ino_s);
        !          3330:
        !          3331:    /*
        !          3332:     * One of the inode numbers is unknown, try a forced FullName() and
        !          3333:     * compare the file names.
        !          3334:     */
        !          3335:    retval_c = FullName(fname_c, buf_c, MAXPATHL, TRUE);
        !          3336:    retval_s = FullName(fname_s, buf_s, MAXPATHL, TRUE);
        !          3337:    if (retval_c == OK && retval_s == OK)
        !          3338:        return (STRCMP(buf_c, buf_s) != 0);
        !          3339:
        !          3340:    /*
        !          3341:     * Can't compare inodes or filenames, guess that the files are different,
        !          3342:     * unless both appear not to exist at all.
        !          3343:     */
        !          3344:    if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
        !          3345:        return FALSE;
        !          3346:    return TRUE;
        !          3347: }
        !          3348: #endif /* CHECK_INODE */
        !          3349:
        !          3350: /*
        !          3351:  * Move a long integer into a four byte character array.
        !          3352:  * Used for machine independency in block zero.
        !          3353:  */
        !          3354:    static void
        !          3355: long_to_char(n, s)
        !          3356:    long    n;
        !          3357:    char_u  *s;
        !          3358: {
        !          3359:    s[0] = (n & 0xff);
        !          3360:    n >>= 8;
        !          3361:    s[1] = (n & 0xff);
        !          3362:    n >>= 8;
        !          3363:    s[2] = (n & 0xff);
        !          3364:    n >>= 8;
        !          3365:    s[3] = (n & 0xff);
        !          3366: }
        !          3367:
        !          3368:    static long
        !          3369: char_to_long(s)
        !          3370:    char_u  *s;
        !          3371: {
        !          3372:    long    retval;
        !          3373:
        !          3374:    retval = s[3];
        !          3375:    retval <<= 8;
        !          3376:    retval += s[2];
        !          3377:    retval <<= 8;
        !          3378:    retval += s[1];
        !          3379:    retval <<= 8;
        !          3380:    retval += s[0];
        !          3381:
        !          3382:    return retval;
        !          3383: }