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