#define CHECK(c, s) do { } while (0)
#include "vim.h"
#ifndef UNIX // it's in os_unix.h for Unix
# include <time.h>
#endif
#if defined(SASC) || defined(__amigaos4__)
# include <proto/dos.h> // for Open() and Close()
#endif
typedef struct block0 ZERO_BL; typedef struct pointer_block PTR_BL; typedef struct data_block DATA_BL; typedef struct pointer_entry PTR_EN;
#define DATA_ID (('d' << 8) + 'a') // data block id
#define PTR_ID (('p' << 8) + 't') // pointer block id
#define BLOCK0_ID0 'b' // block 0 id 0
#define BLOCK0_ID1 '0' // block 0 id 1
#define BLOCK0_ID1_C0 'c' // block 0 id 1 'cm' 0
#define BLOCK0_ID1_C1 'C' // block 0 id 1 'cm' 1
#define BLOCK0_ID1_C2 'd' // block 0 id 1 'cm' 2
#if defined(FEAT_CRYPT)
static int id1_codes[] = {
BLOCK0_ID1_C0, BLOCK0_ID1_C1, BLOCK0_ID1_C2, };
#endif
struct pointer_entry
{
blocknr_T pe_bnum; linenr_T pe_line_count; linenr_T pe_old_lnum; int pe_page_count; };
struct pointer_block
{
short_u pb_id; short_u pb_count; short_u pb_count_max; PTR_EN pb_pointer[1]; };
struct data_block
{
short_u db_id; unsigned db_free; unsigned db_txt_start; unsigned db_txt_end; linenr_T db_line_count; unsigned db_index[1]; };
#define DB_MARKED ((unsigned)1 << ((sizeof(unsigned) * 8) - 1))
#define DB_INDEX_MASK (~DB_MARKED)
#define INDEX_SIZE (sizeof(unsigned)) // size of one db_index entry
#define HEADER_SIZE (sizeof(DATA_BL) - INDEX_SIZE) // size of data block header
#define B0_FNAME_SIZE_ORG 900 // what it was in older versions
#define B0_FNAME_SIZE_NOCRYPT 898 // 2 bytes used for other things
#define B0_FNAME_SIZE_CRYPT 890 // 10 bytes used for other things
#define B0_UNAME_SIZE 40
#define B0_HNAME_SIZE 40
#define B0_MAGIC_LONG 0x30313233L
#define B0_MAGIC_INT 0x20212223L
#define B0_MAGIC_SHORT 0x10111213L
#define B0_MAGIC_CHAR 0x55
struct block0
{
char_u b0_id[2]; char_u b0_version[10]; char_u b0_page_size[4]; char_u b0_mtime[4]; char_u b0_ino[4]; char_u b0_pid[4]; char_u b0_uname[B0_UNAME_SIZE]; char_u b0_hname[B0_HNAME_SIZE]; char_u b0_fname[B0_FNAME_SIZE_ORG]; long b0_magic_long; int b0_magic_int; short b0_magic_short; char_u b0_magic_char; };
#define B0_DIRTY 0x55
#define b0_dirty b0_fname[B0_FNAME_SIZE_ORG - 1]
#define b0_flags b0_fname[B0_FNAME_SIZE_ORG - 2]
#define b0_seed b0_fname[B0_FNAME_SIZE_ORG - 2 - MF_SEED_LEN]
#define B0_FF_MASK 3
#define B0_SAME_DIR 4
#define B0_HAS_FENC 8
#define STACK_INCR 5 // nr of entries added to ml_stack at a time
static linenr_T lowest_marked = 0;
#define ML_DELETE 0x11 // delete line
#define ML_INSERT 0x12 // insert line
#define ML_FIND 0x13 // just find the line
#define ML_FLUSH 0x02 // flush locked block
#define ML_SIMPLE(x) (x & 0x10) // DEL, INS or FIND
typedef enum {
UB_FNAME = 0 , UB_SAME_DIR , UB_CRYPT } upd_block0_T;
#ifdef FEAT_CRYPT
static void ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p);
#endif
static void ml_upd_block0(buf_T *buf, upd_block0_T what);
static void set_b0_fname(ZERO_BL *, buf_T *buf);
static void set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf);
static void add_b0_fenc(ZERO_BL *b0p, buf_T *buf);
static time_t swapfile_info(char_u *);
static int recov_file_names(char_u **, char_u *, int prepend_dot);
static char_u *findswapname(buf_T *, char_u **, char_u *);
static void ml_flush_line(buf_T *);
static bhdr_T *ml_new_data(memfile_T *, int, int);
static bhdr_T *ml_new_ptr(memfile_T *);
static bhdr_T *ml_find_line(buf_T *, linenr_T, int);
static int ml_add_stack(buf_T *);
static void ml_lineadd(buf_T *, int);
static int b0_magic_wrong(ZERO_BL *);
#ifdef CHECK_INODE
static int fnamecmp_ino(char_u *, char_u *, long);
#endif
static void long_to_char(long, char_u *);
static long char_to_long(char_u *);
#ifdef FEAT_CRYPT
static cryptstate_T *ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading);
#endif
#ifdef FEAT_BYTEOFF
static void ml_updatechunk(buf_T *buf, long line, long len, int updtype);
#endif
int
ml_open(buf_T *buf)
{
memfile_T *mfp;
bhdr_T *hp = NULL;
ZERO_BL *b0p;
PTR_BL *pp;
DATA_BL *dp;
buf->b_ml.ml_stack_size = 0; buf->b_ml.ml_stack = NULL; buf->b_ml.ml_stack_top = 0; buf->b_ml.ml_locked = NULL; buf->b_ml.ml_line_lnum = 0; #ifdef FEAT_BYTEOFF
buf->b_ml.ml_chunksize = NULL;
#endif
if (cmdmod.noswapfile)
buf->b_p_swf = FALSE;
if (p_uc && buf->b_p_swf)
buf->b_may_swap = TRUE;
else
buf->b_may_swap = FALSE;
mfp = mf_open(NULL, 0);
if (mfp == NULL)
goto error;
buf->b_ml.ml_mfp = mfp;
#ifdef FEAT_CRYPT
mfp->mf_buffer = buf;
#endif
buf->b_ml.ml_flags = ML_EMPTY;
buf->b_ml.ml_line_count = 1;
#ifdef FEAT_LINEBREAK
curwin->w_nrwidth_line_count = 0;
#endif
if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
goto error;
if (hp->bh_bnum != 0)
{
iemsg(_("E298: Didn't get block nr 0?"));
goto error;
}
b0p = (ZERO_BL *)(hp->bh_data);
b0p->b0_id[0] = BLOCK0_ID0;
b0p->b0_id[1] = BLOCK0_ID1;
b0p->b0_magic_long = (long)B0_MAGIC_LONG;
b0p->b0_magic_int = (int)B0_MAGIC_INT;
b0p->b0_magic_short = (short)B0_MAGIC_SHORT;
b0p->b0_magic_char = B0_MAGIC_CHAR;
mch_memmove(b0p->b0_version, "VIM ", 4);
STRNCPY(b0p->b0_version + 4, Version, 6);
long_to_char((long)mfp->mf_page_size, b0p->b0_page_size);
#ifdef FEAT_SPELL
if (!buf->b_spell)
#endif
{
b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
b0p->b0_flags = get_fileformat(buf) + 1;
set_b0_fname(b0p, buf);
(void)get_user_name(b0p->b0_uname, B0_UNAME_SIZE);
b0p->b0_uname[B0_UNAME_SIZE - 1] = NUL;
mch_get_host_name(b0p->b0_hname, B0_HNAME_SIZE);
b0p->b0_hname[B0_HNAME_SIZE - 1] = NUL;
long_to_char(mch_get_pid(), b0p->b0_pid);
#ifdef FEAT_CRYPT
ml_set_b0_crypt(buf, b0p);
#endif
}
mf_put(mfp, hp, TRUE, FALSE);
if (!buf->b_help && !B_SPELL(buf))
(void)mf_sync(mfp, 0);
if ((hp = ml_new_ptr(mfp)) == NULL)
goto error;
if (hp->bh_bnum != 1)
{
iemsg(_("E298: Didn't get block nr 1?"));
goto error;
}
pp = (PTR_BL *)(hp->bh_data);
pp->pb_count = 1;
pp->pb_pointer[0].pe_bnum = 2;
pp->pb_pointer[0].pe_page_count = 1;
pp->pb_pointer[0].pe_old_lnum = 1;
pp->pb_pointer[0].pe_line_count = 1; mf_put(mfp, hp, TRUE, FALSE);
if ((hp = ml_new_data(mfp, FALSE, 1)) == NULL)
goto error;
if (hp->bh_bnum != 2)
{
iemsg(_("E298: Didn't get block nr 2?"));
goto error;
}
dp = (DATA_BL *)(hp->bh_data);
dp->db_index[0] = --dp->db_txt_start; dp->db_free -= 1 + INDEX_SIZE;
dp->db_line_count = 1;
*((char_u *)dp + dp->db_txt_start) = NUL;
return OK;
error:
if (mfp != NULL)
{
if (hp)
mf_put(mfp, hp, FALSE, FALSE);
mf_close(mfp, TRUE); }
buf->b_ml.ml_mfp = NULL;
return FAIL;
}
#if defined(FEAT_CRYPT) || defined(PROTO)
static void
ml_set_mfp_crypt(buf_T *buf)
{
if (*buf->b_p_key != NUL)
{
int method_nr = crypt_get_method_nr(buf);
if (method_nr > CRYPT_M_ZIP)
{
sha2_seed(buf->b_ml.ml_mfp->mf_seed, MF_SEED_LEN, NULL, 0);
}
}
}
static void
ml_set_b0_crypt(buf_T *buf, ZERO_BL *b0p)
{
if (*buf->b_p_key == NUL)
b0p->b0_id[1] = BLOCK0_ID1;
else
{
int method_nr = crypt_get_method_nr(buf);
b0p->b0_id[1] = id1_codes[method_nr];
if (method_nr > CRYPT_M_ZIP)
{
sha2_seed(&b0p->b0_seed, MF_SEED_LEN, NULL, 0);
mch_memmove(buf->b_ml.ml_mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
}
}
}
void
ml_set_crypt_key(
buf_T *buf,
char_u *old_key,
char_u *old_cm)
{
memfile_T *mfp = buf->b_ml.ml_mfp;
bhdr_T *hp;
int page_count;
int idx;
long error;
infoptr_T *ip;
PTR_BL *pp;
DATA_BL *dp;
blocknr_T bnum;
int top;
int old_method;
if (mfp == NULL)
return; old_method = crypt_method_nr_from_name(old_cm);
{
char_u *new_key = buf->b_p_key;
char_u *new_buf_cm = buf->b_p_cm;
buf->b_p_key = old_key;
buf->b_p_cm = old_cm;
ml_preserve(buf, FALSE);
buf->b_p_key = new_key;
buf->b_p_cm = new_buf_cm;
}
mfp->mf_old_key = old_key;
mfp->mf_old_cm = old_method;
if (old_method > 0 && *old_key != NUL)
mch_memmove(mfp->mf_old_seed, mfp->mf_seed, MF_SEED_LEN);
ml_upd_block0(buf, UB_CRYPT);
if (mfp->mf_infile_count > 2)
{
ml_flush_line(buf); (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
hp = NULL;
bnum = 1; page_count = 1; idx = 0; error = 0;
buf->b_ml.ml_stack_top = 0;
VIM_CLEAR(buf->b_ml.ml_stack);
buf->b_ml.ml_stack_size = 0;
for ( ; !got_int; line_breakcheck())
{
if (hp != NULL)
mf_put(mfp, hp, FALSE, FALSE);
if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
{
if (bnum == 1)
break;
++error;
}
else
{
pp = (PTR_BL *)(hp->bh_data);
if (pp->pb_id == PTR_ID) {
if (pp->pb_count == 0)
{
++error;
}
else if (idx < (int)pp->pb_count) {
if (pp->pb_pointer[idx].pe_bnum < 0)
{
++idx;
continue;
}
if ((top = ml_add_stack(buf)) < 0)
{
++error;
break; }
ip = &(buf->b_ml.ml_stack[top]);
ip->ip_bnum = bnum;
ip->ip_index = idx;
bnum = pp->pb_pointer[idx].pe_bnum;
page_count = pp->pb_pointer[idx].pe_page_count;
idx = 0;
continue;
}
}
else {
dp = (DATA_BL *)(hp->bh_data);
if (dp->db_id != DATA_ID) ++error;
else
{
mf_put(mfp, hp, TRUE, FALSE);
hp = NULL;
}
}
}
if (buf->b_ml.ml_stack_top == 0) break;
ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
bnum = ip->ip_bnum;
idx = ip->ip_index + 1; page_count = 1;
}
if (hp != NULL)
mf_put(mfp, hp, FALSE, FALSE);
if (error > 0)
emsg(_("E843: Error while updating swap file crypt"));
}
mfp->mf_old_key = NULL;
}
#endif
void
ml_setname(buf_T *buf)
{
int success = FALSE;
memfile_T *mfp;
char_u *fname;
char_u *dirp;
#if defined(MSWIN)
char_u *p;
#endif
mfp = buf->b_ml.ml_mfp;
if (mfp->mf_fd < 0) {
if (p_uc != 0 && !cmdmod.noswapfile)
ml_open_file(buf); return;
}
dirp = p_dir;
for (;;)
{
if (*dirp == NUL) break;
fname = findswapname(buf, &dirp, mfp->mf_fname);
if (dirp == NULL) break;
if (fname == NULL) continue;
#if defined(MSWIN)
p = FullName_save(fname, FALSE);
vim_free(fname);
fname = p;
if (fname == NULL)
continue;
#endif
if (fnamecmp(fname, mfp->mf_fname) == 0)
{
vim_free(fname);
success = TRUE;
break;
}
if (mfp->mf_fd >= 0)
{
close(mfp->mf_fd);
mfp->mf_fd = -1;
}
if (vim_rename(mfp->mf_fname, fname) == 0)
{
success = TRUE;
vim_free(mfp->mf_fname);
mfp->mf_fname = fname;
vim_free(mfp->mf_ffname);
#if defined(MSWIN)
mfp->mf_ffname = NULL; #else
mf_set_ffname(mfp);
#endif
ml_upd_block0(buf, UB_SAME_DIR);
break;
}
vim_free(fname); }
if (mfp->mf_fd == -1) {
mfp->mf_fd = mch_open((char *)mfp->mf_fname, O_RDWR | O_EXTRA, 0);
if (mfp->mf_fd < 0)
{
emsg(_("E301: Oops, lost the swap file!!!"));
return;
}
#ifdef HAVE_FD_CLOEXEC
{
int fdflags = fcntl(mfp->mf_fd, F_GETFD);
if (fdflags >= 0 && (fdflags & FD_CLOEXEC) == 0)
(void)fcntl(mfp->mf_fd, F_SETFD, fdflags | FD_CLOEXEC);
}
#endif
}
if (!success)
emsg(_("E302: Could not rename swap file"));
}
void
ml_open_files(void)
{
buf_T *buf;
FOR_ALL_BUFFERS(buf)
if (!buf->b_p_ro || buf->b_changed)
ml_open_file(buf);
}
void
ml_open_file(buf_T *buf)
{
memfile_T *mfp;
char_u *fname;
char_u *dirp;
mfp = buf->b_ml.ml_mfp;
if (mfp == NULL || mfp->mf_fd >= 0 || !buf->b_p_swf || cmdmod.noswapfile)
return;
#ifdef FEAT_SPELL
if (buf->b_spell)
{
fname = vim_tempname('s', FALSE);
if (fname != NULL)
(void)mf_open_file(mfp, fname); buf->b_may_swap = FALSE;
return;
}
#endif
dirp = p_dir;
for (;;)
{
if (*dirp == NUL)
break;
fname = findswapname(buf, &dirp, NULL); if (dirp == NULL)
break; if (fname == NULL)
continue;
if (mf_open_file(mfp, fname) == OK) {
#if defined(MSWIN)
mf_fullname(mfp);
#endif
ml_upd_block0(buf, UB_SAME_DIR);
if (mf_sync(mfp, MFS_ZERO) == OK)
{
mf_set_dirty(mfp);
break;
}
mf_close_file(buf, FALSE);
}
}
if (*p_dir != NUL && mfp->mf_fname == NULL)
{
need_wait_return = TRUE; ++no_wait_return;
(void)semsg(_("E303: Unable to open swap file for \"%s\", recovery impossible"),
buf_spname(buf) != NULL ? buf_spname(buf) : buf->b_fname);
--no_wait_return;
}
buf->b_may_swap = FALSE;
}
void
check_need_swap(
int newfile) {
int old_msg_silent = msg_silent;
if (curbuf->b_may_swap && (!curbuf->b_p_ro || !newfile))
ml_open_file(curbuf);
msg_silent = old_msg_silent;
}
void
ml_close(buf_T *buf, int del_file)
{
if (buf->b_ml.ml_mfp == NULL) return;
mf_close(buf->b_ml.ml_mfp, del_file); if (buf->b_ml.ml_line_lnum != 0 && (buf->b_ml.ml_flags & ML_LINE_DIRTY))
vim_free(buf->b_ml.ml_line_ptr);
vim_free(buf->b_ml.ml_stack);
#ifdef FEAT_BYTEOFF
VIM_CLEAR(buf->b_ml.ml_chunksize);
#endif
buf->b_ml.ml_mfp = NULL;
buf->b_flags &= ~BF_RECOVERED;
}
void
ml_close_all(int del_file)
{
buf_T *buf;
FOR_ALL_BUFFERS(buf)
ml_close(buf, del_file && ((buf->b_flags & BF_PRESERVED) == 0
|| vim_strchr(p_cpo, CPO_PRESERVE) == NULL));
#ifdef FEAT_SPELL
spell_delete_wordlist(); #endif
#ifdef TEMPDIRNAMES
vim_deltempdir(); #endif
}
void
ml_close_notmod(void)
{
buf_T *buf;
FOR_ALL_BUFFERS(buf)
if (!bufIsChanged(buf))
ml_close(buf, TRUE); }
void
ml_timestamp(buf_T *buf)
{
ml_upd_block0(buf, UB_FNAME);
}
static int
ml_check_b0_id(ZERO_BL *b0p)
{
if (b0p->b0_id[0] != BLOCK0_ID0
|| (b0p->b0_id[1] != BLOCK0_ID1
&& b0p->b0_id[1] != BLOCK0_ID1_C0
&& b0p->b0_id[1] != BLOCK0_ID1_C1
&& b0p->b0_id[1] != BLOCK0_ID1_C2)
)
return FAIL;
return OK;
}
static void
ml_upd_block0(buf_T *buf, upd_block0_T what)
{
memfile_T *mfp;
bhdr_T *hp;
ZERO_BL *b0p;
mfp = buf->b_ml.ml_mfp;
if (mfp == NULL)
return;
hp = mf_get(mfp, (blocknr_T)0, 1);
if (hp == NULL)
{
#ifdef FEAT_CRYPT
if (what == UB_CRYPT)
ml_set_mfp_crypt(buf);
#endif
return;
}
b0p = (ZERO_BL *)(hp->bh_data);
if (ml_check_b0_id(b0p) == FAIL)
iemsg(_("E304: ml_upd_block0(): Didn't get block 0??"));
else
{
if (what == UB_FNAME)
set_b0_fname(b0p, buf);
#ifdef FEAT_CRYPT
else if (what == UB_CRYPT)
ml_set_b0_crypt(buf, b0p);
#endif
else set_b0_dir_flag(b0p, buf);
}
mf_put(mfp, hp, TRUE, FALSE);
}
static void
set_b0_fname(ZERO_BL *b0p, buf_T *buf)
{
stat_T st;
if (buf->b_ffname == NULL)
b0p->b0_fname[0] = NUL;
else
{
#if defined(MSWIN) || defined(AMIGA)
vim_strncpy(b0p->b0_fname, buf->b_ffname, B0_FNAME_SIZE_CRYPT - 1);
# ifdef BACKSLASH_IN_FILENAME
forward_slash(b0p->b0_fname);
# endif
#else
size_t flen, ulen;
char_u uname[B0_UNAME_SIZE];
home_replace(NULL, buf->b_ffname, b0p->b0_fname,
B0_FNAME_SIZE_CRYPT, TRUE);
if (b0p->b0_fname[0] == '~')
{
flen = STRLEN(b0p->b0_fname);
if (get_user_name(uname, B0_UNAME_SIZE) == FAIL
|| (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1)
vim_strncpy(b0p->b0_fname, buf->b_ffname,
B0_FNAME_SIZE_CRYPT - 1);
else
{
mch_memmove(b0p->b0_fname + ulen + 1, b0p->b0_fname + 1, flen);
mch_memmove(b0p->b0_fname + 1, uname, ulen);
}
}
#endif
if (mch_stat((char *)buf->b_ffname, &st) >= 0)
{
long_to_char((long)st.st_mtime, b0p->b0_mtime);
#ifdef CHECK_INODE
long_to_char((long)st.st_ino, b0p->b0_ino);
#endif
buf_store_time(buf, &st, buf->b_ffname);
buf->b_mtime_read = buf->b_mtime;
}
else
{
long_to_char(0L, b0p->b0_mtime);
#ifdef CHECK_INODE
long_to_char(0L, b0p->b0_ino);
#endif
buf->b_mtime = 0;
buf->b_mtime_read = 0;
buf->b_orig_size = 0;
buf->b_orig_mode = 0;
}
}
add_b0_fenc(b0p, curbuf);
}
static void
set_b0_dir_flag(ZERO_BL *b0p, buf_T *buf)
{
if (same_directory(buf->b_ml.ml_mfp->mf_fname, buf->b_ffname))
b0p->b0_flags |= B0_SAME_DIR;
else
b0p->b0_flags &= ~B0_SAME_DIR;
}
static void
add_b0_fenc(
ZERO_BL *b0p,
buf_T *buf)
{
int n;
int size = B0_FNAME_SIZE_NOCRYPT;
#ifdef FEAT_CRYPT
if (*buf->b_p_key != NUL)
size = B0_FNAME_SIZE_CRYPT;
#endif
n = (int)STRLEN(buf->b_p_fenc);
if ((int)STRLEN(b0p->b0_fname) + n + 1 > size)
b0p->b0_flags &= ~B0_HAS_FENC;
else
{
mch_memmove((char *)b0p->b0_fname + size - n,
(char *)buf->b_p_fenc, (size_t)n);
*(b0p->b0_fname + size - n - 1) = NUL;
b0p->b0_flags |= B0_HAS_FENC;
}
}
void
ml_recover(int checkext)
{
buf_T *buf = NULL;
memfile_T *mfp = NULL;
char_u *fname;
char_u *fname_used = NULL;
bhdr_T *hp = NULL;
ZERO_BL *b0p;
int b0_ff;
char_u *b0_fenc = NULL;
#ifdef FEAT_CRYPT
int b0_cm = -1;
#endif
PTR_BL *pp;
DATA_BL *dp;
infoptr_T *ip;
blocknr_T bnum;
int page_count;
stat_T org_stat, swp_stat;
int len;
int directly;
linenr_T lnum;
char_u *p;
int i;
long error;
int cannot_open;
linenr_T line_count;
int has_error;
int idx;
int top;
int txt_start;
off_T size;
int called_from_main;
int serious_error = TRUE;
long mtime;
int attr;
int orig_file_status = NOTDONE;
recoverymode = TRUE;
called_from_main = (curbuf->b_ml.ml_mfp == NULL);
attr = HL_ATTR(HLF_E);
fname = curbuf->b_fname;
if (fname == NULL) fname = (char_u *)"";
len = (int)STRLEN(fname);
if (checkext && len >= 4 &&
#if defined(VMS)
STRNICMP(fname + len - 4, "_s", 2)
#else
STRNICMP(fname + len - 4, ".s", 2)
#endif
== 0
&& vim_strchr((char_u *)"abcdefghijklmnopqrstuvw",
TOLOWER_ASC(fname[len - 2])) != NULL
&& ASCII_ISALPHA(fname[len - 1]))
{
directly = TRUE;
fname_used = vim_strsave(fname); }
else
{
directly = FALSE;
len = recover_names(fname, FALSE, 0, NULL);
if (len == 0) {
semsg(_("E305: No swap file found for %s"), fname);
goto theend;
}
if (len == 1) i = 1;
else {
(void)recover_names(fname, TRUE, 0, NULL);
msg_putchar('\n');
msg_puts(_("Enter number of swap file to use (0 to quit): "));
i = get_number(FALSE, NULL);
if (i < 1 || i > len)
goto theend;
}
(void)recover_names(fname, FALSE, i, &fname_used);
}
if (fname_used == NULL)
goto theend;
if (called_from_main && ml_open(curbuf) == FAIL)
getout(1);
buf = ALLOC_ONE(buf_T);
if (buf == NULL)
goto theend;
buf->b_ml.ml_stack_size = 0; buf->b_ml.ml_stack = NULL; buf->b_ml.ml_stack_top = 0; buf->b_ml.ml_line_lnum = 0; buf->b_ml.ml_locked = NULL; buf->b_ml.ml_flags = 0;
#ifdef FEAT_CRYPT
buf->b_p_key = empty_option;
buf->b_p_cm = empty_option;
#endif
p = vim_strsave(fname_used); mfp = mf_open(fname_used, O_RDONLY);
fname_used = p;
if (mfp == NULL || mfp->mf_fd < 0)
{
if (fname_used != NULL)
semsg(_("E306: Cannot open %s"), fname_used);
goto theend;
}
buf->b_ml.ml_mfp = mfp;
#ifdef FEAT_CRYPT
mfp->mf_buffer = buf;
#endif
mfp->mf_page_size = MIN_SWAP_PAGE_SIZE;
if ((hp = mf_get(mfp, (blocknr_T)0, 1)) == NULL)
{
msg_start();
msg_puts_attr(_("Unable to read block 0 from "), attr | MSG_HIST);
msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
msg_puts_attr(_("\nMaybe no changes were made or Vim did not update the swap file."),
attr | MSG_HIST);
msg_end();
goto theend;
}
b0p = (ZERO_BL *)(hp->bh_data);
if (STRNCMP(b0p->b0_version, "VIM 3.0", 7) == 0)
{
msg_start();
msg_outtrans_attr(mfp->mf_fname, MSG_HIST);
msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
MSG_HIST);
msg_puts_attr(_("Use Vim version 3.0.\n"), MSG_HIST);
msg_end();
goto theend;
}
if (ml_check_b0_id(b0p) == FAIL)
{
semsg(_("E307: %s does not look like a Vim swap file"), mfp->mf_fname);
goto theend;
}
if (b0_magic_wrong(b0p))
{
msg_start();
msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
#if defined(MSWIN)
if (STRNCMP(b0p->b0_hname, "PC ", 3) == 0)
msg_puts_attr(_(" cannot be used with this version of Vim.\n"),
attr | MSG_HIST);
else
#endif
msg_puts_attr(_(" cannot be used on this computer.\n"),
attr | MSG_HIST);
msg_puts_attr(_("The file was created on "), attr | MSG_HIST);
b0p->b0_fname[0] = NUL;
msg_puts_attr((char *)b0p->b0_hname, attr | MSG_HIST);
msg_puts_attr(_(",\nor the file has been damaged."), attr | MSG_HIST);
msg_end();
goto theend;
}
#ifdef FEAT_CRYPT
for (i = 0; i < (int)(sizeof(id1_codes) / sizeof(int)); ++i)
if (id1_codes[i] == b0p->b0_id[1])
b0_cm = i;
if (b0_cm > 0)
mch_memmove(mfp->mf_seed, &b0p->b0_seed, MF_SEED_LEN);
crypt_set_cm_option(buf, b0_cm < 0 ? 0 : b0_cm);
#else
if (b0p->b0_id[1] != BLOCK0_ID1)
{
semsg(_("E833: %s is encrypted and this version of Vim does not support encryption"), mfp->mf_fname);
goto theend;
}
#endif
if (mfp->mf_page_size != (unsigned)char_to_long(b0p->b0_page_size))
{
unsigned previous_page_size = mfp->mf_page_size;
mf_new_page_size(mfp, (unsigned)char_to_long(b0p->b0_page_size));
if (mfp->mf_page_size < previous_page_size)
{
msg_start();
msg_outtrans_attr(mfp->mf_fname, attr | MSG_HIST);
msg_puts_attr(_(" has been damaged (page size is smaller than minimum value).\n"),
attr | MSG_HIST);
msg_end();
goto theend;
}
if ((size = vim_lseek(mfp->mf_fd, (off_T)0L, SEEK_END)) <= 0)
mfp->mf_blocknr_max = 0; else
mfp->mf_blocknr_max = (blocknr_T)(size / mfp->mf_page_size);
mfp->mf_infile_count = mfp->mf_blocknr_max;
p = alloc(mfp->mf_page_size);
if (p == NULL)
goto theend;
mch_memmove(p, hp->bh_data, previous_page_size);
vim_free(hp->bh_data);
hp->bh_data = p;
b0p = (ZERO_BL *)(hp->bh_data);
}
if (directly)
{
expand_env(b0p->b0_fname, NameBuff, MAXPATHL);
if (setfname(curbuf, NameBuff, NULL, TRUE) == FAIL)
goto theend;
}
home_replace(NULL, mfp->mf_fname, NameBuff, MAXPATHL, TRUE);
smsg(_("Using swap file \"%s\""), NameBuff);
if (buf_spname(curbuf) != NULL)
vim_strncpy(NameBuff, buf_spname(curbuf), MAXPATHL - 1);
else
home_replace(NULL, curbuf->b_ffname, NameBuff, MAXPATHL, TRUE);
smsg(_("Original file \"%s\""), NameBuff);
msg_putchar('\n');
mtime = char_to_long(b0p->b0_mtime);
if (curbuf->b_ffname != NULL
&& mch_stat((char *)curbuf->b_ffname, &org_stat) != -1
&& ((mch_stat((char *)mfp->mf_fname, &swp_stat) != -1
&& org_stat.st_mtime > swp_stat.st_mtime)
|| org_stat.st_mtime != mtime))
emsg(_("E308: Warning: Original file may have been changed"));
out_flush();
b0_ff = (b0p->b0_flags & B0_FF_MASK);
if (b0p->b0_flags & B0_HAS_FENC)
{
int fnsize = B0_FNAME_SIZE_NOCRYPT;
#ifdef FEAT_CRYPT
if (b0p->b0_id[1] != BLOCK0_ID1)
fnsize = B0_FNAME_SIZE_CRYPT;
#endif
for (p = b0p->b0_fname + fnsize; p > b0p->b0_fname && p[-1] != NUL; --p)
;
b0_fenc = vim_strnsave(p, (int)(b0p->b0_fname + fnsize - p));
}
mf_put(mfp, hp, FALSE, FALSE); hp = NULL;
while (!(curbuf->b_ml.ml_flags & ML_EMPTY))
ml_delete((linenr_T)1, FALSE);
if (curbuf->b_ffname != NULL)
orig_file_status = readfile(curbuf->b_ffname, NULL, (linenr_T)0,
(linenr_T)0, (linenr_T)MAXLNUM, NULL, READ_NEW);
#ifdef FEAT_CRYPT
if (b0_cm >= 0)
{
if (*curbuf->b_p_key != NUL)
{
smsg(_("Swap file is encrypted: \"%s\""), fname_used);
msg_puts(_("\nIf you entered a new crypt key but did not write the text file,"));
msg_puts(_("\nenter the new crypt key."));
msg_puts(_("\nIf you wrote the text file after changing the crypt key press enter"));
msg_puts(_("\nto use the same key for text file and swap file"));
}
else
smsg(_(need_key_msg), fname_used);
buf->b_p_key = crypt_get_key(FALSE, FALSE);
if (buf->b_p_key == NULL)
buf->b_p_key = curbuf->b_p_key;
else if (*buf->b_p_key == NUL)
{
vim_free(buf->b_p_key);
buf->b_p_key = curbuf->b_p_key;
}
if (buf->b_p_key == NULL)
buf->b_p_key = empty_option;
}
#endif
if (b0_ff != 0)
set_fileformat(b0_ff - 1, OPT_LOCAL);
if (b0_fenc != NULL)
{
set_option_value((char_u *)"fenc", 0L, b0_fenc, OPT_LOCAL);
vim_free(b0_fenc);
}
unchanged(curbuf, TRUE, TRUE);
bnum = 1; page_count = 1; lnum = 0; line_count = 0;
idx = 0; error = 0;
buf->b_ml.ml_stack_top = 0;
buf->b_ml.ml_stack = NULL;
buf->b_ml.ml_stack_size = 0;
if (curbuf->b_ffname == NULL)
cannot_open = TRUE;
else
cannot_open = FALSE;
serious_error = FALSE;
for ( ; !got_int; line_breakcheck())
{
if (hp != NULL)
mf_put(mfp, hp, FALSE, FALSE);
if ((hp = mf_get(mfp, (blocknr_T)bnum, page_count)) == NULL)
{
if (bnum == 1)
{
semsg(_("E309: Unable to read block 1 from %s"), mfp->mf_fname);
goto theend;
}
++error;
ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"),
(colnr_T)0, TRUE);
}
else {
pp = (PTR_BL *)(hp->bh_data);
if (pp->pb_id == PTR_ID) {
if (idx == 0 && line_count != 0)
{
for (i = 0; i < (int)pp->pb_count; ++i)
line_count -= pp->pb_pointer[i].pe_line_count;
if (line_count != 0)
{
++error;
ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"),
(colnr_T)0, TRUE);
}
}
if (pp->pb_count == 0)
{
ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"),
(colnr_T)0, TRUE);
++error;
}
else if (idx < (int)pp->pb_count) {
if (pp->pb_pointer[idx].pe_bnum < 0)
{
if (!cannot_open)
{
line_count = pp->pb_pointer[idx].pe_line_count;
if (readfile(curbuf->b_ffname, NULL, lnum,
pp->pb_pointer[idx].pe_old_lnum - 1,
line_count, NULL, 0) != OK)
cannot_open = TRUE;
else
lnum += line_count;
}
if (cannot_open)
{
++error;
ml_append(lnum++, (char_u *)_("???LINES MISSING"),
(colnr_T)0, TRUE);
}
++idx; continue;
}
if ((top = ml_add_stack(buf)) < 0) {
++error;
break; }
ip = &(buf->b_ml.ml_stack[top]);
ip->ip_bnum = bnum;
ip->ip_index = idx;
bnum = pp->pb_pointer[idx].pe_bnum;
line_count = pp->pb_pointer[idx].pe_line_count;
page_count = pp->pb_pointer[idx].pe_page_count;
idx = 0;
continue;
}
}
else {
dp = (DATA_BL *)(hp->bh_data);
if (dp->db_id != DATA_ID) {
if (bnum == 1)
{
semsg(_("E310: Block 1 ID wrong (%s not a .swp file?)"),
mfp->mf_fname);
goto theend;
}
++error;
ml_append(lnum++, (char_u *)_("???BLOCK MISSING"),
(colnr_T)0, TRUE);
}
else
{
has_error = FALSE;
if (page_count * mfp->mf_page_size != dp->db_txt_end)
{
ml_append(lnum++, (char_u *)_("??? from here until ???END lines may be messed up"),
(colnr_T)0, TRUE);
++error;
has_error = TRUE;
dp->db_txt_end = page_count * mfp->mf_page_size;
}
*((char_u *)dp + dp->db_txt_end - 1) = NUL;
if (line_count != dp->db_line_count)
{
ml_append(lnum++, (char_u *)_("??? from here until ???END lines may have been inserted/deleted"),
(colnr_T)0, TRUE);
++error;
has_error = TRUE;
}
for (i = 0; i < dp->db_line_count; ++i)
{
txt_start = (dp->db_index[i] & DB_INDEX_MASK);
if (txt_start <= (int)HEADER_SIZE
|| txt_start >= (int)dp->db_txt_end)
{
p = (char_u *)"???";
++error;
}
else
p = (char_u *)dp + txt_start;
ml_append(lnum++, p, (colnr_T)0, TRUE);
}
if (has_error)
ml_append(lnum++, (char_u *)_("???END"),
(colnr_T)0, TRUE);
}
}
}
if (buf->b_ml.ml_stack_top == 0) break;
ip = &(buf->b_ml.ml_stack[--(buf->b_ml.ml_stack_top)]);
bnum = ip->ip_bnum;
idx = ip->ip_index + 1; page_count = 1;
}
if (orig_file_status != OK || curbuf->b_ml.ml_line_count != lnum * 2 + 1)
{
if (!(curbuf->b_ml.ml_line_count == 2 && *ml_get(1) == NUL))
{
changed_internal();
++CHANGEDTICK(curbuf);
}
}
else
{
for (idx = 1; idx <= lnum; ++idx)
{
p = vim_strsave(ml_get(idx));
i = STRCMP(p, ml_get(idx + lnum));
vim_free(p);
if (i != 0)
{
changed_internal();
++CHANGEDTICK(curbuf);
break;
}
}
}
while (curbuf->b_ml.ml_line_count > lnum
&& !(curbuf->b_ml.ml_flags & ML_EMPTY))
ml_delete(curbuf->b_ml.ml_line_count, FALSE);
curbuf->b_flags |= BF_RECOVERED;
recoverymode = FALSE;
if (got_int)
emsg(_("E311: Recovery Interrupted"));
else if (error)
{
++no_wait_return;
msg(">>>>>>>>>>>>>");
emsg(_("E312: Errors detected while recovering; look for lines starting with ???"));
--no_wait_return;
msg(_("See \":help E312\" for more information."));
msg(">>>>>>>>>>>>>");
}
else
{
if (curbuf->b_changed)
{
msg(_("Recovery completed. You should check if everything is OK."));
msg_puts(_("\n(You might want to write out this file under another name\n"));
msg_puts(_("and run diff with the original file to check for changes)"));
}
else
msg(_("Recovery completed. Buffer contents equals file contents."));
if (vim_strchr(p_cpo, CPO_PRESERVE) == NULL)
msg_puts(_("\nYou may want to delete the .swp file now.\n\n"));
cmdline_row = msg_row;
}
#ifdef FEAT_CRYPT
if (*buf->b_p_key != NUL && STRCMP(curbuf->b_p_key, buf->b_p_key) != 0)
{
msg_puts(_("Using crypt key from swap file for the text file.\n"));
set_option_value((char_u *)"key", 0L, buf->b_p_key, OPT_LOCAL);
}
#endif
redraw_curbuf_later(NOT_VALID);
theend:
vim_free(fname_used);
recoverymode = FALSE;
if (mfp != NULL)
{
if (hp != NULL)
mf_put(mfp, hp, FALSE, FALSE);
mf_close(mfp, (vim_strchr(p_cpo, CPO_PRESERVE) != NULL)); }
if (buf != NULL)
{
#ifdef FEAT_CRYPT
if (buf->b_p_key != curbuf->b_p_key)
free_string_option(buf->b_p_key);
free_string_option(buf->b_p_cm);
#endif
vim_free(buf->b_ml.ml_stack);
vim_free(buf);
}
if (serious_error && called_from_main)
ml_close(curbuf, TRUE);
else
{
apply_autocmds(EVENT_BUFREADPOST, NULL, curbuf->b_fname, FALSE, curbuf);
apply_autocmds(EVENT_BUFWINENTER, NULL, curbuf->b_fname, FALSE, curbuf);
}
return;
}
int
recover_names(
char_u *fname, int list, int nr, char_u **fname_out) {
int num_names;
char_u *(names[6]);
char_u *tail;
char_u *p;
int num_files;
int file_count = 0;
char_u **files;
int i;
char_u *dirp;
char_u *dir_name;
char_u *fname_res = NULL;
#ifdef HAVE_READLINK
char_u fname_buf[MAXPATHL];
#endif
if (fname != NULL)
{
#ifdef HAVE_READLINK
if (resolve_symlink(fname, fname_buf) == OK)
fname_res = fname_buf;
else
#endif
fname_res = fname;
}
if (list)
{
msg(_("Swap files found:"));
msg_putchar('\n');
}
dir_name = alloc(STRLEN(p_dir) + 1);
dirp = p_dir;
while (dir_name != NULL && *dirp)
{
(void)copy_option_part(&dirp, dir_name, 31000, ",");
if (dir_name[0] == '.' && dir_name[1] == NUL) {
if (fname == NULL)
{
#ifdef VMS
names[0] = vim_strsave((char_u *)"*_sw%");
#else
names[0] = vim_strsave((char_u *)"*.sw?");
#endif
#if defined(UNIX) || defined(MSWIN)
names[1] = vim_strsave((char_u *)".*.sw?");
names[2] = vim_strsave((char_u *)".sw?");
num_names = 3;
#else
# ifdef VMS
names[1] = vim_strsave((char_u *)".*_sw%");
num_names = 2;
# else
num_names = 1;
# endif
#endif
}
else
num_names = recov_file_names(names, fname_res, TRUE);
}
else {
if (fname == NULL)
{
#ifdef VMS
names[0] = concat_fnames(dir_name, (char_u *)"*_sw%", TRUE);
#else
names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE);
#endif
#if defined(UNIX) || defined(MSWIN)
names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE);
names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE);
num_names = 3;
#else
# ifdef VMS
names[1] = concat_fnames(dir_name, (char_u *)".*_sw%", TRUE);
num_names = 2;
# else
num_names = 1;
# endif
#endif
}
else
{
#if defined(UNIX) || defined(MSWIN)
int len = (int)STRLEN(dir_name);
p = dir_name + len;
if (after_pathsep(dir_name, p) && len > 1 && p[-1] == p[-2])
{
tail = make_percent_swname(dir_name, fname_res);
}
else
#endif
{
tail = gettail(fname_res);
tail = concat_fnames(dir_name, tail, TRUE);
}
if (tail == NULL)
num_names = 0;
else
{
num_names = recov_file_names(names, tail, FALSE);
vim_free(tail);
}
}
}
for (i = 0; i < num_names; ++i)
{
if (names[i] == NULL)
{
for (i = 0; i < num_names; ++i)
vim_free(names[i]);
num_names = 0;
}
}
if (num_names == 0)
num_files = 0;
else if (expand_wildcards(num_names, names, &num_files, &files,
EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
num_files = 0;
if (*dirp == NUL && file_count + num_files == 0 && fname != NULL)
{
stat_T st;
char_u *swapname;
swapname = modname(fname_res,
#if defined(VMS)
(char_u *)"_swp", FALSE
#else
(char_u *)".swp", TRUE
#endif
);
if (swapname != NULL)
{
if (mch_stat((char *)swapname, &st) != -1) {
files = ALLOC_ONE(char_u *);
if (files != NULL)
{
files[0] = swapname;
swapname = NULL;
num_files = 1;
}
}
vim_free(swapname);
}
}
if (curbuf->b_ml.ml_mfp != NULL
&& (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
{
for (i = 0; i < num_files; ++i)
if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME)
{
vim_free(files[i]);
if (--num_files == 0)
vim_free(files);
else
for ( ; i < num_files; ++i)
files[i] = files[i + 1];
}
}
if (nr > 0)
{
file_count += num_files;
if (nr <= file_count)
{
*fname_out = vim_strsave(
files[nr - 1 + num_files - file_count]);
dirp = (char_u *)""; }
}
else if (list)
{
if (dir_name[0] == '.' && dir_name[1] == NUL)
{
if (fname == NULL)
msg_puts(_(" In current directory:\n"));
else
msg_puts(_(" Using specified name:\n"));
}
else
{
msg_puts(_(" In directory "));
msg_home_replace(dir_name);
msg_puts(":\n");
}
if (num_files)
{
for (i = 0; i < num_files; ++i)
{
msg_outnum((long)++file_count);
msg_puts(". ");
msg_puts((char *)gettail(files[i]));
msg_putchar('\n');
(void)swapfile_info(files[i]);
}
}
else
msg_puts(_(" -- none --\n"));
out_flush();
}
else
file_count += num_files;
for (i = 0; i < num_names; ++i)
vim_free(names[i]);
if (num_files > 0)
FreeWild(num_files, files);
}
vim_free(dir_name);
return file_count;
}
#if defined(UNIX) || defined(MSWIN) || defined(PROTO)
char_u *
make_percent_swname(char_u *dir, char_u *name)
{
char_u *d = NULL, *s, *f;
f = fix_fname(name != NULL ? name : (char_u *)"");
if (f != NULL)
{
s = alloc(STRLEN(f) + 1);
if (s != NULL)
{
STRCPY(s, f);
for (d = s; *d != NUL; MB_PTR_ADV(d))
if (vim_ispathsep(*d))
*d = '%';
d = concat_fnames(dir, s, TRUE);
vim_free(s);
}
vim_free(f);
}
return d;
}
#endif
#if (defined(UNIX) || defined(VMS) || defined(MSWIN)) \
&& (defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG))
# define HAVE_PROCESS_STILL_RUNNING
static int process_still_running;
#endif
#if defined(FEAT_EVAL) || defined(PROTO)
void
get_b0_dict(char_u *fname, dict_T *d)
{
int fd;
struct block0 b0;
if ((fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
{
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
{
if (ml_check_b0_id(&b0) == FAIL)
dict_add_string(d, "error", (char_u *)"Not a swap file");
else if (b0_magic_wrong(&b0))
dict_add_string(d, "error", (char_u *)"Magic number mismatch");
else
{
dict_add_string_len(d, "version", b0.b0_version, 10);
dict_add_string_len(d, "user", b0.b0_uname, B0_UNAME_SIZE);
dict_add_string_len(d, "host", b0.b0_hname, B0_HNAME_SIZE);
dict_add_string_len(d, "fname", b0.b0_fname, B0_FNAME_SIZE_ORG);
dict_add_number(d, "pid", char_to_long(b0.b0_pid));
dict_add_number(d, "mtime", char_to_long(b0.b0_mtime));
dict_add_number(d, "dirty", b0.b0_dirty ? 1 : 0);
# ifdef CHECK_INODE
dict_add_number(d, "inode", char_to_long(b0.b0_ino));
# endif
}
}
else
dict_add_string(d, "error", (char_u *)"Cannot read file");
close(fd);
}
else
dict_add_string(d, "error", (char_u *)"Cannot open file");
}
#endif
static time_t
swapfile_info(char_u *fname)
{
stat_T st;
int fd;
struct block0 b0;
#ifdef UNIX
char_u uname[B0_UNAME_SIZE];
#endif
if (mch_stat((char *)fname, &st) != -1)
{
#ifdef UNIX
if (mch_get_uname(st.st_uid, uname, B0_UNAME_SIZE) == OK)
{
msg_puts(_(" owned by: "));
msg_outtrans(uname);
msg_puts(_(" dated: "));
}
else
#endif
msg_puts(_(" dated: "));
msg_puts(get_ctime(st.st_mtime, TRUE));
}
else
st.st_mtime = 0;
fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
if (fd >= 0)
{
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
{
if (STRNCMP(b0.b0_version, "VIM 3.0", 7) == 0)
{
msg_puts(_(" [from Vim version 3.0]"));
}
else if (ml_check_b0_id(&b0) == FAIL)
{
msg_puts(_(" [does not look like a Vim swap file]"));
}
else
{
msg_puts(_(" file name: "));
if (b0.b0_fname[0] == NUL)
msg_puts(_("[No Name]"));
else
msg_outtrans(b0.b0_fname);
msg_puts(_("\n modified: "));
msg_puts(b0.b0_dirty ? _("YES") : _("no"));
if (*(b0.b0_uname) != NUL)
{
msg_puts(_("\n user name: "));
msg_outtrans(b0.b0_uname);
}
if (*(b0.b0_hname) != NUL)
{
if (*(b0.b0_uname) != NUL)
msg_puts(_(" host name: "));
else
msg_puts(_("\n host name: "));
msg_outtrans(b0.b0_hname);
}
if (char_to_long(b0.b0_pid) != 0L)
{
msg_puts(_("\n process ID: "));
msg_outnum(char_to_long(b0.b0_pid));
#if defined(UNIX) || defined(MSWIN)
if (mch_process_running(char_to_long(b0.b0_pid)))
{
msg_puts(_(" (STILL RUNNING)"));
# ifdef HAVE_PROCESS_STILL_RUNNING
process_still_running = TRUE;
# endif
}
#endif
}
if (b0_magic_wrong(&b0))
{
#if defined(MSWIN)
if (STRNCMP(b0.b0_hname, "PC ", 3) == 0)
msg_puts(_("\n [not usable with this version of Vim]"));
else
#endif
msg_puts(_("\n [not usable on this computer]"));
}
}
}
else
msg_puts(_(" [cannot be read]"));
close(fd);
}
else
msg_puts(_(" [cannot be opened]"));
msg_putchar('\n');
return st.st_mtime;
}
static time_t
swapfile_unchanged(char_u *fname)
{
stat_T st;
int fd;
struct block0 b0;
int ret = TRUE;
#if defined(UNIX) || defined(MSWIN)
long pid;
#endif
if (mch_stat((char *)fname, &st) == -1)
return FALSE;
fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
if (fd < 0)
return FALSE;
if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0))
{
close(fd);
return FALSE;
}
if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0))
ret = FALSE;
if (b0.b0_dirty)
ret = FALSE;
#if defined(UNIX) || defined(MSWIN)
pid = char_to_long(b0.b0_pid);
if (pid == 0L || mch_process_running(pid))
ret = FALSE;
#endif
close(fd);
return ret;
}
static int
recov_file_names(char_u **names, char_u *path, int prepend_dot)
{
int num_names;
char_u *p;
int i;
# ifndef MSWIN
int shortname = curbuf->b_shortname;
curbuf->b_shortname = FALSE;
# endif
num_names = 0;
if (prepend_dot)
{
names[num_names] = modname(path, (char_u *)".sw?", TRUE);
if (names[num_names] == NULL)
goto end;
++num_names;
}
#ifdef VMS
names[num_names] = concat_fnames(path, (char_u *)"_sw%", FALSE);
#else
names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE);
#endif
if (names[num_names] == NULL)
goto end;
if (num_names >= 1) {
p = names[num_names - 1];
i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]);
if (i > 0)
p += i;
if (STRCMP(p, names[num_names]) != 0)
++num_names;
else
vim_free(names[num_names]);
}
else
++num_names;
# ifndef MSWIN
curbuf->b_shortname = TRUE;
#ifdef VMS
names[num_names] = modname(path, (char_u *)"_sw%", FALSE);
#else
names[num_names] = modname(path, (char_u *)".sw?", FALSE);
#endif
if (names[num_names] == NULL)
goto end;
p = names[num_names];
i = STRLEN(names[num_names]) - STRLEN(names[num_names - 1]);
if (i > 0)
p += i; if (STRCMP(names[num_names - 1], p) == 0)
vim_free(names[num_names]);
else
++num_names;
# endif
end:
# ifndef MSWIN
curbuf->b_shortname = shortname;
# endif
return num_names;
}
void
ml_sync_all(int check_file, int check_char)
{
buf_T *buf;
stat_T st;
FOR_ALL_BUFFERS(buf)
{
if (buf->b_ml.ml_mfp == NULL || buf->b_ml.ml_mfp->mf_fname == NULL)
continue;
ml_flush_line(buf); (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH);
if (bufIsChanged(buf) && check_file && mf_need_trans(buf->b_ml.ml_mfp)
&& buf->b_ffname != NULL)
{
if (mch_stat((char *)buf->b_ffname, &st) == -1
|| st.st_mtime != buf->b_mtime_read
|| st.st_size != buf->b_orig_size)
{
ml_preserve(buf, FALSE);
did_check_timestamps = FALSE;
need_check_timestamps = TRUE; }
}
if (buf->b_ml.ml_mfp->mf_dirty)
{
(void)mf_sync(buf->b_ml.ml_mfp, (check_char ? MFS_STOP : 0)
| (bufIsChanged(buf) ? MFS_FLUSH : 0));
if (check_char && ui_char_avail()) break;
}
}
}
void
ml_preserve(buf_T *buf, int message)
{
bhdr_T *hp;
linenr_T lnum;
memfile_T *mfp = buf->b_ml.ml_mfp;
int status;
int got_int_save = got_int;
if (mfp == NULL || mfp->mf_fname == NULL)
{
if (message)
emsg(_("E313: Cannot preserve, there is no swap file"));
return;
}
got_int = FALSE;
ml_flush_line(buf); (void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); status = mf_sync(mfp, MFS_ALL | MFS_FLUSH);
buf->b_ml.ml_stack_top = 0;
if (mf_need_trans(mfp) && !got_int)
{
lnum = 1;
while (mf_need_trans(mfp) && lnum <= buf->b_ml.ml_line_count)
{
hp = ml_find_line(buf, lnum, ML_FIND);
if (hp == NULL)
{
status = FAIL;
goto theend;
}
CHECK(buf->b_ml.ml_locked_low != lnum, "low != lnum");
lnum = buf->b_ml.ml_locked_high + 1;
}
(void)ml_find_line(buf, (linenr_T)0, ML_FLUSH); if (mf_sync(mfp, MFS_ALL | MFS_FLUSH) == FAIL)
status = FAIL;
buf->b_ml.ml_stack_top = 0; }
theend:
got_int |= got_int_save;
if (message)
{
if (status == OK)
msg(_("File preserved"));
else
emsg(_("E314: Preserve failed"));
}
}
char_u *
ml_get(linenr_T lnum)
{
return ml_get_buf(curbuf, lnum, FALSE);
}
char_u *
ml_get_pos(pos_T *pos)
{
return (ml_get_buf(curbuf, pos->lnum, FALSE) + pos->col);
}
char_u *
ml_get_curline(void)
{
return ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE);
}
char_u *
ml_get_cursor(void)
{
return (ml_get_buf(curbuf, curwin->w_cursor.lnum, FALSE) +
curwin->w_cursor.col);
}
char_u *
ml_get_buf(
buf_T *buf,
linenr_T lnum,
int will_change) {
bhdr_T *hp;
DATA_BL *dp;
static int recursive = 0;
if (lnum > buf->b_ml.ml_line_count) {
if (recursive == 0)
{
++recursive;
siemsg(_("E315: ml_get: invalid lnum: %ld"), lnum);
--recursive;
}
errorret:
STRCPY(IObuff, "???");
buf->b_ml.ml_line_len = 4;
return IObuff;
}
if (lnum <= 0) lnum = 1;
if (buf->b_ml.ml_mfp == NULL) {
buf->b_ml.ml_line_len = 1;
return (char_u *)"";
}
if (buf->b_ml.ml_line_lnum != lnum || mf_dont_release)
{
unsigned start, end;
colnr_T len;
int idx;
ml_flush_line(buf);
if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
{
if (recursive == 0)
{
++recursive;
get_trans_bufname(buf);
shorten_dir(NameBuff);
siemsg(_("E316: ml_get: cannot find line %ld in buffer %d %s"),
lnum, buf->b_fnum, NameBuff);
--recursive;
}
goto errorret;
}
dp = (DATA_BL *)(hp->bh_data);
idx = lnum - buf->b_ml.ml_locked_low;
start = ((dp->db_index[idx]) & DB_INDEX_MASK);
if (idx == 0)
end = dp->db_txt_end;
else
end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
len = end - start;
buf->b_ml.ml_line_ptr = (char_u *)dp + start;
buf->b_ml.ml_line_len = len;
buf->b_ml.ml_line_lnum = lnum;
buf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
}
if (will_change)
buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
return buf->b_ml.ml_line_ptr;
}
int
ml_line_alloced(void)
{
return (curbuf->b_ml.ml_flags & ML_LINE_DIRTY);
}
#ifdef FEAT_PROP_POPUP
static void
add_text_props_for_append(
buf_T *buf,
linenr_T lnum,
char_u **line,
int *len,
char_u **tofree)
{
int round;
int new_prop_count = 0;
int count;
int n;
char_u *props;
int new_len = 0; char_u *new_line;
textprop_T prop;
for (round = 1; round <= 2; ++round)
{
if (round == 2)
{
if (new_prop_count == 0)
return; new_len = *len + new_prop_count * sizeof(textprop_T);
new_line = alloc(new_len);
if (new_line == NULL)
return;
mch_memmove(new_line, *line, *len);
new_prop_count = 0;
}
count = get_text_props(buf, lnum, &props, FALSE);
for (n = 0; n < count; ++n)
{
mch_memmove(&prop, props + n * sizeof(textprop_T),
sizeof(textprop_T));
if (prop.tp_flags & TP_FLAG_CONT_NEXT)
{
if (round == 2)
{
prop.tp_flags |= TP_FLAG_CONT_PREV;
prop.tp_col = 1;
prop.tp_len = *len; mch_memmove(new_line + *len + new_prop_count
* sizeof(textprop_T), &prop, sizeof(textprop_T));
}
++new_prop_count;
}
}
}
*line = new_line;
*tofree = new_line;
*len = new_len;
}
#endif
static int
ml_append_int(
buf_T *buf,
linenr_T lnum, char_u *line_arg, colnr_T len_arg, int flags) {
char_u *line = line_arg;
colnr_T len = len_arg;
int i;
int line_count; int offset;
int from, to;
int space_needed; int page_size;
int page_count;
int db_idx; bhdr_T *hp;
memfile_T *mfp;
DATA_BL *dp;
PTR_BL *pp;
infoptr_T *ip;
#ifdef FEAT_PROP_POPUP
char_u *tofree = NULL;
#endif
int ret = FAIL;
if (lnum > buf->b_ml.ml_line_count || buf->b_ml.ml_mfp == NULL)
return FAIL;
if (lowest_marked && lowest_marked > lnum)
lowest_marked = lnum + 1;
if (len == 0)
len = (colnr_T)STRLEN(line) + 1;
#ifdef FEAT_PROP_POPUP
if (curbuf->b_has_textprop && lnum > 0 && !(flags & ML_APPEND_UNDO))
add_text_props_for_append(buf, lnum, &line, &len, &tofree);
#endif
space_needed = len + INDEX_SIZE;
mfp = buf->b_ml.ml_mfp;
page_size = mfp->mf_page_size;
if ((hp = ml_find_line(buf, lnum == 0 ? (linenr_T)1 : lnum,
ML_INSERT)) == NULL)
goto theend;
buf->b_ml.ml_flags &= ~ML_EMPTY;
if (lnum == 0) db_idx = -1; else
db_idx = lnum - buf->b_ml.ml_locked_low;
line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
dp = (DATA_BL *)(hp->bh_data);
if ((int)dp->db_free < space_needed && db_idx == line_count - 1
&& lnum < buf->b_ml.ml_line_count)
{
--(buf->b_ml.ml_locked_lineadd);
--(buf->b_ml.ml_locked_high);
if ((hp = ml_find_line(buf, lnum + 1, ML_INSERT)) == NULL)
goto theend;
db_idx = -1; line_count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low;
CHECK(buf->b_ml.ml_locked_low != lnum + 1, "locked_low != lnum + 1");
dp = (DATA_BL *)(hp->bh_data);
}
++buf->b_ml.ml_line_count;
if ((int)dp->db_free >= space_needed) {
dp->db_txt_start -= len;
dp->db_free -= space_needed;
++(dp->db_line_count);
if (line_count > db_idx + 1) {
if (db_idx < 0)
offset = dp->db_txt_end;
else
offset = ((dp->db_index[db_idx]) & DB_INDEX_MASK);
mch_memmove((char *)dp + dp->db_txt_start,
(char *)dp + dp->db_txt_start + len,
(size_t)(offset - (dp->db_txt_start + len)));
for (i = line_count - 1; i > db_idx; --i)
dp->db_index[i + 1] = dp->db_index[i] - len;
dp->db_index[db_idx + 1] = offset - len;
}
else
dp->db_index[db_idx + 1] = dp->db_txt_start;
mch_memmove((char *)dp + dp->db_index[db_idx + 1], line, (size_t)len);
if (flags & ML_APPEND_MARK)
dp->db_index[db_idx + 1] |= DB_MARKED;
buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
if (!(flags & ML_APPEND_NEW))
buf->b_ml.ml_flags |= ML_LOCKED_POS;
}
else {
long line_count_left, line_count_right;
int page_count_left, page_count_right;
bhdr_T *hp_left;
bhdr_T *hp_right;
bhdr_T *hp_new;
int lines_moved;
int data_moved = 0; int total_moved = 0; DATA_BL *dp_right, *dp_left;
int stack_idx;
int in_left;
int lineadd;
blocknr_T bnum_left, bnum_right;
linenr_T lnum_left, lnum_right;
int pb_idx;
PTR_BL *pp_new;
if (db_idx < 0) {
lines_moved = 0;
in_left = TRUE;
}
else {
lines_moved = line_count - db_idx - 1;
if (lines_moved == 0)
in_left = FALSE; else
{
data_moved = ((dp->db_index[db_idx]) & DB_INDEX_MASK) -
dp->db_txt_start;
total_moved = data_moved + lines_moved * INDEX_SIZE;
if ((int)dp->db_free + total_moved >= space_needed)
{
in_left = TRUE; space_needed = total_moved;
}
else
{
in_left = FALSE; space_needed += total_moved;
}
}
}
page_count = ((space_needed + HEADER_SIZE) + page_size - 1) / page_size;
if ((hp_new = ml_new_data(mfp, flags & ML_APPEND_NEW, page_count))
== NULL)
{
--(buf->b_ml.ml_locked_lineadd);
--(buf->b_ml.ml_locked_high);
goto theend;
}
if (db_idx < 0) {
hp_left = hp_new;
hp_right = hp;
line_count_left = 0;
line_count_right = line_count;
}
else {
hp_left = hp;
hp_right = hp_new;
line_count_left = line_count;
line_count_right = 0;
}
dp_right = (DATA_BL *)(hp_right->bh_data);
dp_left = (DATA_BL *)(hp_left->bh_data);
bnum_left = hp_left->bh_bnum;
bnum_right = hp_right->bh_bnum;
page_count_left = hp_left->bh_page_count;
page_count_right = hp_right->bh_page_count;
if (!in_left)
{
dp_right->db_txt_start -= len;
dp_right->db_free -= len + INDEX_SIZE;
dp_right->db_index[0] = dp_right->db_txt_start;
if (flags & ML_APPEND_MARK)
dp_right->db_index[0] |= DB_MARKED;
mch_memmove((char *)dp_right + dp_right->db_txt_start,
line, (size_t)len);
++line_count_right;
}
if (lines_moved)
{
dp_right->db_txt_start -= data_moved;
dp_right->db_free -= total_moved;
mch_memmove((char *)dp_right + dp_right->db_txt_start,
(char *)dp_left + dp_left->db_txt_start,
(size_t)data_moved);
offset = dp_right->db_txt_start - dp_left->db_txt_start;
dp_left->db_txt_start += data_moved;
dp_left->db_free += total_moved;
for (to = line_count_right, from = db_idx + 1;
from < line_count_left; ++from, ++to)
dp_right->db_index[to] = dp->db_index[from] + offset;
line_count_right += lines_moved;
line_count_left -= lines_moved;
}
if (in_left)
{
dp_left->db_txt_start -= len;
dp_left->db_free -= len + INDEX_SIZE;
dp_left->db_index[line_count_left] = dp_left->db_txt_start;
if (flags & ML_APPEND_MARK)
dp_left->db_index[line_count_left] |= DB_MARKED;
mch_memmove((char *)dp_left + dp_left->db_txt_start,
line, (size_t)len);
++line_count_left;
}
if (db_idx < 0) {
lnum_left = lnum + 1;
lnum_right = 0;
}
else {
lnum_left = 0;
if (in_left)
lnum_right = lnum + 2;
else
lnum_right = lnum + 1;
}
dp_left->db_line_count = line_count_left;
dp_right->db_line_count = line_count_right;
if (lines_moved || in_left)
buf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
if (!(flags & ML_APPEND_NEW) && db_idx >= 0 && in_left)
buf->b_ml.ml_flags |= ML_LOCKED_POS;
mf_put(mfp, hp_new, TRUE, FALSE);
lineadd = buf->b_ml.ml_locked_lineadd;
buf->b_ml.ml_locked_lineadd = 0;
ml_find_line(buf, (linenr_T)0, ML_FLUSH);
for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
--stack_idx)
{
ip = &(buf->b_ml.ml_stack[stack_idx]);
pb_idx = ip->ip_index;
if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
goto theend;
pp = (PTR_BL *)(hp->bh_data); if (pp->pb_id != PTR_ID)
{
iemsg(_("E317: pointer block id wrong 3"));
mf_put(mfp, hp, FALSE, FALSE);
goto theend;
}
if (pp->pb_count < pp->pb_count_max)
{
if (pb_idx + 1 < (int)pp->pb_count)
mch_memmove(&pp->pb_pointer[pb_idx + 2],
&pp->pb_pointer[pb_idx + 1],
(size_t)(pp->pb_count - pb_idx - 1) * sizeof(PTR_EN));
++pp->pb_count;
pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
if (lnum_left != 0)
pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
if (lnum_right != 0)
pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
mf_put(mfp, hp, TRUE, FALSE);
buf->b_ml.ml_stack_top = stack_idx + 1;
if (lineadd)
{
--(buf->b_ml.ml_stack_top);
ml_lineadd(buf, lineadd);
buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
lineadd;
++(buf->b_ml.ml_stack_top);
}
break;
}
else {
for (;;) {
hp_new = ml_new_ptr(mfp);
if (hp_new == NULL) goto theend;
pp_new = (PTR_BL *)(hp_new->bh_data);
if (hp->bh_bnum != 1)
break;
mch_memmove(pp_new, pp, (size_t)page_size);
pp->pb_count = 1;
pp->pb_pointer[0].pe_bnum = hp_new->bh_bnum;
pp->pb_pointer[0].pe_line_count = buf->b_ml.ml_line_count;
pp->pb_pointer[0].pe_old_lnum = 1;
pp->pb_pointer[0].pe_page_count = 1;
mf_put(mfp, hp, TRUE, FALSE); hp = hp_new; pp = pp_new;
CHECK(stack_idx != 0, _("stack_idx should be 0"));
ip->ip_index = 0;
++stack_idx; }
total_moved = pp->pb_count - pb_idx - 1;
if (total_moved)
{
mch_memmove(&pp_new->pb_pointer[0],
&pp->pb_pointer[pb_idx + 1],
(size_t)(total_moved) * sizeof(PTR_EN));
pp_new->pb_count = total_moved;
pp->pb_count -= total_moved - 1;
pp->pb_pointer[pb_idx + 1].pe_bnum = bnum_right;
pp->pb_pointer[pb_idx + 1].pe_line_count = line_count_right;
pp->pb_pointer[pb_idx + 1].pe_page_count = page_count_right;
if (lnum_right)
pp->pb_pointer[pb_idx + 1].pe_old_lnum = lnum_right;
}
else
{
pp_new->pb_count = 1;
pp_new->pb_pointer[0].pe_bnum = bnum_right;
pp_new->pb_pointer[0].pe_line_count = line_count_right;
pp_new->pb_pointer[0].pe_page_count = page_count_right;
pp_new->pb_pointer[0].pe_old_lnum = lnum_right;
}
pp->pb_pointer[pb_idx].pe_bnum = bnum_left;
pp->pb_pointer[pb_idx].pe_line_count = line_count_left;
pp->pb_pointer[pb_idx].pe_page_count = page_count_left;
if (lnum_left)
pp->pb_pointer[pb_idx].pe_old_lnum = lnum_left;
lnum_left = 0;
lnum_right = 0;
line_count_right = 0;
for (i = 0; i < (int)pp_new->pb_count; ++i)
line_count_right += pp_new->pb_pointer[i].pe_line_count;
line_count_left = 0;
for (i = 0; i < (int)pp->pb_count; ++i)
line_count_left += pp->pb_pointer[i].pe_line_count;
bnum_left = hp->bh_bnum;
bnum_right = hp_new->bh_bnum;
page_count_left = 1;
page_count_right = 1;
mf_put(mfp, hp, TRUE, FALSE);
mf_put(mfp, hp_new, TRUE, FALSE);
}
}
if (stack_idx < 0)
{
iemsg(_("E318: Updated too many blocks?"));
buf->b_ml.ml_stack_top = 0; }
}
#ifdef FEAT_BYTEOFF
ml_updatechunk(buf, lnum + 1, (long)len, ML_CHNK_ADDLINE);
#endif
#ifdef FEAT_NETBEANS_INTG
if (netbeans_active())
{
if (STRLEN(line) > 0)
netbeans_inserted(buf, lnum+1, (colnr_T)0, line, (int)STRLEN(line));
netbeans_inserted(buf, lnum+1, (colnr_T)STRLEN(line),
(char_u *)"\n", 1);
}
#endif
#ifdef FEAT_JOB_CHANNEL
if (buf->b_write_to_channel)
channel_write_new_lines(buf);
#endif
ret = OK;
theend:
#ifdef FEAT_PROP_POPUP
vim_free(tofree);
#endif
return ret;
}
static int
ml_append_flush(
buf_T *buf,
linenr_T lnum, char_u *line, colnr_T len, int flags) {
if (lnum > buf->b_ml.ml_line_count)
return FAIL;
if (buf->b_ml.ml_line_lnum != 0)
ml_flush_line(buf);
#ifdef FEAT_EVAL
may_invoke_listeners(buf, lnum + 1, lnum + 1, 1);
if (buf->b_ml.ml_line_lnum != 0)
ml_flush_line(buf);
#endif
return ml_append_int(buf, lnum, line, len, flags);
}
int
ml_append(
linenr_T lnum, char_u *line, colnr_T len, int newfile) {
return ml_append_flags(lnum, line, len, newfile ? ML_APPEND_NEW : 0);
}
int
ml_append_flags(
linenr_T lnum, char_u *line, colnr_T len, int flags) {
if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
return FAIL;
return ml_append_flush(curbuf, lnum, line, len, flags);
}
#if defined(FEAT_SPELL) || defined(FEAT_QUICKFIX) || defined(PROTO)
int
ml_append_buf(
buf_T *buf,
linenr_T lnum, char_u *line, colnr_T len, int newfile) {
if (buf->b_ml.ml_mfp == NULL)
return FAIL;
return ml_append_flush(buf, lnum, line, len, newfile ? ML_APPEND_NEW : 0);
}
#endif
int
ml_replace(linenr_T lnum, char_u *line, int copy)
{
colnr_T len = -1;
if (line != NULL)
len = (colnr_T)STRLEN(line);
return ml_replace_len(lnum, line, len, FALSE, copy);
}
int
ml_replace_len(
linenr_T lnum,
char_u *line_arg,
colnr_T len_arg,
int has_props,
int copy)
{
char_u *line = line_arg;
colnr_T len = len_arg;
if (line == NULL) return FAIL;
if (curbuf->b_ml.ml_mfp == NULL && open_buffer(FALSE, NULL, 0) == FAIL)
return FAIL;
if (!has_props)
++len; if (copy)
{
#ifdef FEAT_PROP_POPUP
if (has_props)
line = vim_memsave(line, len);
else
#endif
line = vim_strnsave(line, len - 1);
if (line == NULL)
return FAIL;
}
#ifdef FEAT_NETBEANS_INTG
if (netbeans_active())
{
netbeans_removed(curbuf, lnum, 0, (long)STRLEN(ml_get(lnum)));
netbeans_inserted(curbuf, lnum, 0, line, (int)STRLEN(line));
}
#endif
if (curbuf->b_ml.ml_line_lnum != lnum)
{
ml_flush_line(curbuf);
curbuf->b_ml.ml_flags &= ~ML_LINE_DIRTY;
#ifdef FEAT_PROP_POPUP
if (curbuf->b_has_textprop && !has_props)
ml_get_buf(curbuf, lnum, TRUE);
#endif
}
#ifdef FEAT_PROP_POPUP
if (curbuf->b_has_textprop && !has_props)
{
size_t oldtextlen = STRLEN(curbuf->b_ml.ml_line_ptr) + 1;
if (oldtextlen < (size_t)curbuf->b_ml.ml_line_len)
{
char_u *newline;
size_t textproplen = curbuf->b_ml.ml_line_len - oldtextlen;
newline = alloc(len + (int)textproplen);
if (newline != NULL)
{
mch_memmove(newline, line, len);
mch_memmove(newline + len, curbuf->b_ml.ml_line_ptr
+ oldtextlen, textproplen);
vim_free(line);
line = newline;
len += (colnr_T)textproplen;
}
}
}
#endif
if (curbuf->b_ml.ml_flags & ML_LINE_DIRTY) vim_free(curbuf->b_ml.ml_line_ptr);
curbuf->b_ml.ml_line_ptr = line;
curbuf->b_ml.ml_line_len = len;
curbuf->b_ml.ml_line_lnum = lnum;
curbuf->b_ml.ml_flags = (curbuf->b_ml.ml_flags | ML_LINE_DIRTY) & ~ML_EMPTY;
return OK;
}
#ifdef FEAT_PROP_POPUP
static void
adjust_text_props_for_delete(
buf_T *buf,
linenr_T lnum,
char_u *del_props,
int del_props_len,
int above)
{
int did_get_line = FALSE;
int done_del;
int done_this;
textprop_T prop_del;
bhdr_T *hp;
DATA_BL *dp;
int idx;
int line_start;
long line_size;
int this_props_len;
char_u *text;
size_t textlen;
int found;
for (done_del = 0; done_del < del_props_len; done_del += sizeof(textprop_T))
{
mch_memmove(&prop_del, del_props + done_del, sizeof(textprop_T));
if ((above && (prop_del.tp_flags & TP_FLAG_CONT_PREV)
&& !(prop_del.tp_flags & TP_FLAG_CONT_NEXT))
|| (!above && (prop_del.tp_flags & TP_FLAG_CONT_NEXT)
&& !(prop_del.tp_flags & TP_FLAG_CONT_PREV)))
{
if (!did_get_line)
{
did_get_line = TRUE;
if ((hp = ml_find_line(buf, lnum, ML_FIND)) == NULL)
return;
dp = (DATA_BL *)(hp->bh_data);
idx = lnum - buf->b_ml.ml_locked_low;
line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
if (idx == 0) line_size = dp->db_txt_end - line_start;
else
line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK)
- line_start;
text = (char_u *)dp + line_start;
textlen = STRLEN(text) + 1;
if ((long)textlen >= line_size)
{
if (above)
internal_error("no text property above deleted line");
else
internal_error("no text property below deleted line");
return;
}
this_props_len = line_size - (int)textlen;
}
found = FALSE;
for (done_this = 0; done_this < this_props_len;
done_this += sizeof(textprop_T))
{
int flag = above ? TP_FLAG_CONT_NEXT
: TP_FLAG_CONT_PREV;
textprop_T prop_this;
mch_memmove(&prop_this, text + textlen + done_del,
sizeof(textprop_T));
if ((prop_this.tp_flags & flag)
&& prop_del.tp_id == prop_this.tp_id
&& prop_del.tp_type == prop_this.tp_type)
{
found = TRUE;
prop_this.tp_flags &= ~flag;
mch_memmove(text + textlen + done_del, &prop_this,
sizeof(textprop_T));
break;
}
}
if (!found)
{
if (above)
internal_error("text property above deleted line not found");
else
internal_error("text property below deleted line not found");
}
buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
}
}
}
#endif
static int
ml_delete_int(buf_T *buf, linenr_T lnum, int flags)
{
bhdr_T *hp;
memfile_T *mfp;
DATA_BL *dp;
PTR_BL *pp;
infoptr_T *ip;
int count; int idx;
int stack_idx;
int text_start;
int line_start;
long line_size;
int i;
int ret = FAIL;
#ifdef FEAT_PROP_POPUP
char_u *textprop_save = NULL;
int textprop_save_len;
#endif
if (lowest_marked && lowest_marked > lnum)
lowest_marked--;
if (buf->b_ml.ml_line_count == 1) {
if ((flags & ML_DEL_MESSAGE)
#ifdef FEAT_NETBEANS_INTG
&& !netbeansSuppressNoLines
#endif
)
set_keep_msg((char_u *)_(no_lines_msg), 0);
i = ml_replace((linenr_T)1, (char_u *)"", TRUE);
buf->b_ml.ml_flags |= ML_EMPTY;
return i;
}
mfp = buf->b_ml.ml_mfp;
if (mfp == NULL)
return FAIL;
if ((hp = ml_find_line(buf, lnum, ML_DELETE)) == NULL)
return FAIL;
dp = (DATA_BL *)(hp->bh_data);
count = (long)(buf->b_ml.ml_locked_high)
- (long)(buf->b_ml.ml_locked_low) + 2;
idx = lnum - buf->b_ml.ml_locked_low;
--buf->b_ml.ml_line_count;
line_start = ((dp->db_index[idx]) & DB_INDEX_MASK);
if (idx == 0) line_size = dp->db_txt_end - line_start;
else
line_size = ((dp->db_index[idx - 1]) & DB_INDEX_MASK) - line_start;
#ifdef FEAT_NETBEANS_INTG
if (netbeans_active())
netbeans_removed(buf, lnum, 0, (long)line_size);
#endif
#ifdef FEAT_PROP_POPUP
if (buf->b_has_textprop && !(flags & ML_DEL_UNDO))
{
size_t textlen = STRLEN((char_u *)dp + line_start) + 1;
if ((long)textlen < line_size)
{
textprop_save_len = line_size - (int)textlen;
textprop_save = vim_memsave((char_u *)dp + line_start + textlen,
textprop_save_len);
}
}
#endif
if (count == 1)
{
mf_free(mfp, hp); buf->b_ml.ml_locked = NULL;
for (stack_idx = buf->b_ml.ml_stack_top - 1; stack_idx >= 0;
--stack_idx)
{
buf->b_ml.ml_stack_top = 0; ip = &(buf->b_ml.ml_stack[stack_idx]);
idx = ip->ip_index;
if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
goto theend;
pp = (PTR_BL *)(hp->bh_data); if (pp->pb_id != PTR_ID)
{
iemsg(_("E317: pointer block id wrong 4"));
mf_put(mfp, hp, FALSE, FALSE);
goto theend;
}
count = --(pp->pb_count);
if (count == 0) mf_free(mfp, hp);
else
{
if (count != idx) mch_memmove(&pp->pb_pointer[idx], &pp->pb_pointer[idx + 1],
(size_t)(count - idx) * sizeof(PTR_EN));
mf_put(mfp, hp, TRUE, FALSE);
buf->b_ml.ml_stack_top = stack_idx; if (buf->b_ml.ml_locked_lineadd != 0)
{
ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
buf->b_ml.ml_stack[buf->b_ml.ml_stack_top].ip_high +=
buf->b_ml.ml_locked_lineadd;
}
++(buf->b_ml.ml_stack_top);
break;
}
}
CHECK(stack_idx < 0, _("deleted block 1?"));
}
else
{
text_start = dp->db_txt_start;
mch_memmove((char *)dp + text_start + line_size,
(char *)dp + text_start, (size_t)(line_start - text_start));
for (i = idx; i < count - 1; ++i)
dp->db_index[i] = dp->db_index[i + 1] + line_size;
dp->db_free += line_size + INDEX_SIZE;
dp->db_txt_start += line_size;
--(dp->db_line_count);
buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
}
#ifdef FEAT_BYTEOFF
ml_updatechunk(buf, lnum, line_size, ML_CHNK_DELLINE);
#endif
ret = OK;
theend:
#ifdef FEAT_PROP_POPUP
if (textprop_save != NULL)
{
if (lnum > 1)
adjust_text_props_for_delete(buf, lnum - 1, textprop_save, textprop_save_len, TRUE);
if (lnum <= buf->b_ml.ml_line_count)
adjust_text_props_for_delete(buf, lnum, textprop_save, textprop_save_len, FALSE);
}
vim_free(textprop_save);
#endif
return ret;
}
int
ml_delete(linenr_T lnum, int message)
{
return ml_delete_flags(lnum, message ? ML_DEL_MESSAGE : 0);
}
int
ml_delete_flags(linenr_T lnum, int flags)
{
ml_flush_line(curbuf);
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count)
return FAIL;
#ifdef FEAT_EVAL
may_invoke_listeners(curbuf, lnum, lnum + 1, -1);
#endif
return ml_delete_int(curbuf, lnum, flags);
}
void
ml_setmarked(linenr_T lnum)
{
bhdr_T *hp;
DATA_BL *dp;
if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count
|| curbuf->b_ml.ml_mfp == NULL)
return;
if (lowest_marked == 0 || lowest_marked > lnum)
lowest_marked = lnum;
if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
return;
dp = (DATA_BL *)(hp->bh_data);
dp->db_index[lnum - curbuf->b_ml.ml_locked_low] |= DB_MARKED;
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
}
linenr_T
ml_firstmarked(void)
{
bhdr_T *hp;
DATA_BL *dp;
linenr_T lnum;
int i;
if (curbuf->b_ml.ml_mfp == NULL)
return (linenr_T) 0;
for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
{
if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
return (linenr_T)0;
dp = (DATA_BL *)(hp->bh_data);
for (i = lnum - curbuf->b_ml.ml_locked_low;
lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
if ((dp->db_index[i]) & DB_MARKED)
{
(dp->db_index[i]) &= DB_INDEX_MASK;
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
lowest_marked = lnum + 1;
return lnum;
}
}
return (linenr_T) 0;
}
void
ml_clearmarked(void)
{
bhdr_T *hp;
DATA_BL *dp;
linenr_T lnum;
int i;
if (curbuf->b_ml.ml_mfp == NULL) return;
for (lnum = lowest_marked; lnum <= curbuf->b_ml.ml_line_count; )
{
if ((hp = ml_find_line(curbuf, lnum, ML_FIND)) == NULL)
return;
dp = (DATA_BL *)(hp->bh_data);
for (i = lnum - curbuf->b_ml.ml_locked_low;
lnum <= curbuf->b_ml.ml_locked_high; ++i, ++lnum)
if ((dp->db_index[i]) & DB_MARKED)
{
(dp->db_index[i]) &= DB_INDEX_MASK;
curbuf->b_ml.ml_flags |= ML_LOCKED_DIRTY;
}
}
lowest_marked = 0;
return;
}
static void
ml_flush_line(buf_T *buf)
{
bhdr_T *hp;
DATA_BL *dp;
linenr_T lnum;
char_u *new_line;
char_u *old_line;
colnr_T new_len;
int old_len;
int extra;
int idx;
int start;
int count;
int i;
static int entered = FALSE;
if (buf->b_ml.ml_line_lnum == 0 || buf->b_ml.ml_mfp == NULL)
return;
if (buf->b_ml.ml_flags & ML_LINE_DIRTY)
{
if (entered)
return;
entered = TRUE;
lnum = buf->b_ml.ml_line_lnum;
new_line = buf->b_ml.ml_line_ptr;
hp = ml_find_line(buf, lnum, ML_FIND);
if (hp == NULL)
siemsg(_("E320: Cannot find line %ld"), lnum);
else
{
dp = (DATA_BL *)(hp->bh_data);
idx = lnum - buf->b_ml.ml_locked_low;
start = ((dp->db_index[idx]) & DB_INDEX_MASK);
old_line = (char_u *)dp + start;
if (idx == 0) old_len = dp->db_txt_end - start;
else old_len = (dp->db_index[idx - 1] & DB_INDEX_MASK) - start;
new_len = buf->b_ml.ml_line_len;
extra = new_len - old_len;
if ((int)dp->db_free >= extra)
{
count = buf->b_ml.ml_locked_high - buf->b_ml.ml_locked_low + 1;
if (extra != 0 && idx < count - 1)
{
mch_memmove((char *)dp + dp->db_txt_start - extra,
(char *)dp + dp->db_txt_start,
(size_t)(start - dp->db_txt_start));
for (i = idx + 1; i < count; ++i)
dp->db_index[i] -= extra;
}
dp->db_index[idx] -= extra;
dp->db_free -= extra;
dp->db_txt_start -= extra;
mch_memmove(old_line - extra, new_line, (size_t)new_len);
buf->b_ml.ml_flags |= (ML_LOCKED_DIRTY | ML_LOCKED_POS);
#ifdef FEAT_BYTEOFF
ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE);
#endif
}
else
{
(void)ml_append_int(buf, lnum, new_line, new_len,
(dp->db_index[idx] & DB_MARKED) ? ML_APPEND_MARK : 0);
(void)ml_delete_int(buf, lnum, 0);
}
}
vim_free(new_line);
entered = FALSE;
}
buf->b_ml.ml_line_lnum = 0;
}
static bhdr_T *
ml_new_data(memfile_T *mfp, int negative, int page_count)
{
bhdr_T *hp;
DATA_BL *dp;
if ((hp = mf_new(mfp, negative, page_count)) == NULL)
return NULL;
dp = (DATA_BL *)(hp->bh_data);
dp->db_id = DATA_ID;
dp->db_txt_start = dp->db_txt_end = page_count * mfp->mf_page_size;
dp->db_free = dp->db_txt_start - HEADER_SIZE;
dp->db_line_count = 0;
return hp;
}
static bhdr_T *
ml_new_ptr(memfile_T *mfp)
{
bhdr_T *hp;
PTR_BL *pp;
if ((hp = mf_new(mfp, FALSE, 1)) == NULL)
return NULL;
pp = (PTR_BL *)(hp->bh_data);
pp->pb_id = PTR_ID;
pp->pb_count = 0;
pp->pb_count_max = (short_u)((mfp->mf_page_size - sizeof(PTR_BL))
/ sizeof(PTR_EN) + 1);
return hp;
}
static bhdr_T *
ml_find_line(buf_T *buf, linenr_T lnum, int action)
{
DATA_BL *dp;
PTR_BL *pp;
infoptr_T *ip;
bhdr_T *hp;
memfile_T *mfp;
linenr_T t;
blocknr_T bnum, bnum2;
int dirty;
linenr_T low, high;
int top;
int page_count;
int idx;
mfp = buf->b_ml.ml_mfp;
if (buf->b_ml.ml_locked)
{
if (ML_SIMPLE(action)
&& buf->b_ml.ml_locked_low <= lnum
&& buf->b_ml.ml_locked_high >= lnum
&& !mf_dont_release)
{
if (action == ML_INSERT)
{
++(buf->b_ml.ml_locked_lineadd);
++(buf->b_ml.ml_locked_high);
}
else if (action == ML_DELETE)
{
--(buf->b_ml.ml_locked_lineadd);
--(buf->b_ml.ml_locked_high);
}
return (buf->b_ml.ml_locked);
}
mf_put(mfp, buf->b_ml.ml_locked, buf->b_ml.ml_flags & ML_LOCKED_DIRTY,
buf->b_ml.ml_flags & ML_LOCKED_POS);
buf->b_ml.ml_locked = NULL;
if (buf->b_ml.ml_locked_lineadd != 0)
ml_lineadd(buf, buf->b_ml.ml_locked_lineadd);
}
if (action == ML_FLUSH) return NULL;
bnum = 1; page_count = 1;
low = 1;
high = buf->b_ml.ml_line_count;
if (action == ML_FIND) {
for (top = buf->b_ml.ml_stack_top - 1; top >= 0; --top)
{
ip = &(buf->b_ml.ml_stack[top]);
if (ip->ip_low <= lnum && ip->ip_high >= lnum)
{
bnum = ip->ip_bnum;
low = ip->ip_low;
high = ip->ip_high;
buf->b_ml.ml_stack_top = top; break;
}
}
if (top < 0)
buf->b_ml.ml_stack_top = 0; }
else buf->b_ml.ml_stack_top = 0;
for (;;)
{
if ((hp = mf_get(mfp, bnum, page_count)) == NULL)
goto error_noblock;
if (action == ML_INSERT)
++high;
else if (action == ML_DELETE)
--high;
dp = (DATA_BL *)(hp->bh_data);
if (dp->db_id == DATA_ID) {
buf->b_ml.ml_locked = hp;
buf->b_ml.ml_locked_low = low;
buf->b_ml.ml_locked_high = high;
buf->b_ml.ml_locked_lineadd = 0;
buf->b_ml.ml_flags &= ~(ML_LOCKED_DIRTY | ML_LOCKED_POS);
return hp;
}
pp = (PTR_BL *)(dp); if (pp->pb_id != PTR_ID)
{
iemsg(_("E317: pointer block id wrong"));
goto error_block;
}
if ((top = ml_add_stack(buf)) < 0) goto error_block;
ip = &(buf->b_ml.ml_stack[top]);
ip->ip_bnum = bnum;
ip->ip_low = low;
ip->ip_high = high;
ip->ip_index = -1;
dirty = FALSE;
for (idx = 0; idx < (int)pp->pb_count; ++idx)
{
t = pp->pb_pointer[idx].pe_line_count;
CHECK(t == 0, _("pe_line_count is zero"));
if ((low += t) > lnum)
{
ip->ip_index = idx;
bnum = pp->pb_pointer[idx].pe_bnum;
page_count = pp->pb_pointer[idx].pe_page_count;
high = low - 1;
low -= t;
if (bnum < 0)
{
bnum2 = mf_trans_del(mfp, bnum);
if (bnum != bnum2)
{
bnum = bnum2;
pp->pb_pointer[idx].pe_bnum = bnum;
dirty = TRUE;
}
}
break;
}
}
if (idx >= (int)pp->pb_count) {
if (lnum > buf->b_ml.ml_line_count)
siemsg(_("E322: line number out of range: %ld past the end"),
lnum - buf->b_ml.ml_line_count);
else
siemsg(_("E323: line count wrong in block %ld"), bnum);
goto error_block;
}
if (action == ML_DELETE)
{
pp->pb_pointer[idx].pe_line_count--;
dirty = TRUE;
}
else if (action == ML_INSERT)
{
pp->pb_pointer[idx].pe_line_count++;
dirty = TRUE;
}
mf_put(mfp, hp, dirty, FALSE);
}
error_block:
mf_put(mfp, hp, FALSE, FALSE);
error_noblock:
if (action == ML_DELETE)
ml_lineadd(buf, 1);
else if (action == ML_INSERT)
ml_lineadd(buf, -1);
buf->b_ml.ml_stack_top = 0;
return NULL;
}
static int
ml_add_stack(buf_T *buf)
{
int top;
infoptr_T *newstack;
top = buf->b_ml.ml_stack_top;
if (top == buf->b_ml.ml_stack_size)
{
CHECK(top > 0, _("Stack size increases"));
newstack = ALLOC_MULT(infoptr_T, buf->b_ml.ml_stack_size + STACK_INCR);
if (newstack == NULL)
return -1;
if (top > 0)
mch_memmove(newstack, buf->b_ml.ml_stack,
(size_t)top * sizeof(infoptr_T));
vim_free(buf->b_ml.ml_stack);
buf->b_ml.ml_stack = newstack;
buf->b_ml.ml_stack_size += STACK_INCR;
}
buf->b_ml.ml_stack_top++;
return top;
}
static void
ml_lineadd(buf_T *buf, int count)
{
int idx;
infoptr_T *ip;
PTR_BL *pp;
memfile_T *mfp = buf->b_ml.ml_mfp;
bhdr_T *hp;
for (idx = buf->b_ml.ml_stack_top - 1; idx >= 0; --idx)
{
ip = &(buf->b_ml.ml_stack[idx]);
if ((hp = mf_get(mfp, ip->ip_bnum, 1)) == NULL)
break;
pp = (PTR_BL *)(hp->bh_data); if (pp->pb_id != PTR_ID)
{
mf_put(mfp, hp, FALSE, FALSE);
iemsg(_("E317: pointer block id wrong 2"));
break;
}
pp->pb_pointer[ip->ip_index].pe_line_count += count;
ip->ip_high += count;
mf_put(mfp, hp, TRUE, FALSE);
}
}
#if defined(HAVE_READLINK) || defined(PROTO)
int
resolve_symlink(char_u *fname, char_u *buf)
{
char_u tmp[MAXPATHL];
int ret;
int depth = 0;
if (fname == NULL)
return FAIL;
vim_strncpy(tmp, fname, MAXPATHL - 1);
for (;;)
{
if (++depth == 100)
{
semsg(_("E773: Symlink loop for \"%s\""), fname);
return FAIL;
}
ret = readlink((char *)tmp, (char *)buf, MAXPATHL - 1);
if (ret <= 0)
{
if (errno == EINVAL || errno == ENOENT)
{
if (depth == 1)
return FAIL;
break;
}
return FAIL;
}
buf[ret] = NUL;
if (mch_isFullName(buf))
STRCPY(tmp, buf);
else
{
char_u *tail;
tail = gettail(tmp);
if (STRLEN(tail) + STRLEN(buf) >= MAXPATHL)
return FAIL;
STRCPY(tail, buf);
}
}
return vim_FullName(tmp, buf, MAXPATHL, TRUE);
}
#endif
char_u *
makeswapname(
char_u *fname,
char_u *ffname UNUSED,
buf_T *buf,
char_u *dir_name)
{
char_u *r, *s;
char_u *fname_res = fname;
#ifdef HAVE_READLINK
char_u fname_buf[MAXPATHL];
#endif
#if defined(UNIX) || defined(MSWIN) // Need _very_ long file names
int len = (int)STRLEN(dir_name);
s = dir_name + len;
if (after_pathsep(dir_name, s) && len > 1 && s[-1] == s[-2])
{ r = NULL;
if ((s = make_percent_swname(dir_name, fname)) != NULL)
{
r = modname(s, (char_u *)".swp", FALSE);
vim_free(s);
}
return r;
}
#endif
#ifdef HAVE_READLINK
if (resolve_symlink(fname, fname_buf) == OK)
fname_res = fname_buf;
#endif
r = buf_modname(
(buf->b_p_sn || buf->b_shortname),
fname_res,
(char_u *)
#if defined(VMS)
"_swp",
#else
".swp",
#endif
dir_name[0] == '.' && dir_name[1] == NUL);
if (r == NULL) return NULL;
s = get_file_in_dir(r, dir_name);
vim_free(r);
return s;
}
char_u *
get_file_in_dir(
char_u *fname,
char_u *dname) {
char_u *t;
char_u *tail;
char_u *retval;
int save_char;
tail = gettail(fname);
if (dname[0] == '.' && dname[1] == NUL)
retval = vim_strsave(fname);
else if (dname[0] == '.' && vim_ispathsep(dname[1]))
{
if (tail == fname) retval = concat_fnames(dname + 2, tail, TRUE);
else
{
save_char = *tail;
*tail = NUL;
t = concat_fnames(fname, dname + 2, TRUE);
*tail = save_char;
if (t == NULL) retval = NULL;
else
{
retval = concat_fnames(t, tail, TRUE);
vim_free(t);
}
}
}
else
retval = concat_fnames(dname, tail, TRUE);
#ifdef MSWIN
if (retval != NULL)
for (t = gettail(retval); *t != NUL; MB_PTR_ADV(t))
if (*t == ':')
*t = '%';
#endif
return retval;
}
static void
attention_message(
buf_T *buf, char_u *fname) {
stat_T st;
time_t swap_mtime;
++no_wait_return;
(void)emsg(_("E325: ATTENTION"));
msg_puts(_("\nFound a swap file by the name \""));
msg_home_replace(fname);
msg_puts("\"\n");
swap_mtime = swapfile_info(fname);
msg_puts(_("While opening file \""));
msg_outtrans(buf->b_fname);
msg_puts("\"\n");
if (mch_stat((char *)buf->b_fname, &st) == -1)
{
msg_puts(_(" CANNOT BE FOUND"));
}
else
{
msg_puts(_(" dated: "));
msg_puts(get_ctime(st.st_mtime, TRUE));
if (swap_mtime != 0 && st.st_mtime > swap_mtime)
msg_puts(_(" NEWER than swap file!\n"));
}
msg_puts(_("\n(1) Another program may be editing the same file. If this is the case,\n be careful not to end up with two different instances of the same\n file when making changes. Quit, or continue with caution.\n"));
msg_puts(_("(2) An edit session for this file crashed.\n"));
msg_puts(_(" If this is the case, use \":recover\" or \"vim -r "));
msg_outtrans(buf->b_fname);
msg_puts(_("\"\n to recover the changes (see \":help recovery\").\n"));
msg_puts(_(" If you did this already, delete the swap file \""));
msg_outtrans(fname);
msg_puts(_("\"\n to avoid this message.\n"));
cmdline_row = msg_row;
--no_wait_return;
}
#if defined(FEAT_EVAL)
static int
do_swapexists(buf_T *buf, char_u *fname)
{
set_vim_var_string(VV_SWAPNAME, fname, -1);
set_vim_var_string(VV_SWAPCHOICE, NULL, -1);
++allbuf_lock;
apply_autocmds(EVENT_SWAPEXISTS, buf->b_fname, NULL, FALSE, NULL);
--allbuf_lock;
set_vim_var_string(VV_SWAPNAME, NULL, -1);
switch (*get_vim_var_str(VV_SWAPCHOICE))
{
case 'o': return 1;
case 'e': return 2;
case 'r': return 3;
case 'd': return 4;
case 'q': return 5;
case 'a': return 6;
}
return 0;
}
#endif
static char_u *
findswapname(
buf_T *buf,
char_u **dirp, char_u *old_fname) {
char_u *fname;
int n;
char_u *dir_name;
#ifdef AMIGA
BPTR fh;
#endif
int r;
char_u *buf_fname = buf->b_fname;
#if !defined(UNIX)
# define CREATE_DUMMY_FILE
FILE *dummyfd = NULL;
# ifdef MSWIN
if (buf_fname != NULL && !mch_isFullName(buf_fname)
&& vim_strchr(gettail(buf_fname), ':'))
{
char_u *t;
buf_fname = vim_strsave(buf_fname);
if (buf_fname == NULL)
buf_fname = buf->b_fname;
else
for (t = gettail(buf_fname); *t != NUL; MB_PTR_ADV(t))
if (*t == ':')
*t = '%';
}
# endif
if (!(buf->b_p_sn || buf->b_shortname) && buf_fname != NULL
&& mch_getperm(buf_fname) < 0)
dummyfd = mch_fopen((char *)buf_fname, "w");
#endif
dir_name = alloc(STRLEN(*dirp) + 1);
if (dir_name == NULL)
*dirp = NULL;
else
(void)copy_option_part(dirp, dir_name, 31000, ",");
if (dir_name == NULL) fname = NULL;
else
fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name);
for (;;)
{
if (fname == NULL) break;
if ((n = (int)STRLEN(fname)) == 0) {
VIM_CLEAR(fname);
break;
}
#if defined(UNIX)
if (fname[n - 2] == 'w' && fname[n - 1] == 'p'
&& !(buf->b_p_sn || buf->b_shortname))
{
char_u *tail;
char_u *fname2;
stat_T s1, s2;
int f1, f2;
int created1 = FALSE, created2 = FALSE;
int same = FALSE;
tail = gettail(buf_fname);
if ( vim_strchr(tail, '.') != NULL
|| STRLEN(tail) > (size_t)8
|| *gettail(fname) == '.')
{
fname2 = alloc(n + 2);
if (fname2 != NULL)
{
STRCPY(fname2, fname);
if (vim_strchr(tail, '.') != NULL)
fname2[n - 1] = 'x';
else if (*gettail(fname) == '.')
{
fname2[n] = 'x';
fname2[n + 1] = NUL;
}
else
fname2[n - 5] += 1;
f1 = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
if (f1 < 0)
{
f1 = mch_open_rw((char *)fname,
O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
created1 = TRUE;
}
if (f1 >= 0)
{
f2 = mch_open((char *)fname2, O_RDONLY | O_EXTRA, 0);
if (f2 < 0)
{
f2 = mch_open_rw((char *)fname2,
O_RDWR|O_CREAT|O_EXCL|O_EXTRA);
created2 = TRUE;
}
if (f2 >= 0)
{
if (mch_fstat(f1, &s1) != -1
&& mch_fstat(f2, &s2) != -1
&& s1.st_dev == s2.st_dev
&& s1.st_ino == s2.st_ino)
same = TRUE;
close(f2);
if (created2)
mch_remove(fname2);
}
close(f1);
if (created1)
mch_remove(fname);
}
vim_free(fname2);
if (same)
{
buf->b_shortname = TRUE;
vim_free(fname);
fname = makeswapname(buf_fname, buf->b_ffname,
buf, dir_name);
continue; }
}
}
}
#endif
if (mch_getperm(fname) < 0) {
#ifdef HAVE_LSTAT
stat_T sb;
if (mch_lstat((char *)fname, &sb) < 0)
#else
# ifdef AMIGA
fh = Open((UBYTE *)fname, (long)MODE_NEWFILE);
if (fh != (BPTR)NULL) {
Close(fh);
mch_remove(fname);
break;
}
if (IoErr() != ERROR_OBJECT_IN_USE
&& IoErr() != ERROR_OBJECT_EXISTS)
# endif
#endif
break;
}
if (old_fname != NULL && fnamecmp(fname, old_fname) == 0)
break;
if (fname[n - 2] == 'w' && fname[n - 1] == 'p') {
if (!(buf->b_p_sn || buf->b_shortname)) {
fname[n - 1] = 'x';
r = mch_getperm(fname); fname[n - 1] = 'p';
if (r >= 0) {
buf->b_shortname = TRUE;
vim_free(fname);
fname = makeswapname(buf_fname, buf->b_ffname,
buf, dir_name);
continue; }
}
if (!recoverymode && buf_fname != NULL
&& !buf->b_help
&& !(buf->b_flags & (BF_DUMMY | BF_NO_SEA)))
{
int fd;
struct block0 b0;
int differ = FALSE;
fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0);
if (fd >= 0)
{
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0))
{
if (b0.b0_flags & B0_SAME_DIR)
{
if (fnamecmp(gettail(buf->b_ffname),
gettail(b0.b0_fname)) != 0
|| !same_directory(fname, buf->b_ffname))
{
#ifdef CHECK_INODE
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
if (fnamecmp_ino(buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino)))
#endif
differ = TRUE;
}
}
else
{
expand_env(b0.b0_fname, NameBuff, MAXPATHL);
#ifdef CHECK_INODE
if (fnamecmp_ino(buf->b_ffname, NameBuff,
char_to_long(b0.b0_ino)))
differ = TRUE;
#else
if (fnamecmp(NameBuff, buf->b_ffname) != 0)
differ = TRUE;
#endif
}
}
close(fd);
}
if (differ == FALSE && !(curbuf->b_flags & BF_RECOVERED)
&& vim_strchr(p_shm, SHM_ATTENTION) == NULL)
{
int choice = 0;
stat_T st;
#ifdef CREATE_DUMMY_FILE
int did_use_dummy = FALSE;
if (dummyfd != NULL)
{
fclose(dummyfd);
dummyfd = NULL;
mch_remove(buf_fname);
did_use_dummy = TRUE;
}
#endif
#ifdef HAVE_PROCESS_STILL_RUNNING
process_still_running = FALSE;
#endif
if (mch_stat((char *)buf->b_fname, &st) == 0
&& swapfile_unchanged(fname))
{
choice = 4;
if (p_verbose > 0)
verb_msg(_("Found a swap file that is not useful, deleting it"));
}
#if defined(FEAT_EVAL)
if (choice == 0
&& swap_exists_action != SEA_NONE
&& has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf))
choice = do_swapexists(buf, fname);
if (choice == 0)
#endif
{
#ifdef FEAT_GUI
if (gui.starting && !gui.in_use)
gui_start(NULL);
#endif
attention_message(buf, fname);
got_int = FALSE;
flush_buffers(FLUSH_TYPEAHEAD);
}
#if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
if (swap_exists_action != SEA_NONE && choice == 0)
{
char_u *name;
name = alloc(STRLEN(fname)
+ STRLEN(_("Swap file \""))
+ STRLEN(_("\" already exists!")) + 5);
if (name != NULL)
{
STRCPY(name, _("Swap file \""));
home_replace(NULL, fname, name + STRLEN(name),
1000, TRUE);
STRCAT(name, _("\" already exists!"));
}
choice = do_dialog(VIM_WARNING,
(char_u *)_("VIM - ATTENTION"),
name == NULL
? (char_u *)_("Swap file already exists!")
: name,
# ifdef HAVE_PROCESS_STILL_RUNNING
process_still_running
? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") :
# endif
(char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Delete it\n&Quit\n&Abort"), 1, NULL, FALSE);
# ifdef HAVE_PROCESS_STILL_RUNNING
if (process_still_running && choice >= 4)
choice++; # endif
vim_free(name);
msg_scrolled = 0;
redraw_all_later(NOT_VALID);
}
#endif
if (choice > 0)
{
switch (choice)
{
case 1:
buf->b_p_ro = TRUE;
break;
case 2:
break;
case 3:
swap_exists_action = SEA_RECOVER;
break;
case 4:
mch_remove(fname);
break;
case 5:
swap_exists_action = SEA_QUIT;
break;
case 6:
swap_exists_action = SEA_QUIT;
got_int = TRUE;
break;
}
if (mch_getperm(fname) < 0)
break;
}
else
{
msg_puts("\n");
if (msg_silent == 0)
need_wait_return = TRUE;
}
#ifdef CREATE_DUMMY_FILE
if (did_use_dummy)
dummyfd = mch_fopen((char *)buf_fname, "w");
#endif
}
}
}
if (fname[n - 1] == 'a') {
if (fname[n - 2] == 'a') {
emsg(_("E326: Too many swap files found"));
VIM_CLEAR(fname);
break;
}
--fname[n - 2]; fname[n - 1] = 'z' + 1;
}
--fname[n - 1]; }
vim_free(dir_name);
#ifdef CREATE_DUMMY_FILE
if (dummyfd != NULL) {
fclose(dummyfd);
mch_remove(buf_fname);
}
#endif
#ifdef MSWIN
if (buf_fname != buf->b_fname)
vim_free(buf_fname);
#endif
return fname;
}
static int
b0_magic_wrong(ZERO_BL *b0p)
{
return (b0p->b0_magic_long != (long)B0_MAGIC_LONG
|| b0p->b0_magic_int != (int)B0_MAGIC_INT
|| b0p->b0_magic_short != (short)B0_MAGIC_SHORT
|| b0p->b0_magic_char != B0_MAGIC_CHAR);
}
#ifdef CHECK_INODE
static int
fnamecmp_ino(
char_u *fname_c, char_u *fname_s, long ino_block0)
{
stat_T st;
ino_t ino_c = 0; ino_t ino_s; char_u buf_c[MAXPATHL]; char_u buf_s[MAXPATHL]; int retval_c; int retval_s;
if (mch_stat((char *)fname_c, &st) == 0)
ino_c = (ino_t)st.st_ino;
if (mch_stat((char *)fname_s, &st) == 0)
ino_s = (ino_t)st.st_ino;
else
ino_s = (ino_t)ino_block0;
if (ino_c && ino_s)
return (ino_c != ino_s);
retval_c = vim_FullName(fname_c, buf_c, MAXPATHL, TRUE);
retval_s = vim_FullName(fname_s, buf_s, MAXPATHL, TRUE);
if (retval_c == OK && retval_s == OK)
return STRCMP(buf_c, buf_s) != 0;
if (ino_s == 0 && ino_c == 0 && retval_c == FAIL && retval_s == FAIL)
return STRCMP(fname_c, fname_s) != 0;
return TRUE;
}
#endif // CHECK_INODE
static void
long_to_char(long n, char_u *s)
{
s[0] = (char_u)(n & 0xff);
n = (unsigned)n >> 8;
s[1] = (char_u)(n & 0xff);
n = (unsigned)n >> 8;
s[2] = (char_u)(n & 0xff);
n = (unsigned)n >> 8;
s[3] = (char_u)(n & 0xff);
}
static long
char_to_long(char_u *s)
{
long retval;
retval = s[3];
retval <<= 8;
retval |= s[2];
retval <<= 8;
retval |= s[1];
retval <<= 8;
retval |= s[0];
return retval;
}
void
ml_setflags(buf_T *buf)
{
bhdr_T *hp;
ZERO_BL *b0p;
if (!buf->b_ml.ml_mfp)
return;
for (hp = buf->b_ml.ml_mfp->mf_used_last; hp != NULL; hp = hp->bh_prev)
{
if (hp->bh_bnum == 0)
{
b0p = (ZERO_BL *)(hp->bh_data);
b0p->b0_dirty = buf->b_changed ? B0_DIRTY : 0;
b0p->b0_flags = (b0p->b0_flags & ~B0_FF_MASK)
| (get_fileformat(buf) + 1);
add_b0_fenc(b0p, buf);
hp->bh_flags |= BH_DIRTY;
mf_sync(buf->b_ml.ml_mfp, MFS_ZERO);
break;
}
}
}
#if defined(FEAT_CRYPT) || defined(PROTO)
char_u *
ml_encrypt_data(
memfile_T *mfp,
char_u *data,
off_T offset,
unsigned size)
{
DATA_BL *dp = (DATA_BL *)data;
char_u *head_end;
char_u *text_start;
char_u *new_data;
int text_len;
cryptstate_T *state;
if (dp->db_id != DATA_ID)
return data;
state = ml_crypt_prepare(mfp, offset, FALSE);
if (state == NULL)
return data;
new_data = alloc(size);
if (new_data == NULL)
return NULL;
head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
text_start = (char_u *)dp + dp->db_txt_start;
text_len = size - dp->db_txt_start;
mch_memmove(new_data, dp, head_end - (char_u *)dp);
crypt_encode(state, text_start, text_len, new_data + dp->db_txt_start);
crypt_free_state(state);
if (head_end < text_start)
vim_memset(new_data + (head_end - data), 0, text_start - head_end);
return new_data;
}
void
ml_decrypt_data(
memfile_T *mfp,
char_u *data,
off_T offset,
unsigned size)
{
DATA_BL *dp = (DATA_BL *)data;
char_u *head_end;
char_u *text_start;
int text_len;
cryptstate_T *state;
if (dp->db_id == DATA_ID)
{
head_end = (char_u *)(&dp->db_index[dp->db_line_count]);
text_start = (char_u *)dp + dp->db_txt_start;
text_len = dp->db_txt_end - dp->db_txt_start;
if (head_end > text_start || dp->db_txt_start > size
|| dp->db_txt_end > size)
return;
state = ml_crypt_prepare(mfp, offset, TRUE);
if (state != NULL)
{
crypt_decode_inplace(state, text_start, text_len);
crypt_free_state(state);
}
}
}
static cryptstate_T *
ml_crypt_prepare(memfile_T *mfp, off_T offset, int reading)
{
buf_T *buf = mfp->mf_buffer;
char_u salt[50];
int method_nr;
char_u *key;
char_u *seed;
if (reading && mfp->mf_old_key != NULL)
{
method_nr = mfp->mf_old_cm;
key = mfp->mf_old_key;
seed = mfp->mf_old_seed;
}
else
{
method_nr = crypt_get_method_nr(buf);
key = buf->b_p_key;
seed = mfp->mf_seed;
}
if (*key == NUL)
return NULL;
if (method_nr == CRYPT_M_ZIP)
{
vim_snprintf((char *)salt, sizeof(salt), "%s%ld", key, (long)offset);
return crypt_create(method_nr, salt, NULL, 0, NULL, 0);
}
vim_snprintf((char *)salt, sizeof(salt), "%ld", (long)offset);
return crypt_create(method_nr, key, salt, (int)STRLEN(salt),
seed, MF_SEED_LEN);
}
#endif
#if defined(FEAT_BYTEOFF) || defined(PROTO)
#define MLCS_MAXL 800 // max no of lines in chunk
#define MLCS_MINL 400 // should be half of MLCS_MAXL
static void
ml_updatechunk(
buf_T *buf,
linenr_T line,
long len,
int updtype)
{
static buf_T *ml_upd_lastbuf = NULL;
static linenr_T ml_upd_lastline;
static linenr_T ml_upd_lastcurline;
static int ml_upd_lastcurix;
linenr_T curline = ml_upd_lastcurline;
int curix = ml_upd_lastcurix;
long size;
chunksize_T *curchnk;
int rest;
bhdr_T *hp;
DATA_BL *dp;
if (buf->b_ml.ml_usedchunks == -1 || len == 0)
return;
if (buf->b_ml.ml_chunksize == NULL)
{
buf->b_ml.ml_chunksize = ALLOC_MULT(chunksize_T, 100);
if (buf->b_ml.ml_chunksize == NULL)
{
buf->b_ml.ml_usedchunks = -1;
return;
}
buf->b_ml.ml_numchunks = 100;
buf->b_ml.ml_usedchunks = 1;
buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
buf->b_ml.ml_chunksize[0].mlcs_totalsize = 1;
}
if (updtype == ML_CHNK_UPDLINE && buf->b_ml.ml_line_count == 1)
{
buf->b_ml.ml_usedchunks = 1;
buf->b_ml.ml_chunksize[0].mlcs_numlines = 1;
buf->b_ml.ml_chunksize[0].mlcs_totalsize = (long)buf->b_ml.ml_line_len;
return;
}
if (buf != ml_upd_lastbuf || line != ml_upd_lastline + 1
|| updtype != ML_CHNK_ADDLINE)
{
for (curline = 1, curix = 0;
curix < buf->b_ml.ml_usedchunks - 1
&& line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines;
curix++)
curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
}
else if (curix < buf->b_ml.ml_usedchunks - 1
&& line >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
{
curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
curix++;
}
curchnk = buf->b_ml.ml_chunksize + curix;
if (updtype == ML_CHNK_DELLINE)
len = -len;
curchnk->mlcs_totalsize += len;
if (updtype == ML_CHNK_ADDLINE)
{
curchnk->mlcs_numlines++;
if (buf->b_ml.ml_usedchunks + 1 >= buf->b_ml.ml_numchunks)
{
chunksize_T *t_chunksize = buf->b_ml.ml_chunksize;
buf->b_ml.ml_numchunks = buf->b_ml.ml_numchunks * 3 / 2;
buf->b_ml.ml_chunksize = (chunksize_T *)
vim_realloc(buf->b_ml.ml_chunksize,
sizeof(chunksize_T) * buf->b_ml.ml_numchunks);
if (buf->b_ml.ml_chunksize == NULL)
{
vim_free(t_chunksize);
buf->b_ml.ml_usedchunks = -1;
return;
}
}
if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MAXL)
{
int count; int idx;
int end_idx;
int text_end;
int linecnt;
mch_memmove(buf->b_ml.ml_chunksize + curix + 1,
buf->b_ml.ml_chunksize + curix,
(buf->b_ml.ml_usedchunks - curix) *
sizeof(chunksize_T));
size = 0;
linecnt = 0;
while (curline < buf->b_ml.ml_line_count
&& linecnt < MLCS_MINL)
{
if ((hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
{
buf->b_ml.ml_usedchunks = -1;
return;
}
dp = (DATA_BL *)(hp->bh_data);
count = (long)(buf->b_ml.ml_locked_high) -
(long)(buf->b_ml.ml_locked_low) + 1;
idx = curline - buf->b_ml.ml_locked_low;
curline = buf->b_ml.ml_locked_high + 1;
rest = count - idx;
if (linecnt + rest > MLCS_MINL)
{
end_idx = idx + MLCS_MINL - linecnt - 1;
linecnt = MLCS_MINL;
}
else
{
end_idx = count - 1;
linecnt += rest;
}
#ifdef FEAT_PROP_POPUP
if (buf->b_has_textprop)
{
int i;
for (i = end_idx; i < idx; ++i)
size += (int)STRLEN((char_u *)dp + (dp->db_index[i] & DB_INDEX_MASK)) + 1;
}
else
#endif
{
if (idx == 0) text_end = dp->db_txt_end;
else
text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
size += text_end - ((dp->db_index[end_idx]) & DB_INDEX_MASK);
}
}
buf->b_ml.ml_chunksize[curix].mlcs_numlines = linecnt;
buf->b_ml.ml_chunksize[curix + 1].mlcs_numlines -= linecnt;
buf->b_ml.ml_chunksize[curix].mlcs_totalsize = size;
buf->b_ml.ml_chunksize[curix + 1].mlcs_totalsize -= size;
buf->b_ml.ml_usedchunks++;
ml_upd_lastbuf = NULL; return;
}
else if (buf->b_ml.ml_chunksize[curix].mlcs_numlines >= MLCS_MINL
&& curix == buf->b_ml.ml_usedchunks - 1
&& buf->b_ml.ml_line_count - line <= 1)
{
curchnk = buf->b_ml.ml_chunksize + curix + 1;
buf->b_ml.ml_usedchunks++;
if (line == buf->b_ml.ml_line_count)
{
curchnk->mlcs_numlines = 0;
curchnk->mlcs_totalsize = 0;
}
else
{
hp = ml_find_line(buf, buf->b_ml.ml_line_count, ML_FIND);
if (hp == NULL)
{
buf->b_ml.ml_usedchunks = -1;
return;
}
dp = (DATA_BL *)(hp->bh_data);
if (dp->db_line_count == 1)
rest = dp->db_txt_end - dp->db_txt_start;
else
rest =
((dp->db_index[dp->db_line_count - 2]) & DB_INDEX_MASK)
- dp->db_txt_start;
curchnk->mlcs_totalsize = rest;
curchnk->mlcs_numlines = 1;
curchnk[-1].mlcs_totalsize -= rest;
curchnk[-1].mlcs_numlines -= 1;
}
}
}
else if (updtype == ML_CHNK_DELLINE)
{
curchnk->mlcs_numlines--;
ml_upd_lastbuf = NULL; if (curix < (buf->b_ml.ml_usedchunks - 1)
&& (curchnk->mlcs_numlines + curchnk[1].mlcs_numlines)
<= MLCS_MINL)
{
curix++;
curchnk = buf->b_ml.ml_chunksize + curix;
}
else if (curix == 0 && curchnk->mlcs_numlines <= 0)
{
buf->b_ml.ml_usedchunks--;
mch_memmove(buf->b_ml.ml_chunksize, buf->b_ml.ml_chunksize + 1,
buf->b_ml.ml_usedchunks * sizeof(chunksize_T));
return;
}
else if (curix == 0 || (curchnk->mlcs_numlines > 10
&& (curchnk->mlcs_numlines + curchnk[-1].mlcs_numlines)
> MLCS_MINL))
{
return;
}
curchnk[-1].mlcs_numlines += curchnk->mlcs_numlines;
curchnk[-1].mlcs_totalsize += curchnk->mlcs_totalsize;
buf->b_ml.ml_usedchunks--;
if (curix < buf->b_ml.ml_usedchunks)
{
mch_memmove(buf->b_ml.ml_chunksize + curix,
buf->b_ml.ml_chunksize + curix + 1,
(buf->b_ml.ml_usedchunks - curix) *
sizeof(chunksize_T));
}
return;
}
ml_upd_lastbuf = buf;
ml_upd_lastline = line;
ml_upd_lastcurline = curline;
ml_upd_lastcurix = curix;
}
long
ml_find_line_or_offset(buf_T *buf, linenr_T lnum, long *offp)
{
linenr_T curline;
int curix;
long size;
bhdr_T *hp;
DATA_BL *dp;
int count; int idx;
int start_idx;
int text_end;
long offset;
int len;
int ffdos = (get_fileformat(buf) == EOL_DOS);
int extra = 0;
ml_flush_line(curbuf);
if (buf->b_ml.ml_usedchunks == -1
|| buf->b_ml.ml_chunksize == NULL
|| lnum < 0)
return -1;
if (offp == NULL)
offset = 0;
else
offset = *offp;
if (lnum == 0 && offset <= 0)
return 1;
curline = 1;
curix = size = 0;
while (curix < buf->b_ml.ml_usedchunks - 1
&& ((lnum != 0
&& lnum >= curline + buf->b_ml.ml_chunksize[curix].mlcs_numlines)
|| (offset != 0
&& offset > size + buf->b_ml.ml_chunksize[curix].mlcs_totalsize
+ ffdos * buf->b_ml.ml_chunksize[curix].mlcs_numlines)))
{
curline += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
size += buf->b_ml.ml_chunksize[curix].mlcs_totalsize;
if (offset && ffdos)
size += buf->b_ml.ml_chunksize[curix].mlcs_numlines;
curix++;
}
while ((lnum != 0 && curline < lnum) || (offset != 0 && size < offset))
{
if (curline > buf->b_ml.ml_line_count
|| (hp = ml_find_line(buf, curline, ML_FIND)) == NULL)
return -1;
dp = (DATA_BL *)(hp->bh_data);
count = (long)(buf->b_ml.ml_locked_high) -
(long)(buf->b_ml.ml_locked_low) + 1;
start_idx = idx = curline - buf->b_ml.ml_locked_low;
if (idx == 0) text_end = dp->db_txt_end;
else
text_end = ((dp->db_index[idx - 1]) & DB_INDEX_MASK);
if (lnum != 0)
{
if (curline + (count - idx) >= lnum)
idx += lnum - curline - 1;
else
idx = count - 1;
}
else
{
#ifdef FEAT_PROP_POPUP
size_t textprop_total = 0;
size_t textprop_size = 0;
char_u *l1, *l2;
#endif
extra = 0;
for (;;)
{
#ifdef FEAT_PROP_POPUP
if (buf->b_has_textprop)
{
l1 = (char_u *)dp + ((dp->db_index[idx]) & DB_INDEX_MASK);
l2 = (char_u *)dp + (idx == 0 ? dp->db_txt_end
: ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
textprop_size = (l2 - l1) - (STRLEN(l1) + 1);
}
#endif
if (!(offset >= size
+ text_end - (int)((dp->db_index[idx]) & DB_INDEX_MASK)
#ifdef FEAT_PROP_POPUP
- (long)(textprop_total + textprop_size)
#endif
+ ffdos))
break;
if (ffdos)
size++;
#ifdef FEAT_PROP_POPUP
textprop_total += textprop_size;
#endif
if (idx == count - 1)
{
extra = 1;
break;
}
idx++;
}
}
#ifdef FEAT_PROP_POPUP
if (buf->b_has_textprop)
{
int i;
len = 0;
for (i = start_idx; i <= idx; ++i)
len += (int)STRLEN((char_u *)dp
+ ((dp->db_index[i]) & DB_INDEX_MASK)) + 1;
}
else
#endif
len = text_end - ((dp->db_index[idx]) & DB_INDEX_MASK);
size += len;
if (offset != 0 && size >= offset)
{
if (size + ffdos == offset)
*offp = 0;
else if (idx == start_idx)
*offp = offset - size + len;
else
*offp = offset - size + len
- (text_end - ((dp->db_index[idx - 1]) & DB_INDEX_MASK));
curline += idx - start_idx + extra;
if (curline > buf->b_ml.ml_line_count)
return -1; return curline;
}
curline = buf->b_ml.ml_locked_high + 1;
}
if (lnum != 0)
{
if (ffdos)
size += lnum - 1;
if ((!buf->b_p_fixeol || buf->b_p_bin) && !buf->b_p_eol
&& lnum > buf->b_ml.ml_line_count)
size -= ffdos + 1;
}
return size;
}
void
goto_byte(long cnt)
{
long boff = cnt;
linenr_T lnum;
ml_flush_line(curbuf); setpcmark();
if (boff)
--boff;
lnum = ml_find_line_or_offset(curbuf, (linenr_T)0, &boff);
if (lnum < 1) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
curwin->w_curswant = MAXCOL;
coladvance((colnr_T)MAXCOL);
}
else
{
curwin->w_cursor.lnum = lnum;
curwin->w_cursor.col = (colnr_T)boff;
curwin->w_cursor.coladd = 0;
curwin->w_set_curswant = TRUE;
}
check_cursor();
if (has_mbyte)
mb_adjust_cursor();
}
#endif