aboutsummaryrefslogtreecommitdiff
path: root/src/undo.c
diff options
context:
space:
mode:
authorEliseo Martínez <eliseomarmol@gmail.com>2014-05-12 02:25:17 +0200
committerEliseo Martínez <eliseomarmol@gmail.com>2014-05-15 20:46:01 +0200
commitda51dc9cf202772f60bd2da975dbef257bd9237c (patch)
tree5c16b93238a153f55634e9323077f30c8133970c /src/undo.c
parentffe61e5ba1721340ca51d56bae3ddaca415fb5bc (diff)
downloadrneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.gz
rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.tar.bz2
rneovim-da51dc9cf202772f60bd2da975dbef257bd9237c.zip
Introduce nvim namespace: Move files.
Move files from src/ to src/nvim/. - src/nvim/ becomes the new root dir for nvim executable sources. - src/libnvim/ is planned to become root dir of the neovim library.
Diffstat (limited to 'src/undo.c')
-rw-r--r--src/undo.c2880
1 files changed, 0 insertions, 2880 deletions
diff --git a/src/undo.c b/src/undo.c
deleted file mode 100644
index 47ce8ee8da..0000000000
--- a/src/undo.c
+++ /dev/null
@@ -1,2880 +0,0 @@
-/*
- * VIM - Vi IMproved by Bram Moolenaar
- *
- * Do ":help uganda" in Vim to read copying and usage conditions.
- * Do ":help credits" in Vim to see a list of people who contributed.
- * See README.txt for an overview of the Vim source code.
- */
-
-/*
- * undo.c: multi level undo facility
- *
- * The saved lines are stored in a list of lists (one for each buffer):
- *
- * b_u_oldhead------------------------------------------------+
- * |
- * V
- * +--------------+ +--------------+ +--------------+
- * b_u_newhead--->| u_header | | u_header | | u_header |
- * | uh_next------>| uh_next------>| uh_next---->NULL
- * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
- * | uh_entry | | uh_entry | | uh_entry |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ +--------------+ +--------------+
- * | u_entry | | u_entry | | u_entry |
- * | ue_next | | ue_next | | ue_next |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ NULL NULL
- * | u_entry |
- * | ue_next |
- * +--------|-----+
- * |
- * V
- * etc.
- *
- * Each u_entry list contains the information for one undo or redo.
- * curbuf->b_u_curhead points to the header of the last undo (the next redo),
- * or is NULL if nothing has been undone (end of the branch).
- *
- * For keeping alternate undo/redo branches the uh_alt field is used. Thus at
- * each point in the list a branch may appear for an alternate to redo. The
- * uh_seq field is numbered sequentially to be able to find a newer or older
- * branch.
- *
- * +---------------+ +---------------+
- * b_u_oldhead --->| u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next ----> NULL
- * NULL <----- uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next | | uh_alt_next |
- * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * NULL +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next |
- * | uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * etc. etc.
- *
- *
- * All data is allocated and will all be freed when the buffer is unloaded.
- */
-
-/* Uncomment the next line for including the u_check() function. This warns
- * for errors in the debug information. */
-/* #define U_DEBUG 1 */
-#define UH_MAGIC 0x18dade /* value for uh_magic when in use */
-#define UE_MAGIC 0xabc123 /* value for ue_magic when in use */
-
-#include <string.h>
-
-#include "vim.h"
-#include "undo.h"
-#include "edit.h"
-#include "eval.h"
-#include "fileio.h"
-#include "fold.h"
-#include "mark.h"
-#include "memline.h"
-#include "message.h"
-#include "misc1.h"
-#include "misc2.h"
-#include "memory.h"
-#include "crypt.h"
-#include "garray.h"
-#include "option.h"
-#include "os_unix.h"
-#include "path.h"
-#include "quickfix.h"
-#include "screen.h"
-#include "sha256.h"
-#include "os/os.h"
-#include "os/time.h"
-
-static long get_undolevel(void);
-static void u_unch_branch(u_header_T *uhp);
-static u_entry_T *u_get_headentry(void);
-static void u_getbot(void);
-static void u_doit(int count);
-static void u_undoredo(int undo);
-static void u_undo_end(int did_undo, int absolute);
-static void u_add_time(char_u *buf, size_t buflen, time_t tt);
-static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
-static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp);
-static void u_freeentries(buf_T *buf, u_header_T *uhp,
- u_header_T **uhpp);
-static void u_freeentry(u_entry_T *, long);
-static void corruption_error(char *mesg, char_u *file_name);
-static void u_free_uhp(u_header_T *uhp);
-static size_t fwrite_crypt(buf_T *buf, char_u *ptr, size_t len,
- FILE *fp);
-static char_u *read_string_decrypt(buf_T *buf, FILE *fd, int len);
-static int serialize_header(FILE *fp, buf_T *buf, char_u *hash);
-static int serialize_uhp(FILE *fp, buf_T *buf, u_header_T *uhp);
-static u_header_T *unserialize_uhp(FILE *fp, char_u *file_name);
-static int serialize_uep(FILE *fp, buf_T *buf, u_entry_T *uep);
-static u_entry_T *unserialize_uep(FILE *fp, int *error,
- char_u *file_name);
-static void serialize_pos(pos_T pos, FILE *fp);
-static void unserialize_pos(pos_T *pos, FILE *fp);
-static void serialize_visualinfo(visualinfo_T *info, FILE *fp);
-static void unserialize_visualinfo(visualinfo_T *info, FILE *fp);
-static void put_header_ptr(FILE *fp, u_header_T *uhp);
-
-static char_u *u_save_line(linenr_T);
-
-/* used in undo_end() to report number of added and deleted lines */
-static long u_newcount, u_oldcount;
-
-/*
- * When 'u' flag included in 'cpoptions', we behave like vi. Need to remember
- * the action that "u" should do.
- */
-static int undo_undoes = FALSE;
-
-static int lastmark = 0;
-
-#if defined(U_DEBUG) || defined(PROTO)
-/*
- * Check the undo structures for being valid. Print a warning when something
- * looks wrong.
- */
-static int seen_b_u_curhead;
-static int seen_b_u_newhead;
-static int header_count;
-
-static void u_check_tree(u_header_T *uhp,
- u_header_T *exp_uh_next,
- u_header_T *exp_uh_alt_prev) {
- u_entry_T *uep;
-
- if (uhp == NULL)
- return;
- ++header_count;
- if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1) {
- EMSG("b_u_curhead found twice (looping?)");
- return;
- }
- if (uhp == curbuf->b_u_newhead && ++seen_b_u_newhead > 1) {
- EMSG("b_u_newhead found twice (looping?)");
- return;
- }
-
- if (uhp->uh_magic != UH_MAGIC)
- EMSG("uh_magic wrong (may be using freed memory)");
- else {
- /* Check pointers back are correct. */
- if (uhp->uh_next.ptr != exp_uh_next) {
- EMSG("uh_next wrong");
- smsg((char_u *)"expected: 0x%x, actual: 0x%x",
- exp_uh_next, uhp->uh_next.ptr);
- }
- if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev) {
- EMSG("uh_alt_prev wrong");
- smsg((char_u *)"expected: 0x%x, actual: 0x%x",
- exp_uh_alt_prev, uhp->uh_alt_prev.ptr);
- }
-
- /* Check the undo tree at this header. */
- for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) {
- if (uep->ue_magic != UE_MAGIC) {
- EMSG("ue_magic wrong (may be using freed memory)");
- break;
- }
- }
-
- /* Check the next alt tree. */
- u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp);
-
- /* Check the next header in this branch. */
- u_check_tree(uhp->uh_prev.ptr, uhp, NULL);
- }
-}
-
-static void u_check(int newhead_may_be_NULL) {
- seen_b_u_newhead = 0;
- seen_b_u_curhead = 0;
- header_count = 0;
-
- u_check_tree(curbuf->b_u_oldhead, NULL, NULL);
-
- if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL
- && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL))
- EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead);
- if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0)
- EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead);
- if (header_count != curbuf->b_u_numhead) {
- EMSG("b_u_numhead invalid");
- smsg((char_u *)"expected: %" PRId64 ", actual: %" PRId64,
- (int64_t)header_count, (int64_t)curbuf->b_u_numhead);
- }
-}
-
-#endif
-
-/*
- * Save the current line for both the "u" and "U" command.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns OK or FAIL.
- */
-int u_save_cursor(void)
-{
- return u_save((linenr_T)(curwin->w_cursor.lnum - 1),
- (linenr_T)(curwin->w_cursor.lnum + 1));
-}
-
-/*
- * Save the lines between "top" and "bot" for both the "u" and "U" command.
- * "top" may be 0 and bot may be curbuf->b_ml.ml_line_count + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
-int u_save(linenr_T top, linenr_T bot)
-{
- if (undo_off)
- return OK;
-
- if (top > curbuf->b_ml.ml_line_count
- || top >= bot
- || bot > curbuf->b_ml.ml_line_count + 1)
- return FALSE; /* rely on caller to do error messages */
-
- if (top + 2 == bot)
- u_saveline((linenr_T)(top + 1));
-
- return u_savecommon(top, bot, (linenr_T)0, FALSE);
-}
-
-/*
- * Save the line "lnum" (used by ":s" and "~" command).
- * The line is replaced, so the new bottom line is lnum + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
-int u_savesub(linenr_T lnum)
-{
- if (undo_off)
- return OK;
-
- return u_savecommon(lnum - 1, lnum + 1, lnum + 1, FALSE);
-}
-
-/*
- * A new line is inserted before line "lnum" (used by :s command).
- * The line is inserted, so the new bottom line is lnum + 1.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
-int u_inssub(linenr_T lnum)
-{
- if (undo_off)
- return OK;
-
- return u_savecommon(lnum - 1, lnum, lnum + 1, FALSE);
-}
-
-/*
- * Save the lines "lnum" - "lnum" + nlines (used by delete command).
- * The lines are deleted, so the new bottom line is lnum, unless the buffer
- * becomes empty.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
-int u_savedel(linenr_T lnum, long nlines)
-{
- if (undo_off)
- return OK;
-
- return u_savecommon(lnum - 1, lnum + nlines,
- nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, FALSE);
-}
-
-/*
- * Return TRUE when undo is allowed. Otherwise give an error message and
- * return FALSE.
- */
-int undo_allowed(void)
-{
- /* Don't allow changes when 'modifiable' is off. */
- if (!curbuf->b_p_ma) {
- EMSG(_(e_modifiable));
- return FALSE;
- }
-
-#ifdef HAVE_SANDBOX
- /* In the sandbox it's not allowed to change the text. */
- if (sandbox != 0) {
- EMSG(_(e_sandbox));
- return FALSE;
- }
-#endif
-
- /* Don't allow changes in the buffer while editing the cmdline. The
- * caller of getcmdline() may get confused. */
- if (textlock != 0) {
- EMSG(_(e_secure));
- return FALSE;
- }
-
- return TRUE;
-}
-
-/*
- * Get the undolevle value for the current buffer.
- */
-static long get_undolevel(void)
-{
- if (curbuf->b_p_ul == NO_LOCAL_UNDOLEVEL)
- return p_ul;
- return curbuf->b_p_ul;
-}
-
-/*
- * Common code for various ways to save text before a change.
- * "top" is the line above the first changed line.
- * "bot" is the line below the last changed line.
- * "newbot" is the new bottom line. Use zero when not known.
- * "reload" is TRUE when saving for a buffer reload.
- * Careful: may trigger autocommands that reload the buffer.
- * Returns FAIL when lines could not be saved, OK otherwise.
- */
-int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload)
-{
- linenr_T lnum;
- long i;
- u_header_T *uhp;
- u_header_T *old_curhead;
- u_entry_T *uep;
- u_entry_T *prev_uep;
- long size;
-
- if (!reload) {
- /* When making changes is not allowed return FAIL. It's a crude way
- * to make all change commands fail. */
- if (!undo_allowed())
- return FAIL;
-
-
- /*
- * Saving text for undo means we are going to make a change. Give a
- * warning for a read-only file before making the change, so that the
- * FileChangedRO event can replace the buffer with a read-write version
- * (e.g., obtained from a source control system).
- */
- change_warning(0);
- if (bot > curbuf->b_ml.ml_line_count + 1) {
- /* This happens when the FileChangedRO autocommand changes the
- * file in a way it becomes shorter. */
- EMSG(_("E881: Line count changed unexpectedly"));
- return FAIL;
- }
- }
-
-#ifdef U_DEBUG
- u_check(FALSE);
-#endif
-
- size = bot - top - 1;
-
- /*
- * If curbuf->b_u_synced == TRUE make a new header.
- */
- if (curbuf->b_u_synced) {
- /* Need to create new entry in b_changelist. */
- curbuf->b_new_change = TRUE;
-
- if (get_undolevel() >= 0) {
- /*
- * Make a new header entry. Do this first so that we don't mess
- * up the undo info when out of memory.
- */
- uhp = xmalloc(sizeof(u_header_T));
-#ifdef U_DEBUG
- uhp->uh_magic = UH_MAGIC;
-#endif
- } else
- uhp = NULL;
-
- /*
- * If we undid more than we redid, move the entry lists before and
- * including curbuf->b_u_curhead to an alternate branch.
- */
- old_curhead = curbuf->b_u_curhead;
- if (old_curhead != NULL) {
- curbuf->b_u_newhead = old_curhead->uh_next.ptr;
- curbuf->b_u_curhead = NULL;
- }
-
- /*
- * free headers to keep the size right
- */
- while (curbuf->b_u_numhead > get_undolevel()
- && curbuf->b_u_oldhead != NULL) {
- u_header_T *uhfree = curbuf->b_u_oldhead;
-
- if (uhfree == old_curhead)
- /* Can't reconnect the branch, delete all of it. */
- u_freebranch(curbuf, uhfree, &old_curhead);
- else if (uhfree->uh_alt_next.ptr == NULL)
- /* There is no branch, only free one header. */
- u_freeheader(curbuf, uhfree, &old_curhead);
- else {
- /* Free the oldest alternate branch as a whole. */
- while (uhfree->uh_alt_next.ptr != NULL)
- uhfree = uhfree->uh_alt_next.ptr;
- u_freebranch(curbuf, uhfree, &old_curhead);
- }
-#ifdef U_DEBUG
- u_check(TRUE);
-#endif
- }
-
- if (uhp == NULL) { /* no undo at all */
- if (old_curhead != NULL)
- u_freebranch(curbuf, old_curhead, NULL);
- curbuf->b_u_synced = FALSE;
- return OK;
- }
-
- uhp->uh_prev.ptr = NULL;
- uhp->uh_next.ptr = curbuf->b_u_newhead;
- uhp->uh_alt_next.ptr = old_curhead;
- if (old_curhead != NULL) {
- uhp->uh_alt_prev.ptr = old_curhead->uh_alt_prev.ptr;
- if (uhp->uh_alt_prev.ptr != NULL)
- uhp->uh_alt_prev.ptr->uh_alt_next.ptr = uhp;
- old_curhead->uh_alt_prev.ptr = uhp;
- if (curbuf->b_u_oldhead == old_curhead)
- curbuf->b_u_oldhead = uhp;
- } else
- uhp->uh_alt_prev.ptr = NULL;
- if (curbuf->b_u_newhead != NULL)
- curbuf->b_u_newhead->uh_prev.ptr = uhp;
-
- uhp->uh_seq = ++curbuf->b_u_seq_last;
- curbuf->b_u_seq_cur = uhp->uh_seq;
- uhp->uh_time = time(NULL);
- uhp->uh_save_nr = 0;
- curbuf->b_u_time_cur = uhp->uh_time + 1;
-
- uhp->uh_walk = 0;
- uhp->uh_entry = NULL;
- uhp->uh_getbot_entry = NULL;
- uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */
- if (virtual_active() && curwin->w_cursor.coladd > 0)
- uhp->uh_cursor_vcol = getviscol();
- else
- uhp->uh_cursor_vcol = -1;
-
- /* save changed and buffer empty flag for undo */
- uhp->uh_flags = (curbuf->b_changed ? UH_CHANGED : 0) +
- ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0);
-
- /* save named marks and Visual marks for undo */
- memmove(uhp->uh_namedm, curbuf->b_namedm, sizeof(pos_T) * NMARKS);
- uhp->uh_visual = curbuf->b_visual;
-
- curbuf->b_u_newhead = uhp;
- if (curbuf->b_u_oldhead == NULL)
- curbuf->b_u_oldhead = uhp;
- ++curbuf->b_u_numhead;
- } else {
- if (get_undolevel() < 0) /* no undo at all */
- return OK;
-
- /*
- * When saving a single line, and it has been saved just before, it
- * doesn't make sense saving it again. Saves a lot of memory when
- * making lots of changes inside the same line.
- * This is only possible if the previous change didn't increase or
- * decrease the number of lines.
- * Check the ten last changes. More doesn't make sense and takes too
- * long.
- */
- if (size == 1) {
- uep = u_get_headentry();
- prev_uep = NULL;
- for (i = 0; i < 10; ++i) {
- if (uep == NULL)
- break;
-
- /* If lines have been inserted/deleted we give up.
- * Also when the line was included in a multi-line save. */
- if ((curbuf->b_u_newhead->uh_getbot_entry != uep
- ? (uep->ue_top + uep->ue_size + 1
- != (uep->ue_bot == 0
- ? curbuf->b_ml.ml_line_count + 1
- : uep->ue_bot))
- : uep->ue_lcount != curbuf->b_ml.ml_line_count)
- || (uep->ue_size > 1
- && top >= uep->ue_top
- && top + 2 <= uep->ue_top + uep->ue_size + 1))
- break;
-
- /* If it's the same line we can skip saving it again. */
- if (uep->ue_size == 1 && uep->ue_top == top) {
- if (i > 0) {
- /* It's not the last entry: get ue_bot for the last
- * entry now. Following deleted/inserted lines go to
- * the re-used entry. */
- u_getbot();
- curbuf->b_u_synced = FALSE;
-
- /* Move the found entry to become the last entry. The
- * order of undo/redo doesn't matter for the entries
- * we move it over, since they don't change the line
- * count and don't include this line. It does matter
- * for the found entry if the line count is changed by
- * the executed command. */
- prev_uep->ue_next = uep->ue_next;
- uep->ue_next = curbuf->b_u_newhead->uh_entry;
- curbuf->b_u_newhead->uh_entry = uep;
- }
-
- /* The executed command may change the line count. */
- if (newbot != 0)
- uep->ue_bot = newbot;
- else if (bot > curbuf->b_ml.ml_line_count)
- uep->ue_bot = 0;
- else {
- uep->ue_lcount = curbuf->b_ml.ml_line_count;
- curbuf->b_u_newhead->uh_getbot_entry = uep;
- }
- return OK;
- }
- prev_uep = uep;
- uep = uep->ue_next;
- }
- }
-
- /* find line number for ue_bot for previous u_save() */
- u_getbot();
- }
-
- /*
- * add lines in front of entry list
- */
- uep = xmalloc(sizeof(u_entry_T));
- memset(uep, 0, sizeof(u_entry_T));
-#ifdef U_DEBUG
- uep->ue_magic = UE_MAGIC;
-#endif
-
- uep->ue_size = size;
- uep->ue_top = top;
- if (newbot != 0)
- uep->ue_bot = newbot;
- /*
- * Use 0 for ue_bot if bot is below last line.
- * Otherwise we have to compute ue_bot later.
- */
- else if (bot > curbuf->b_ml.ml_line_count)
- uep->ue_bot = 0;
- else {
- uep->ue_lcount = curbuf->b_ml.ml_line_count;
- curbuf->b_u_newhead->uh_getbot_entry = uep;
- }
-
- if (size > 0) {
- uep->ue_array = xmalloc(sizeof(char_u *) * size);
- for (i = 0, lnum = top + 1; i < size; ++i) {
- fast_breakcheck();
- if (got_int) {
- u_freeentry(uep, i);
- return FAIL;
- }
- if ((uep->ue_array[i] = u_save_line(lnum++)) == NULL) {
- u_freeentry(uep, i);
- goto nomem;
- }
- }
- } else
- uep->ue_array = NULL;
- uep->ue_next = curbuf->b_u_newhead->uh_entry;
- curbuf->b_u_newhead->uh_entry = uep;
- curbuf->b_u_synced = FALSE;
- undo_undoes = FALSE;
-
-#ifdef U_DEBUG
- u_check(FALSE);
-#endif
- return OK;
-
-nomem:
- msg_silent = 0; /* must display the prompt */
- if (ask_yesno((char_u *)_("No undo possible; continue anyway"), TRUE)
- == 'y') {
- undo_off = TRUE; /* will be reset when character typed */
- return OK;
- }
- do_outofmem_msg((long_u)0);
- return FAIL;
-}
-
-
-# define UF_START_MAGIC "Vim\237UnDo\345" /* magic at start of undofile */
-# define UF_START_MAGIC_LEN 9
-# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
-# define UF_HEADER_END_MAGIC 0xe7aa /* magic after last header */
-# define UF_ENTRY_MAGIC 0xf518 /* magic at start of entry */
-# define UF_ENTRY_END_MAGIC 0x3581 /* magic after last entry */
-# define UF_VERSION 2 /* 2-byte undofile version number */
-# define UF_VERSION_CRYPT 0x8002 /* idem, encrypted */
-
-/* extra fields for header */
-# define UF_LAST_SAVE_NR 1
-
-/* extra fields for uhp */
-# define UHP_SAVE_NR 1
-
-static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
-
-/*
- * Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
- */
-void u_compute_hash(char_u *hash)
-{
- context_sha256_T ctx;
- linenr_T lnum;
- char_u *p;
-
- sha256_start(&ctx);
- for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) {
- p = ml_get(lnum);
- sha256_update(&ctx, p, (uint32_t)(STRLEN(p) + 1));
- }
- sha256_finish(&ctx, hash);
-}
-
-/*
- * Return an allocated string of the full path of the target undofile.
- * When "reading" is TRUE find the file to read, go over all directories in
- * 'undodir'.
- * When "reading" is FALSE use the first name where the directory exists.
- * Returns NULL when there is no place to write or no file to read.
- */
-char_u *u_get_undo_file_name(char_u *buf_ffname, int reading)
-{
- char_u *dirp;
- char_u dir_name[IOSIZE + 1];
- char_u *munged_name = NULL;
- char_u *undo_file_name = NULL;
- int dir_len;
- char_u *p;
- char_u *ffname = buf_ffname;
-#ifdef HAVE_READLINK
- char_u fname_buf[MAXPATHL];
-#endif
-
- if (ffname == NULL)
- return NULL;
-
-#ifdef HAVE_READLINK
- /* Expand symlink in the file name, so that we put the undo file with the
- * actual file instead of with the symlink. */
- if (resolve_symlink(ffname, fname_buf) == OK)
- ffname = fname_buf;
-#endif
-
- /* Loop over 'undodir'. When reading find the first file that exists.
- * When not reading use the first directory that exists or ".". */
- dirp = p_udir;
- while (*dirp != NUL) {
- dir_len = copy_option_part(&dirp, dir_name, IOSIZE, ",");
- if (dir_len == 1 && dir_name[0] == '.') {
- /* Use same directory as the ffname,
- * "dir/name" -> "dir/.name.un~" */
- undo_file_name = vim_strnsave(ffname, (int)(STRLEN(ffname) + 5));
- if (undo_file_name == NULL)
- break;
- p = path_tail(undo_file_name);
- memmove(p + 1, p, STRLEN(p) + 1);
- *p = '.';
- STRCAT(p, ".un~");
- } else {
- dir_name[dir_len] = NUL;
- if (os_isdir(dir_name)) {
- if (munged_name == NULL) {
- munged_name = vim_strsave(ffname);
- if (munged_name == NULL)
- return NULL;
- for (p = munged_name; *p != NUL; mb_ptr_adv(p))
- if (vim_ispathsep(*p))
- *p = '%';
- }
- undo_file_name = concat_fnames(dir_name, munged_name, TRUE);
- }
- }
-
- // When reading check if the file exists.
- if (undo_file_name != NULL &&
- (!reading || os_file_exists(undo_file_name))) {
- break;
- }
- free(undo_file_name);
- undo_file_name = NULL;
- }
-
- free(munged_name);
- return undo_file_name;
-}
-
-static void corruption_error(char *mesg, char_u *file_name)
-{
- EMSG3(_("E825: Corrupted undo file (%s): %s"), mesg, file_name);
-}
-
-static void u_free_uhp(u_header_T *uhp)
-{
- u_entry_T *nuep;
- u_entry_T *uep;
-
- uep = uhp->uh_entry;
- while (uep != NULL) {
- nuep = uep->ue_next;
- u_freeentry(uep, uep->ue_size);
- uep = nuep;
- }
- free(uhp);
-}
-
-/*
- * Like fwrite() but crypt the bytes when 'key' is set.
- * Returns 1 if successful.
- */
-static size_t fwrite_crypt(buf_T *buf, char_u *ptr, size_t len, FILE *fp)
-{
- char_u *copy;
- char_u small_buf[100];
- size_t i;
-
- if (*buf->b_p_key == NUL)
- return fwrite(ptr, len, (size_t)1, fp);
- if (len < 100)
- copy = small_buf; /* no malloc()/free() for short strings */
- else {
- copy = xmalloc(len);
- }
- crypt_encode(ptr, len, copy);
- i = fwrite(copy, len, (size_t)1, fp);
- if (copy != small_buf)
- free(copy);
- return i;
-}
-
-/*
- * Read a string of length "len" from "fd".
- * When 'key' is set decrypt the bytes.
- */
-static char_u *read_string_decrypt(buf_T *buf, FILE *fd, int len)
-{
- char_u *ptr;
-
- ptr = read_string(fd, len);
- if (ptr != NULL && *buf->b_p_key != NUL)
- crypt_decode(ptr, len);
- return ptr;
-}
-
-static int serialize_header(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;
-
- /* If the buffer is encrypted then all text bytes following will be
- * encrypted. Numbers and other info is not crypted. */
- if (*buf->b_p_key != NUL) {
- char_u *header;
- int header_len;
-
- put_bytes(fp, (long_u)UF_VERSION_CRYPT, 2);
- header = prepare_crypt_write(buf, &header_len);
- if (header == NULL)
- return FAIL;
- len = (int)fwrite(header, (size_t)header_len, (size_t)1, fp);
- free(header);
- if (len != 1) {
- crypt_pop_state();
- return FAIL;
- }
- } else
- 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_crypt(buf, buf->b_u_line_ptr, (size_t)len, 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_time_cur);
-
- /* Optional fields. */
- putc(4, fp);
- putc(UF_LAST_SAVE_NR, fp);
- put_bytes(fp, (long_u)buf->b_u_save_nr_last, 4);
-
- putc(0, fp); /* end marker */
-
- return OK;
-}
-
-static int serialize_uhp(FILE *fp, buf_T *buf, 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.ptr);
- put_header_ptr(fp, uhp->uh_prev.ptr);
- put_header_ptr(fp, uhp->uh_alt_next.ptr);
- put_header_ptr(fp, uhp->uh_alt_prev.ptr);
- put_bytes(fp, uhp->uh_seq, 4);
- serialize_pos(uhp->uh_cursor, fp);
- put_bytes(fp, (long_u)uhp->uh_cursor_vcol, 4);
- 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);
- serialize_visualinfo(&uhp->uh_visual, fp);
- put_time(fp, uhp->uh_time);
-
- /* Optional fields. */
- putc(4, fp);
- putc(UHP_SAVE_NR, fp);
- put_bytes(fp, (long_u)uhp->uh_save_nr, 4);
-
- putc(0, fp); /* end marker */
-
- /* 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(fp, buf, uep) == FAIL)
- return FAIL;
- }
- put_bytes(fp, (long_u)UF_ENTRY_END_MAGIC, 2);
- return OK;
-}
-
-static u_header_T *unserialize_uhp(FILE *fp, char_u *file_name)
-{
- u_header_T *uhp;
- int i;
- u_entry_T *uep, *last_uep;
- int c;
- int error;
-
- uhp = xmalloc(sizeof(u_header_T));
- memset(uhp, 0, sizeof(u_header_T));
-#ifdef U_DEBUG
- uhp->uh_magic = UH_MAGIC;
-#endif
- uhp->uh_next.seq = get4c(fp);
- uhp->uh_prev.seq = get4c(fp);
- uhp->uh_alt_next.seq = get4c(fp);
- uhp->uh_alt_prev.seq = get4c(fp);
- uhp->uh_seq = get4c(fp);
- if (uhp->uh_seq <= 0) {
- corruption_error("uh_seq", file_name);
- free(uhp);
- return NULL;
- }
- unserialize_pos(&uhp->uh_cursor, fp);
- uhp->uh_cursor_vcol = get4c(fp);
- uhp->uh_flags = get2c(fp);
- for (i = 0; i < NMARKS; ++i)
- unserialize_pos(&uhp->uh_namedm[i], fp);
- unserialize_visualinfo(&uhp->uh_visual, fp);
- uhp->uh_time = get8ctime(fp);
-
- /* Optional fields. */
- for (;; ) {
- int len = getc(fp);
- int what;
-
- if (len == 0)
- break;
- what = getc(fp);
- switch (what) {
- case UHP_SAVE_NR:
- uhp->uh_save_nr = get4c(fp);
- break;
- default:
- /* field not supported, skip */
- while (--len >= 0)
- (void)getc(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".
- */
-static int serialize_uep(FILE *fp, buf_T *buf, u_entry_T *uep)
-{
- int i;
- size_t len;
-
- put_bytes(fp, (long_u)uep->ue_top, 4);
- put_bytes(fp, (long_u)uep->ue_bot, 4);
- put_bytes(fp, (long_u)uep->ue_lcount, 4);
- put_bytes(fp, (long_u)uep->ue_size, 4);
- for (i = 0; i < uep->ue_size; ++i) {
- len = STRLEN(uep->ue_array[i]);
- if (put_bytes(fp, (long_u)len, 4) == FAIL)
- return FAIL;
- if (len > 0 && fwrite_crypt(buf, uep->ue_array[i], len, fp) != 1)
- return FAIL;
- }
- return OK;
-}
-
-static u_entry_T *unserialize_uep(FILE *fp, int *error, char_u *file_name)
-{
- int i;
- u_entry_T *uep;
- char_u **array;
- char_u *line;
- int line_len;
-
- uep = xmalloc(sizeof(u_entry_T));
- memset(uep, 0, sizeof(u_entry_T));
-#ifdef U_DEBUG
- uep->ue_magic = UE_MAGIC;
-#endif
- uep->ue_top = get4c(fp);
- uep->ue_bot = get4c(fp);
- uep->ue_lcount = get4c(fp);
- uep->ue_size = get4c(fp);
- if (uep->ue_size > 0) {
- array = xmalloc(sizeof(char_u *) * uep->ue_size);
- memset(array, 0, sizeof(char_u *) * uep->ue_size);
- } else
- array = NULL;
- uep->ue_array = array;
-
- for (i = 0; i < uep->ue_size; ++i) {
- line_len = get4c(fp);
- if (line_len >= 0)
- line = read_string_decrypt(curbuf, fp, line_len);
- else {
- line = NULL;
- corruption_error("line length", file_name);
- }
- if (line == NULL) {
- *error = TRUE;
- return uep;
- }
- array[i] = line;
- }
- return uep;
-}
-
-/*
- * Serialize "pos" to "fp".
- */
-static void serialize_pos(pos_T pos, FILE *fp)
-{
- put_bytes(fp, (long_u)pos.lnum, 4);
- put_bytes(fp, (long_u)pos.col, 4);
- put_bytes(fp, (long_u)pos.coladd, 4);
-}
-
-/*
- * Unserialize the pos_T at the current position in fp.
- */
-static void unserialize_pos(pos_T *pos, FILE *fp)
-{
- pos->lnum = get4c(fp);
- if (pos->lnum < 0)
- pos->lnum = 0;
- pos->col = get4c(fp);
- if (pos->col < 0)
- pos->col = 0;
- pos->coladd = get4c(fp);
- if (pos->coladd < 0)
- pos->coladd = 0;
-}
-
-/*
- * Serialize "info" to "fp".
- */
-static void serialize_visualinfo(visualinfo_T *info, FILE *fp)
-{
- serialize_pos(info->vi_start, fp);
- serialize_pos(info->vi_end, fp);
- put_bytes(fp, (long_u)info->vi_mode, 4);
- put_bytes(fp, (long_u)info->vi_curswant, 4);
-}
-
-/*
- * Unserialize the visualinfo_T at the current position in fp.
- */
-static void unserialize_visualinfo(visualinfo_T *info, FILE *fp)
-{
- unserialize_pos(&info->vi_start, fp);
- unserialize_pos(&info->vi_end, fp);
- info->vi_mode = get4c(fp);
- info->vi_curswant = get4c(fp);
-}
-
-/*
- * Write the pointer to an undo header. Instead of writing the pointer itself
- * we use the sequence number of the header. This is converted back to
- * pointers when reading. */
-static void put_header_ptr(FILE *fp, u_header_T *uhp)
-{
- put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
-}
-