0
0
mirror of https://github.com/vim/vim.git synced 2025-09-29 04:34:16 -04:00

Made reading/writing undo info a bit more robust.

This commit is contained in:
Bram Moolenaar
2010-05-30 20:40:11 +02:00
parent fd3e5dc97f
commit 6ed8ed84f9
6 changed files with 241 additions and 166 deletions

View File

@@ -102,6 +102,9 @@ static void u_freeentry __ARGS((u_entry_T *, long));
#ifdef FEAT_PERSISTENT_UNDO
static void corruption_error __ARGS((char *msg, char_u *file_name));
static void u_free_uhp __ARGS((u_header_T *uhp));
static int serialize_header __ARGS((FILE *fp, buf_T *buf, char_u *hash));
static int serialize_uhp __ARGS((FILE *fp, u_header_T *uhp));
static u_header_T *unserialize_uhp __ARGS((FILE *fp, char_u *file_name));
static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp));
static u_entry_T *unserialize_uep __ARGS((FILE *fp, int *error, char_u *file_name));
static void serialize_pos __ARGS((pos_T pos, FILE *fp));
@@ -797,6 +800,173 @@ u_free_uhp(uhp)
vim_free(uhp);
}
static int
serialize_header(fp, buf, hash)
FILE *fp;
buf_T *buf;
char_u *hash;
{
int len;
/* Start writing, first the magic marker and undo info version. */
if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
return FAIL;
put_bytes(fp, (long_u)UF_VERSION, 2);
/* Write a hash of the buffer text, so that we can verify it is still the
* same when reading the buffer text. */
if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
return FAIL;
/* buffer-specific data */
put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
put_bytes(fp, (long_u)len, 4);
if (len > 0 && fwrite(buf->b_u_line_ptr, (size_t)len, (size_t)1, fp) != 1)
return FAIL;
put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
/* Undo structures header data */
put_header_ptr(fp, buf->b_u_oldhead);
put_header_ptr(fp, buf->b_u_newhead);
put_header_ptr(fp, buf->b_u_curhead);
put_bytes(fp, (long_u)buf->b_u_numhead, 4);
put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
put_time(fp, buf->b_u_seq_time);
return OK;
}
static int
serialize_uhp(fp, uhp)
FILE *fp;
u_header_T *uhp;
{
int i;
u_entry_T *uep;
if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
return FAIL;
put_header_ptr(fp, uhp->uh_next);
put_header_ptr(fp, uhp->uh_prev);
put_header_ptr(fp, uhp->uh_alt_next);
put_header_ptr(fp, uhp->uh_alt_prev);
put_bytes(fp, uhp->uh_seq, 4);
serialize_pos(uhp->uh_cursor, fp);
#ifdef FEAT_VIRTUALEDIT
put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
#else
put_bytes(fp, (long_u)0, 4);
#endif
put_bytes(fp, (long_u)uhp->uh_flags, 2);
/* Assume NMARKS will stay the same. */
for (i = 0; i < NMARKS; ++i)
serialize_pos(uhp->uh_namedm[i], fp);
#ifdef FEAT_VISUAL
serialize_visualinfo(&uhp->uh_visual, fp);
#else
{
visualinfo_T info;
memset(&info, 0, sizeof(visualinfo_T));
serialize_visualinfo(&info, fp);
}
#endif
put_time(fp, uhp->uh_time);
/* Write all the entries. */
for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
{
put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
if (serialize_uep(uep, fp) == FAIL)
return FAIL;
}
put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
return OK;
}
static u_header_T *
unserialize_uhp(fp, file_name)
FILE *fp;
char_u *file_name;
{
u_header_T *uhp;
int i;
u_entry_T *uep, *last_uep;
int c;
int error;
uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
if (uhp == NULL)
return NULL;
vim_memset(uhp, 0, sizeof(u_header_T));
#ifdef U_DEBUG
uhp->uh_magic = UH_MAGIC;
#endif
/* We're not actually trying to store pointers here. We're just storing
* uh_seq numbers of the header pointed to, so we can swizzle them into
* pointers later - hence the type cast. */
uhp->uh_next = (u_header_T *)(long_u)get4c(fp);
uhp->uh_prev = (u_header_T *)(long_u)get4c(fp);
uhp->uh_alt_next = (u_header_T *)(long_u)get4c(fp);
uhp->uh_alt_prev = (u_header_T *)(long_u)get4c(fp);
uhp->uh_seq = get4c(fp);
if (uhp->uh_seq <= 0)
{
corruption_error("uh_seq", file_name);
vim_free(uhp);
return NULL;
}
unserialize_pos(&uhp->uh_cursor, fp);
#ifdef FEAT_VIRTUALEDIT
uhp->uh_cursor_vcol = get4c(fp);
#else
(void)get4c(fp);
#endif
uhp->uh_flags = get2c(fp);
for (i = 0; i < NMARKS; ++i)
unserialize_pos(&uhp->uh_namedm[i], fp);
#ifdef FEAT_VISUAL
unserialize_visualinfo(&uhp->uh_visual, fp);
#else
{
visualinfo_T info;
unserialize_visualinfo(&info, fp);
}
#endif
uhp->uh_time = get8ctime(fp);
/* Unserialize the uep list. */
last_uep = NULL;
while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
{
error = FALSE;
uep = unserialize_uep(fp, &error, file_name);
if (last_uep == NULL)
uhp->uh_entry = uep;
else
last_uep->ue_next = uep;
last_uep = uep;
if (uep == NULL || error)
{
u_free_uhp(uhp);
return NULL;
}
}
if (c != UF_ENTRY_END_MAGIC)
{
corruption_error("entry end", file_name);
u_free_uhp(uhp);
return NULL;
}
return uhp;
}
/*
* Serialize "uep" to "fp".
*/
@@ -830,7 +1000,6 @@ unserialize_uep(fp, error, file_name)
char_u *file_name;
{
int i;
int j;
u_entry_T *uep;
char_u **array;
char_u *line;
@@ -865,7 +1034,7 @@ unserialize_uep(fp, error, file_name)
{
line_len = get4c(fp);
if (line_len >= 0)
line = (char_u *)U_ALLOC_LINE(line_len + 1);
line = read_string(fp, line_len);
else
{
line = NULL;
@@ -876,9 +1045,6 @@ unserialize_uep(fp, error, file_name)
*error = TRUE;
return uep;
}
for (j = 0; j < line_len; j++)
line[j] = getc(fp);
line[j] = NUL;
array[i] = line;
}
return uep;
@@ -910,9 +1076,15 @@ unserialize_pos(pos, fp)
FILE *fp;
{
pos->lnum = get4c(fp);
if (pos->lnum < 0)
pos->lnum = 0;
pos->col = get4c(fp);
if (pos->col < 0)
pos->col = 0;
#ifdef FEAT_VIRTUALEDIT
pos->coladd = get4c(fp);
if (pos->coladd < 0)
pos->coladd = 0;
#else
(void)get4c(fp);
#endif
@@ -975,9 +1147,8 @@ u_write_undo(name, forceit, buf, hash)
char_u *hash;
{
u_header_T *uhp;
u_entry_T *uep;
char_u *file_name;
int str_len, i, mark;
int mark;
#ifdef U_DEBUG
int headers_written = 0;
#endif
@@ -1069,7 +1240,8 @@ u_write_undo(name, forceit, buf, hash)
{
if (name == NULL)
verbose_enter();
smsg((char_u *)_("Will not overwrite, this is not an undo file: %s"),
smsg((char_u *)
_("Will not overwrite, this is not an undo file: %s"),
file_name);
if (name == NULL)
verbose_leave();
@@ -1083,7 +1255,7 @@ u_write_undo(name, forceit, buf, hash)
/* If there is no undo information at all, quit here after deleting any
* existing undo file. */
if (buf->b_u_numhead == 0)
if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL)
{
if (p_verbose > 0)
verb_msg((char_u *)_("Skipping undo file write, noting to undo"));
@@ -1141,36 +1313,11 @@ u_write_undo(name, forceit, buf, hash)
/* Undo must be synced. */
u_sync(TRUE);
/* Start writing, first the undo file header. */
if (fwrite(UF_START_MAGIC, (size_t)UF_START_MAGIC_LEN, (size_t)1, fp) != 1)
/*
* Write the header.
*/
if (serialize_header(fp, buf, hash) == FAIL)
goto write_error;
put_bytes(fp, (long_u)UF_VERSION, 2);
/* Write a hash of the buffer text, so that we can verify it is still the
* same when reading the buffer text. */
if (fwrite(hash, (size_t)UNDO_HASH_SIZE, (size_t)1, fp) != 1)
goto write_error;
put_bytes(fp, (long_u)buf->b_ml.ml_line_count, 4);
/* Begin undo data for U */
str_len = buf->b_u_line_ptr != NULL ? (int)STRLEN(buf->b_u_line_ptr) : 0;
put_bytes(fp, (long_u)str_len, 4);
if (str_len > 0 && fwrite(buf->b_u_line_ptr, (size_t)str_len,
(size_t)1, fp) != 1)
goto write_error;
put_bytes(fp, (long_u)buf->b_u_line_lnum, 4);
put_bytes(fp, (long_u)buf->b_u_line_colnr, 4);
/* Begin general undo data */
put_header_ptr(fp, buf->b_u_oldhead);
put_header_ptr(fp, buf->b_u_newhead);
put_header_ptr(fp, buf->b_u_curhead);
put_bytes(fp, (long_u)buf->b_u_numhead, 4);
put_bytes(fp, (long_u)buf->b_u_seq_last, 4);
put_bytes(fp, (long_u)buf->b_u_seq_cur, 4);
put_time(fp, buf->b_u_seq_time);
/*
* Iteratively serialize UHPs and their UEPs from the top down.
@@ -1186,48 +1333,11 @@ u_write_undo(name, forceit, buf, hash)
#ifdef U_DEBUG
++headers_written;
#endif
if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
if (serialize_uhp(fp, uhp) == FAIL)
goto write_error;
put_header_ptr(fp, uhp->uh_next);
put_header_ptr(fp, uhp->uh_prev);
put_header_ptr(fp, uhp->uh_alt_next);
put_header_ptr(fp, uhp->uh_alt_prev);
put_bytes(fp, uhp->uh_seq, 4);
serialize_pos(uhp->uh_cursor, fp);
#ifdef FEAT_VIRTUALEDIT
put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
#else
put_bytes(fp, (long_u)0, 4);
#endif
put_bytes(fp, (long_u)uhp->uh_flags, 2);
/* Assume NMARKS will stay the same. */
for (i = 0; i < NMARKS; ++i)
serialize_pos(uhp->uh_namedm[i], fp);
#ifdef FEAT_VISUAL
serialize_visualinfo(&uhp->uh_visual, fp);
#else
{
visualinfo_T info;
memset(&info, 0, sizeof(visualinfo_T));
serialize_visualinfo(&info, fp);
}
#endif
put_time(fp, uhp->uh_time);
/* Write all the entries. */
for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next)
{
put_bytes(fp, (long_u)UF_ENTRY_MAGIC, 2);
if (serialize_uep(uep, fp) == FAIL)
goto write_error;
}
put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
}
/* Now walk through the tree - algorithm from undo_time */
/* Now walk through the tree - algorithm from undo_time(). */
if (uhp->uh_prev != NULL && uhp->uh_prev->uh_walk != mark)
uhp = uhp->uh_prev;
else if (uhp->uh_alt_next != NULL && uhp->uh_alt_next->uh_walk != mark)
@@ -1255,6 +1365,8 @@ write_error:
EMSG2(_("E829: write error in undo file: %s"), file_name);
#if defined(MACOS_CLASSIC) || defined(WIN3264)
/* Copy file attributes; for systems where this can only be done after
* closing the file. */
if (buf->b_ffname != NULL)
(void)mch_copy_file_attribute(buf->b_ffname, file_name);
#endif
@@ -1282,9 +1394,10 @@ theend:
* "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
*/
void
u_read_undo(name, hash)
u_read_undo(name, hash, orig_name)
char_u *name;
char_u *hash;
char_u *orig_name;
{
char_u *file_name;
FILE *fp;
@@ -1301,7 +1414,6 @@ u_read_undo(name, hash)
time_t seq_time;
int i, j;
int c;
u_entry_T *uep, *last_uep;
u_header_T *uhp;
u_header_T **uhp_table = NULL;
char_u read_hash[UNDO_HASH_SIZE];
@@ -1309,12 +1421,34 @@ u_read_undo(name, hash)
#ifdef U_DEBUG
int *uhp_table_used;
#endif
#ifdef UNIX
struct stat st_orig;
struct stat st_undo;
#endif
if (name == NULL)
{
file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
if (file_name == NULL)
return;
#ifdef UNIX
/* For safety we only read an undo file if the owner is equal to the
* owner of the text file. */
if (mch_stat((char *)orig_name, &st_orig) >= 0
&& mch_stat((char *)file_name, &st_undo) >= 0
&& st_orig.st_uid != st_undo.st_uid)
{
if (p_verbose > 0)
{
verbose_enter();
smsg((char_u *)_("Not reading undo file, owner differs: %s"),
file_name);
verbose_leave();
}
return;
}
#endif
}
else
file_name = name;
@@ -1325,6 +1459,7 @@ u_read_undo(name, hash)
smsg((char_u *)_("Reading undo file: %s"), file_name);
verbose_leave();
}
fp = mch_fopen((char *)file_name, "r");
if (fp == NULL)
{
@@ -1362,27 +1497,27 @@ u_read_undo(name, hash)
{
if (name == NULL)
verbose_enter();
give_warning((char_u *)_("File contents changed, cannot use undo info"), TRUE);
give_warning((char_u *)
_("File contents changed, cannot use undo info"), TRUE);
if (name == NULL)
verbose_leave();
}
goto error;
}
/* Begin undo data for U */
/* Read undo data for "U" command. */
str_len = get4c(fp);
if (str_len < 0)
goto error;
else if (str_len > 0)
{
if ((line_ptr = U_ALLOC_LINE(str_len + 1)) == NULL)
goto error;
for (i = 0; i < str_len; i++)
line_ptr[i] = (char_u)getc(fp);
line_ptr[i] = NUL;
}
if (str_len > 0)
line_ptr = read_string(fp, str_len);
line_lnum = (linenr_T)get4c(fp);
line_colnr = (colnr_T)get4c(fp);
if (line_lnum < 0 || line_colnr < 0)
{
corruption_error("line lnum/col", file_name);
goto error;
}
/* Begin general undo data */
old_header_seq = get4c(fp);
@@ -1409,76 +1544,13 @@ u_read_undo(name, hash)
{
if (num_read_uhps >= num_head)
{
corruption_error("num_head", file_name);
corruption_error("num_head too small", file_name);
goto error;
}
uhp = (u_header_T *)U_ALLOC_LINE(sizeof(u_header_T));
if (uhp == NULL)
goto error;
vim_memset(uhp, 0, sizeof(u_header_T));
#ifdef U_DEBUG
uhp->uh_magic = UH_MAGIC;
#endif
/* We're not actually trying to store pointers here. We're just storing
* IDs so we can swizzle them into pointers later - hence the type
* cast. */
uhp->uh_next = (u_header_T *)(long_u)get4c(fp);
uhp->uh_prev = (u_header_T *)(long_u)get4c(fp);
uhp->uh_alt_next = (u_header_T *)(long_u)get4c(fp);
uhp->uh_alt_prev = (u_header_T *)(long_u)get4c(fp);
uhp->uh_seq = get4c(fp);
if (uhp->uh_seq <= 0)
{
corruption_error("uh_seq", file_name);
vim_free(uhp);
goto error;
}
uhp->uh_walk = 0;
unserialize_pos(&uhp->uh_cursor, fp);
#ifdef FEAT_VIRTUALEDIT
uhp->uh_cursor_vcol = get4c(fp);
#else
(void)get4c(fp);
#endif
uhp->uh_flags = get2c(fp);
for (i = 0; i < NMARKS; ++i)
unserialize_pos(&uhp->uh_namedm[i], fp);
#ifdef FEAT_VISUAL
unserialize_visualinfo(&uhp->uh_visual, fp);
#else
{
visualinfo_T info;
unserialize_visualinfo(&info, fp);
}
#endif
uhp->uh_time = get8ctime(fp);
/* Unserialize the uep list. */
last_uep = NULL;
while ((c = get2c(fp)) == UF_ENTRY_MAGIC)
{
int error = FALSE;
uep = unserialize_uep(fp, &error, file_name);
if (last_uep == NULL)
uhp->uh_entry = uep;
else
last_uep->ue_next = uep;
last_uep = uep;
if (uep == NULL || error)
{
u_free_uhp(uhp);
goto error;
}
}
if (c != UF_ENTRY_END_MAGIC)
{
corruption_error("entry end", file_name);
u_free_uhp(uhp);
goto error;
}
uhp = unserialize_uhp(fp, file_name);
if (uhp == NULL)
goto error;
uhp_table[num_read_uhps++] = uhp;
}
@@ -1487,7 +1559,6 @@ u_read_undo(name, hash)
corruption_error("num_head", file_name);
goto error;
}
if (c != UF_HEADER_END_MAGIC)
{
corruption_error("end marker", file_name);
@@ -1502,8 +1573,8 @@ u_read_undo(name, hash)
# define SET_FLAG(j)
#endif
/* We have put all of the uhps into a table. Now we iterate through the
* table and swizzle each sequence number we've stored in uh_* into a
/* We have put all of the headers into a table. Now we iterate through the
* table and swizzle each sequence number we have stored in uh_* into a
* pointer corresponding to the header with that sequence number. */
for (i = 0; i < num_head; i++)
{
@@ -1519,7 +1590,6 @@ u_read_undo(name, hash)
corruption_error("duplicate uh_seq", file_name);
goto error;
}
if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
{
uhp->uh_next = uhp_table[j];