1
0
forked from aniani/vim

Included patch for persistent undo. Lots of changes and added test.

This commit is contained in:
Bram Moolenaar 2010-05-23 23:34:36 +02:00
parent c39125d7c4
commit 55debbe384
34 changed files with 1232 additions and 97 deletions

View File

@ -805,6 +805,7 @@ Note: these are typed literally, they are not special keys!
*filename-modifiers*
*:_%:* *::8* *::p* *::.* *::~* *::h* *::t* *::r* *::e* *::s* *::gs*
*%:8* *%:p* *%:.* *%:~* *%:h* *%:t* *%:r* *%:e* *%:s* *%:gs*
The file name modifiers can be used after "%", "#", "#n", "<cfile>", "<sfile>",
"<afile>" or "<abuf>". They are also used with the |fnamemodify()| function.
These are not available when Vim has been compiled without the |+modify_fname|

View File

@ -6053,6 +6053,7 @@ os2 OS/2 version of Vim.
osfiletype Compiled with support for osfiletypes |+osfiletype|
path_extra Compiled with up/downwards search in 'path' and 'tags'
perl Compiled with Perl interface.
persistent_undo Compiled with support for persistent undo history.
postscript Compiled with PostScript file printing.
printer Compiled with |:hardcopy| support.
profile Compiled with |:profile| support.

View File

@ -7220,6 +7220,33 @@ A jump table for the options with a short description can be found at |Q_op|.
global
Alias for 'term', see above.
*'undodir'* *'udir'*
'undodir' 'udir' string (default ".")
global
{not in Vi}
{only when compiled with the +persistent_undo feature}
List of directory names for undo files, separated with commas.
See |'backupdir'| for the format. Specifically, "." means using the
directory of the file.
When writing: The first directory that exists is used. "." always
works, no directories after "." will be used for writing.
When reading all entries are tried to find an undo file. The first
undo file that exists is used. When it cannot be read an error is
given, no further entry is used.
See |undo-persistence|.
*'undofile'* *'udf'*
'undofile' 'udf' boolean (default off)
local to buffer
{not in Vi}
{only when compiled with the +persistent_undo feature}
When on, Vim automatically saves undo history to an undo file when
writing a buffer to a file, and restores undo history from the same
file on buffer read.
The name of the undo file is specified by 'undodir'.
See |undo-persistence|.
WARNING: this is a very new feature. Use at your own risc!
*'undolevels'* *'ul'*
'undolevels' 'ul' number (default 100, 1000 for Unix, VMS,
Win32 and OS/2)

View File

@ -10,6 +10,16 @@ $VIM starting.txt /*$VIM*
$VIM-use version5.txt /*$VIM-use*
$VIMRUNTIME starting.txt /*$VIMRUNTIME*
% motion.txt /*%*
%:. cmdline.txt /*%:.*
%:8 cmdline.txt /*%:8*
%:e cmdline.txt /*%:e*
%:gs cmdline.txt /*%:gs*
%:h cmdline.txt /*%:h*
%:p cmdline.txt /*%:p*
%:r cmdline.txt /*%:r*
%:s cmdline.txt /*%:s*
%:t cmdline.txt /*%:t*
%:~ cmdline.txt /*%:~*
& change.txt /*&*
' motion.txt /*'*
'' motion.txt /*''*
@ -1005,7 +1015,11 @@ $VIMRUNTIME starting.txt /*$VIMRUNTIME*
'tw' options.txt /*'tw'*
'tx' options.txt /*'tx'*
'uc' options.txt /*'uc'*
'udf' options.txt /*'udf'*
'udir' options.txt /*'udir'*
'ul' options.txt /*'ul'*
'undodir' options.txt /*'undodir'*
'undofile' options.txt /*'undofile'*
'undolevels' options.txt /*'undolevels'*
'updatecount' options.txt /*'updatecount'*
'updatetime' options.txt /*'updatetime'*
@ -1163,6 +1177,7 @@ $VIMRUNTIME starting.txt /*$VIMRUNTIME*
+path_extra various.txt /*+path_extra*
+perl various.txt /*+perl*
+perl/dyn various.txt /*+perl\/dyn*
+persistent_undo various.txt /*+persistent_undo*
+postscript various.txt /*+postscript*
+printer various.txt /*+printer*
+profile various.txt /*+profile*
@ -2582,6 +2597,7 @@ $VIMRUNTIME starting.txt /*$VIMRUNTIME*
:rubydo if_ruby.txt /*:rubydo*
:rubyf if_ruby.txt /*:rubyf*
:rubyfile if_ruby.txt /*:rubyfile*
:rundo undo.txt /*:rundo*
:runtime repeat.txt /*:runtime*
:rv starting.txt /*:rv*
:rviminfo starting.txt /*:rviminfo*
@ -2964,6 +2980,7 @@ $VIMRUNTIME starting.txt /*$VIMRUNTIME*
:write_f editing.txt /*:write_f*
:ws workshop.txt /*:ws*
:wsverb workshop.txt /*:wsverb*
:wundo undo.txt /*:wundo*
:wv starting.txt /*:wv*
:wviminfo starting.txt /*:wviminfo*
:x editing.txt /*:x*
@ -6913,6 +6930,7 @@ perl-overview if_perl.txt /*perl-overview*
perl-patterns pattern.txt /*perl-patterns*
perl-using if_perl.txt /*perl-using*
perl.vim syntax.txt /*perl.vim*
persistent-undo undo.txt /*persistent-undo*
pexpr-option print.txt /*pexpr-option*
pfn-option print.txt /*pfn-option*
pheader-option print.txt /*pheader-option*
@ -7835,6 +7853,7 @@ undo undo.txt /*undo*
undo-blocks undo.txt /*undo-blocks*
undo-branches undo.txt /*undo-branches*
undo-commands undo.txt /*undo-commands*
undo-persistence undo.txt /*undo-persistence*
undo-redo undo.txt /*undo-redo*
undo-remarks undo.txt /*undo-remarks*
undo-tree undo.txt /*undo-tree*

View File

@ -30,6 +30,9 @@ be worked on, but only if you sponsor Vim development. See |sponsor|.
*known-bugs*
-------------------- Known bugs and current work -----------------------
When Vim crashes it may run out of stack while executing autocommands. Patch
to not run autocommands when leaving Vim? (James Vega, 2010 May 23)
Cursor positioning wrong with 0x200e character. (John Becket, 2010 May 6)
E315 when trying to change a file in FileChangedRO autocommand event.
@ -1082,6 +1085,18 @@ restored. (Luc St-Louis)
Vim 7.3:
Patches to include:
8 Persistent undo bugs / fixes:
- Add tests. Also with different 'enc'
- Add undofile(name): get undo file name for buffer "name".
- Extend test62 for gettabvar() and settabvar(). (Yegappan Lakshmanan, 2010
May 23)
- Also crypt the undo file.
- Also crypt the swap file, each block separately. Change mf_write() and
mf_read(). How to get b_p_key to these functions?
- Do profiling on sha256 code to find obvious bottlenecks.
- Do profiling on crypt code to find obvious bottlenecks.
- Use off_t instead of long for bytes in a buffer. (James Vega, 2010 May 22,
update next day)
- Include conceal patch?
http://vince.negri.googlepages.com/
http://vim.wikia.com/wiki/Patch_to_conceal_parts_of_lines
@ -1150,42 +1165,17 @@ Needs some work:
Needs some more testing.
Update 2010 Apr 20, patch by Andy Kittner, May 16
- Easier/standard way to disable default plugins.
8 Persistent undo: store undo in a file. Patch by Jordan Lewis, 2009 Feb
20. Repost 2009 Nov 16.
Get tar file from: http://repo.or.cz/w/vim_extended.git/tree/feat/persistent-undo
-> disable by default and add remark that it's new and may fail.
Testing remarks by Christian Brabandt, 2010 May 1:
- doesn't work well with symlinks (Jordan will look into it)
- old undo files tend to pile up
- :rundo should output a message (Jordan will fix this)
Bugs / fixes:
- Undo file should be stored with the original file by default, the undo
directory doesn't handle remote files or directory renames.
Use same mechanism as for swap files? But only with one file name.
- Read coladd depending on FEAT_VIRTUALEDIT, should always read/write it
- invoke u_wundo() inside buf_write()
- invoke u_rundo() inside readfile()
- Document that ":wundo" and ":rundo" should only be used in autocommands.
- unserialize_pos() does not need a return value
- function comments go before the function, not inside
- u_get_undofile() changes its argument ffname
- make magic four bytes.
- errors need numbers "E000:"
- also put 'enc' in undo file.
- don't use timestamp, "touch file" or dir copy may change it and undo
still works.
Older ideas:
- Use timestamps, so that a version a certain time ago can be found and
info before some time/date can be flushed. 'undopersist' gives maximum
time to keep undo: "3h", "1d", "2w", "1y", etc. For the file use dot
and extension: ".filename.un~" (like swapfile but "un~" instead of
"swp").
- ":{range}source": source the lines from the current file.
You can already yank lines and use :@" to execute them.
Most of do_source() would not be used, need a new function.
It's easy when not doing breakpoints or profiling.
Probably not now:
- Use timestamps for undo, so that a version a certain time ago can be found
and info before some time/date can be flushed. 'undopersist' gives maximum
time to keep undo: "3h", "1d", "2w", "1y", etc.
Before (beta) release:
- Add fixes for 7.2 to version7.txt
- Rename vim73 branch to default (hints: Xavier de Gaye, 2010 May 23)
More patches:
@ -1292,7 +1282,6 @@ Awaiting updated patches:
to left as well? See patch of Dec 26. (Nadim Shaikli)
8 Option to lock all used memory so that it doesn't get swapped to disk
(uncrypted). Patch by Jason Holt, 2003 May 23. Uses mlock.
7 Support a stronger encryption. Jason Holt implemented AES (May 6 2003).
7 Add ! register, for shell commands. (patch from Grenie)
8 In the gzip plugin, also recognize *.gz.orig, *.gz.bak, etc. Like it's
done for filetype detection. Patch from Walter Briscoe, 2003 Jul 1.
@ -4320,11 +4309,6 @@ Mouse support:
- When mouse click after 'r' command, get character that was pointed to.
Crypt and security:
8 Also crypt the swapfile, each block separately. Change mf_write() and
mf_read(). How to get b_p_key to these functions?
Argument list:
6 Add command to put all filenames from the tag files in the argument list.
When given an argument, only use the files where that argument matches

View File

@ -12,7 +12,8 @@ The basics are explained in section |02.5| of the user manual.
2. Two ways of undo |undo-two-ways|
3. Undo blocks |undo-blocks|
4. Undo branches |undo-branches|
5. Remarks about undo |undo-remarks|
5. Undo persistence |undo-persistence|
6. Remarks about undo |undo-remarks|
==============================================================================
1. Undo and redo commands *undo-commands*
@ -22,7 +23,7 @@ u Undo [count] changes. {Vi: only one level}
*:u* *:un* *:undo*
:u[ndo] Undo one change. {Vi: only one level}
*E830*
:u[ndo] {N} Jump to after change number {N}. See |undo-branches|
for the meaning of {N}. {not in Vi}
@ -109,6 +110,8 @@ change.
To do the opposite, break a change into two undo blocks, in Insert mode use
CTRL-G u. This is useful if you want an insert command to be undoable in
parts. E.g., for each sentence. |i_CTRL-G_u|
Setting the value of 'undolevels' also breaks undo. Even when the new value
is equal to the old value.
==============================================================================
4. Undo branches *undo-branches* *undo-tree*
@ -201,7 +204,88 @@ Note that using "u" and CTRL-R will not get you to all possible text states
while repeating "g-" and "g+" does.
==============================================================================
5. Remarks about undo *undo-remarks*
5. Undo persistence *undo-persistence* *persistent-undo*
When unloading a buffer Vim normally destroys the tree of undos created for
that buffer. By setting the 'undofile' option, Vim will automatically save
your undo history when you write a file and restore undo history when you edit
the file again.
The 'undofile' option is checked after writing a file, before the BufWritePost
autocommands. If you want to control what files to write undo information
for, you can use a BufWritePre autocommand: >
au BufWritePre /tmp/* setlocal noundofile
Vim saves undo trees in a separate undo file, one for each edited file, using
a simple scheme that maps filesystem paths directly to undo files. Vim will
detect if an undo file is no longer synchronized with the file it was written
for (with a hash of the file contents) and ignore it when the file was changed
after the undo file was written, to prevent corruption.
Undo files are normally saved in the same directory as the file. This can be
changed with the 'undodir' option.
You can also save and restore undo histories by using ":wundo" and ":rundo"
respectively:
*:wundo* *:rundo*
:wundo[!] {file}
Write undo history to {file}.
When {file} exists and it does not look like an undo file
(the magic number at the start of the file is wrong), then
this fails, unless the ! was added.
If it exists and does look like an undo file it is
overwritten.
{not in Vi}
:rundo {file} Read undo history from {file}.
{not in Vi}
You can use these in autocommands to explicitly specify the name of the
history file. E.g.: >
au BufReadPost * rundo %:h/UNDO/%:t
au BufWritePost * wundo %:h/UNDO/%:t
You should keep 'undofile' off, otherwise you end up with two undo files for
every write.
Note: I did not verify this always works!
Note that while reading/writing files and 'undofile' is set most errors will
be silent, unless 'verbose' is set. With :wundo and :rundo you will get more
error messages, e.g., when the file cannot be read or written.
NOTE: undo files are never deleted by Vim. You need to delete them yourself.
Reading an existing undo file may fail for several reasons:
*E822* It cannot be opened, because the file permissions don't allow it.
*E823* The magic number at the start of the file doesn't match. This usually
means it is not an undo file.
*E824* The version number of the undo file indicates that it's written by a
newer version of Vim. You need that newer version to open it. Don't
write the buffer if you want to keep the undo info in the file.
"Undo file contents changed"
The file text differs from when the undo file was written. This means
the undo file cannot be used, it would corrupt the text.
*E825* *E826* The undo file does not contain valid contents and cannot be
used.
*E827* The magic number at the end of the file was not found. This usually
means the file was truncated.
Writing an undo file may fail for these reasons:
*E828* The file to be written cannot be created. Perhaps you do not have
write permissions in the directory.
"Will not overwrite with undo file, cannot read"
A file exists with the name of the undo file to be written, but it
cannot be read. You may want to delete this file or rename it.
"Will not overwrite, this is not an undo file"
A file exists with the name of the undo file to be written, but it
does not start with the right magic number. You may want to delete
this file or rename it.
*E829* An error occurred while writing the undo file. You may want to try
again.
==============================================================================
6. Remarks about undo *undo-remarks*
The number of changes that are remembered is set with the 'undolevels' option.
If it is zero, the Vi-compatible way is always used. If it is negative no

View File

@ -359,6 +359,7 @@ m *+ole* Win32 GUI only: |ole-interface|
N *+path_extra* Up/downwards search in 'path' and 'tags'
m *+perl* Perl interface |perl|
m *+perl/dyn* Perl interface |perl-dynamic| |/dyn|
H *+persistent_undo* Persistent undo |undo-persistence|
*+postscript* |:hardcopy| writes a PostScript file
N *+printer* |:hardcopy| command
H *+profile* |:profile| command

View File

@ -7170,6 +7170,10 @@ the buffer is marked as modified.
Added *added-7.3*
-----
Persistent undo: store undo information in a file. Can undo to before when
the file was read, also for unloaded buffers. |undo-persistence|
(partly by Jordan Lewis)
Added the 'relativenumber' option. (Markus Heidelberg)
Support for Blowfish encryption. Added the 'cryptmethod' option.

View File

@ -1032,6 +1032,10 @@ if has("vertsplit")
call append("$", "cmdwinheight\theight of the command-line window")
call <SID>OptionG("cwh", &cwh)
endif
call append("$", "undofile\tautomatically save and restore undo history")
call <SID>BinOptionG("udf", &udf)
call append("$", "undodir\tlist of directories for undo files")
call <SID>OptionG("udir", &udir)
call <SID>Header("executing external commands")

View File

@ -288,7 +288,7 @@ NOTA: Se batti solo il movimento mentre sei in Modalit
2. Batti d2w per cancellare le due parole MAIUSCOLE
3. Ripeti i passi 1 e 2 con un contatore diverso per cancellare la parole
3. Ripeti i passi 1 e 2 con un contatore diverso per cancellare le parole
MAIUSCOLE consecutive con un solo comando
---> questa ABC DE linea FGHI JK LMN OP di parole è Q RS TUV ora ripulita.

View File

@ -288,7 +288,7 @@ NOTA: Se batti solo il movimento mentre sei in Modalità Normale, senza
2. Batti d2w per cancellare le due parole MAIUSCOLE
3. Ripeti i passi 1 e 2 con un contatore diverso per cancellare la parole
3. Ripeti i passi 1 e 2 con un contatore diverso per cancellare le parole
MAIUSCOLE consecutive con un solo comando
---> questa ABC DE linea FGHI JK LMN OP di parole è Q RS TUV ora ripulita.

View File

@ -61,8 +61,9 @@ static void buf_delete_signs __ARGS((buf_T *buf));
#endif
/*
* Open current buffer, that is: open the memfile and read the file into memory
* return FAIL for failure, OK otherwise
* Open current buffer, that is: open the memfile and read the file into
* memory.
* Return FAIL for failure, OK otherwise.
*/
int
open_buffer(read_stdin, eap)

View File

@ -11869,6 +11869,9 @@ f_has(argvars, rettv)
"perl",
#endif
#endif
#ifdef FEAT_PERSISTENT_UNDO
"persistent_undo",
#endif
#ifdef FEAT_PYTHON
#ifndef DYNAMIC_PYTHON
"python",

View File

@ -773,6 +773,8 @@ EX(CMD_rubydo, "rubydo", ex_rubydo,
RANGE|DFLALL|EXTRA|NEEDARG|CMDWIN),
EX(CMD_rubyfile, "rubyfile", ex_rubyfile,
RANGE|FILE1|NEEDARG|CMDWIN),
EX(CMD_rundo, "rundo", ex_rundo,
NEEDARG|EXTRA|XFILE),
EX(CMD_rviminfo, "rviminfo", ex_viminfo,
BANG|FILE1|TRLBAR|CMDWIN),
EX(CMD_substitute, "substitute", do_sub,
@ -1061,6 +1063,8 @@ EX(CMD_wqall, "wqall", do_wqall,
BANG|FILE1|ARGOPT|DFLALL|TRLBAR),
EX(CMD_wsverb, "wsverb", ex_wsverb,
EXTRA|NOTADR|NEEDARG),
EX(CMD_wundo, "wundo", ex_wundo,
BANG|NEEDARG|EXTRA|XFILE),
EX(CMD_wviminfo, "wviminfo", ex_viminfo,
BANG|FILE1|TRLBAR|CMDWIN),
EX(CMD_xit, "xit", ex_exit,

View File

@ -243,6 +243,10 @@ static void ex_popup __ARGS((exarg_T *eap));
# define ex_spellinfo ex_ni
# define ex_spellrepall ex_ni
#endif
#ifndef FEAT_PERSISTENT_UNDO
# define ex_rundo ex_ni
# define ex_wundo ex_ni
#endif
#ifndef FEAT_MZSCHEME
# define ex_mzscheme ex_script_ni
# define ex_mzfile ex_ni
@ -298,6 +302,10 @@ static void ex_join __ARGS((exarg_T *eap));
static void ex_at __ARGS((exarg_T *eap));
static void ex_bang __ARGS((exarg_T *eap));
static void ex_undo __ARGS((exarg_T *eap));
#ifdef FEAT_PERSISTENT_UNDO
static void ex_wundo __ARGS((exarg_T *eap));
static void ex_rundo __ARGS((exarg_T *eap));
#endif
static void ex_redo __ARGS((exarg_T *eap));
static void ex_later __ARGS((exarg_T *eap));
static void ex_redir __ARGS((exarg_T *eap));
@ -8452,6 +8460,28 @@ ex_undo(eap)
u_undo(1);
}
#ifdef FEAT_PERSISTENT_UNDO
void
ex_wundo(eap)
exarg_T *eap;
{
char_u hash[UNDO_HASH_SIZE];
u_compute_hash(hash);
u_write_undo(eap->arg, eap->forceit, curbuf, hash);
}
void
ex_rundo(eap)
exarg_T *eap;
{
char_u hash[UNDO_HASH_SIZE];
u_compute_hash(hash);
u_read_undo(eap->arg, hash);
}
#endif
/*
* ":redo".
*/

View File

@ -1275,3 +1275,11 @@
|| defined(FEAT_BIG)
# define FEAT_AUTOCHDIR
#endif
/*
* +persistent_undo 'undofile', 'undodir' options, :wundo and :rundo, and
* implementation.
*/
#ifdef FEAT_NORMAL
# define FEAT_PERSISTENT_UNDO
#endif

View File

@ -252,6 +252,10 @@ readfile(fname, sfname, from, lines_to_skip, lines_to_read, eap, flags)
#ifdef FEAT_CRYPT
char_u *cryptkey = NULL;
int did_ask_for_key = FALSE;
#endif
#ifdef FEAT_PERSISTENT_UNDO
context_sha256_T sha_ctx;
int read_undo_file = FALSE;
#endif
int split = 0; /* number of split lines */
#define UNKNOWN 0x0fffffff /* file size is unknown */
@ -1177,6 +1181,12 @@ retry:
read_count = lines_to_read;
#ifdef FEAT_MBYTE
conv_restlen = 0;
#endif
#ifdef FEAT_PERSISTENT_UNDO
read_undo_file = (newfile && curbuf->b_ffname != NULL && curbuf->b_p_udf
&& !filtering && !read_stdin && !read_buffer);
if (read_undo_file)
sha256_start(&sha_ctx);
#endif
}
@ -2133,6 +2143,10 @@ rewind_retry:
error = TRUE;
break;
}
#ifdef FEAT_PERSISTENT_UNDO
if (read_undo_file)
sha256_update(&sha_ctx, line_start, len);
#endif
++lnum;
if (--read_count == 0)
{
@ -2197,6 +2211,10 @@ rewind_retry:
error = TRUE;
break;
}
#ifdef FEAT_PERSISTENT_UNDO
if (read_undo_file)
sha256_update(&sha_ctx, line_start, len);
#endif
++lnum;
if (--read_count == 0)
{
@ -2237,11 +2255,17 @@ failed:
if (set_options)
curbuf->b_p_eol = FALSE;
*ptr = NUL;
if (ml_append(lnum, line_start,
(colnr_T)(ptr - line_start + 1), newfile) == FAIL)
len = (colnr_T)(ptr - line_start + 1);
if (ml_append(lnum, line_start, len, newfile) == FAIL)
error = TRUE;
else
{
#ifdef FEAT_PERSISTENT_UNDO
if (read_undo_file)
sha256_update(&sha_ctx, line_start, len);
#endif
read_no_eol_lnum = ++lnum;
}
}
if (set_options)
@ -2555,6 +2579,19 @@ failed:
*/
write_no_eol_lnum = read_no_eol_lnum;
#ifdef FEAT_PERSISTENT_UNDO
/*
* When opening a new file locate undo info and read it.
*/
if (read_undo_file)
{
char_u hash[UNDO_HASH_SIZE];
sha256_finish(&sha_ctx, hash);
u_read_undo(NULL, hash);
}
#endif
#ifdef FEAT_AUTOCMD
if (!read_stdin && !read_buffer)
{
@ -3038,6 +3075,10 @@ buf_write(buf, fname, sfname, start, end, eap, append, forceit,
vim_acl_T acl = NULL; /* ACL copied from original file to
backup or new file */
#endif
#ifdef FEAT_PERSISTENT_UNDO
int write_undo_file = FALSE;
context_sha256_T sha_ctx;
#endif
if (fname == NULL || *fname == NUL) /* safety check */
return FAIL;
@ -4344,6 +4385,14 @@ restore_backup:
write_info.bw_start_lnum = start;
#endif
#ifdef FEAT_PERSISTENT_UNDO
write_undo_file = (buf->b_p_udf && overwriting && !append
&& !filtering && reset_changed);
if (write_undo_file)
/* Prepare for computing the hash value of the text. */
sha256_start(&sha_ctx);
#endif
write_info.bw_len = bufsize;
#ifdef HAS_BW_FLAGS
write_info.bw_flags = wb_flags;
@ -4358,6 +4407,10 @@ restore_backup:
* Keep it fast!
*/
ptr = ml_get_buf(buf, lnum, FALSE) - 1;
#ifdef FEAT_PERSISTENT_UNDO
if (write_undo_file)
sha256_update(&sha_ctx, ptr + 1, STRLEN(ptr + 1) + 1);
#endif
while ((c = *++ptr) != NUL)
{
if (c == NL)
@ -4886,6 +4939,20 @@ nofail:
}
msg_scroll = msg_save;
#ifdef FEAT_PERSISTENT_UNDO
/*
* When writing the whole file and 'undofile' is set, also write the undo
* file.
*/
if (retval == OK && write_undo_file)
{
char_u hash[UNDO_HASH_SIZE];
sha256_finish(&sha_ctx, hash);
u_write_undo(NULL, FALSE, buf, hash);
}
#endif
#ifdef FEAT_AUTOCMD
#ifdef FEAT_EVAL
if (!should_abort(retval))

View File

@ -245,9 +245,6 @@ static char_u *make_percent_swname __ARGS((char_u *dir, char_u *name));
#ifdef FEAT_BYTEOFF
static void ml_updatechunk __ARGS((buf_T *buf, long line, long len, int updtype));
#endif
#ifdef HAVE_READLINK
static int resolve_symlink __ARGS((char_u *fname, char_u *buf));
#endif
/*
* Open a new memline for "buf".
@ -3559,7 +3556,7 @@ ml_lineadd(buf, count)
}
}
#ifdef HAVE_READLINK
#if defined(HAVE_READLINK) || defined(PROTO)
/*
* Resolve a symlink in the last component of a file name.
* Note that f_resolve() does it for every part of the path, we don't do that
@ -3567,7 +3564,7 @@ ml_lineadd(buf, count)
* If it worked returns OK and the resolved link in "buf[MAXPATHL]".
* Otherwise returns FAIL.
*/
static int
int
resolve_symlink(fname, buf)
char_u *fname;
char_u *buf;
@ -3862,7 +3859,7 @@ do_swapexists(buf, fname)
* Returns the name in allocated memory or NULL.
*
* Note: If BASENAMELEN is not correct, you will get error messages for
* not being able to open the swapfile
* not being able to open the swap or undo file
* Note: May trigger SwapExists autocmd, pointers may change!
*/
static char_u *
@ -3886,29 +3883,29 @@ findswapname(buf, dirp, old_fname)
# define CREATE_DUMMY_FILE
FILE *dummyfd = NULL;
/*
* If we start editing a new file, e.g. "test.doc", which resides on an MSDOS
* compatible filesystem, it is possible that the file "test.doc.swp" which we
* create will be exactly the same file. To avoid this problem we temporarily
* create "test.doc".
* Don't do this when the check below for a 8.3 file name is used.
*/
/*
* If we start editing a new file, e.g. "test.doc", which resides on an
* MSDOS compatible filesystem, it is possible that the file
* "test.doc.swp" which we create will be exactly the same file. To avoid
* this problem we temporarily create "test.doc". Don't do this when the
* check below for a 8.3 file name is used.
*/
if (!(buf->b_p_sn || buf->b_shortname) && buf->b_fname != NULL
&& mch_getperm(buf->b_fname) < 0)
dummyfd = mch_fopen((char *)buf->b_fname, "w");
#endif
/*
* Isolate a directory name from *dirp and put it in dir_name.
* First allocate some memory to put the directory name in.
*/
/*
* Isolate a directory name from *dirp and put it in dir_name.
* First allocate some memory to put the directory name in.
*/
dir_name = alloc((unsigned)STRLEN(*dirp) + 1);
if (dir_name != NULL)
(void)copy_option_part(dirp, dir_name, 31000, ",");
/*
* we try different names until we find one that does not exist yet
*/
/*
* we try different names until we find one that does not exist yet
*/
if (dir_name == NULL) /* out of memory */
fname = NULL;
else

View File

@ -177,6 +177,9 @@
#define PV_TS OPT_BUF(BV_TS)
#define PV_TW OPT_BUF(BV_TW)
#define PV_TX OPT_BUF(BV_TX)
#ifdef FEAT_PERSISTENT_UNDO
# define PV_UDF OPT_BUF(BV_UDF)
#endif
#define PV_WM OPT_BUF(BV_WM)
/*
@ -362,6 +365,9 @@ static char_u *p_spl;
static long p_ts;
static long p_tw;
static int p_tx;
#ifdef FEAT_PERSISTENT_UNDO
static int p_udf;
#endif
static long p_wm;
#ifdef FEAT_KEYMAP
static char_u *p_keymap;
@ -2586,6 +2592,22 @@ static struct vimoption
{"ttytype", "tty", P_STRING|P_EXPAND|P_NODEFAULT|P_NO_MKRC|P_VI_DEF|P_RALL,
(char_u *)&T_NAME, PV_NONE,
{(char_u *)"", (char_u *)0L} SCRIPTID_INIT},
{"undodir", "udir", P_STRING|P_EXPAND|P_COMMA|P_NODUP|P_SECURE|P_VI_DEF,
#ifdef FEAT_PERSISTENT_UNDO
(char_u *)&p_udir, PV_NONE,
{(char_u *)".", (char_u *)0L}
#else
(char_u *)NULL, PV_NONE,
{(char_u *)0L, (char_u *)0L}
#endif
SCRIPTID_INIT},
{"undofile", "udf", P_BOOL|P_VI_DEF|P_VIM,
#ifdef FEAT_PERSISTENT_UNDO
(char_u *)&p_udf, PV_UDF,
#else
(char_u *)NULL, PV_NONE,
#endif
{(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
{"undolevels", "ul", P_NUM|P_VI_DEF,
(char_u *)&p_ul, PV_NONE,
{
@ -9404,6 +9426,9 @@ get_varp(p)
case PV_TS: return (char_u *)&(curbuf->b_p_ts);
case PV_TW: return (char_u *)&(curbuf->b_p_tw);
case PV_TX: return (char_u *)&(curbuf->b_p_tx);
#ifdef FEAT_PERSISTENT_UNDO
case PV_UDF: return (char_u *)&(curbuf->b_p_udf);
#endif
case PV_WM: return (char_u *)&(curbuf->b_p_wm);
#ifdef FEAT_KEYMAP
case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap);
@ -9774,6 +9799,9 @@ buf_copy_options(buf, flags)
#if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
buf->b_p_bexpr = empty_option;
#endif
#ifdef FEAT_PERSISTENT_UNDO
buf->b_p_udf = p_udf;
#endif
/*
* Don't copy the options set by ex_help(), use the saved values,

View File

@ -815,6 +815,7 @@ static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", "jsbterm"
# define TTYM_JSBTERM 0x10
# define TTYM_PTERM 0x20
#endif
EXTERN char_u *p_udir; /* 'undodir' */
EXTERN long p_ul; /* 'undolevels' */
EXTERN long p_uc; /* 'updatecount' */
EXTERN long p_ut; /* 'updatetime' */
@ -1004,6 +1005,7 @@ enum
, BV_TS
, BV_TW
, BV_TX
, BV_UDF
, BV_WM
, BV_COUNT /* must be the last one */
};

View File

@ -204,7 +204,7 @@
#endif
#ifndef DFLT_VDIR
# define DFLT_VDIR "$VIM/vimfiles/view" /* default for 'viewdir' */
# define DFLT_VDIR "$VIM/vimfiles/view" /* default for 'viewdir' */
#endif
#define DFLT_ERRORFILE "errors.err"

View File

@ -25,6 +25,7 @@ int ml_delete __ARGS((linenr_T lnum, int message));
void ml_setmarked __ARGS((linenr_T lnum));
linenr_T ml_firstmarked __ARGS((void));
void ml_clearmarked __ARGS((void));
int resolve_symlink __ARGS((char_u *fname, char_u *buf));
char_u *makeswapname __ARGS((char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name));
char_u *get_file_in_dir __ARGS((char_u *fname, char_u *dname));
void ml_setflags __ARGS((buf_T *buf));

View File

@ -1,4 +1,7 @@
/* sha256.c */
void sha256_start __ARGS((context_sha256_T *ctx));
void sha256_update __ARGS((context_sha256_T *ctx, char_u *input, uint32_t length));
void sha256_finish __ARGS((context_sha256_T *ctx, char_u digest[32]));
char_u *sha256_key __ARGS((char_u *buf));
int sha256_self_test __ARGS((void));
void sha2_seed __ARGS((char_u header[], int header_len));

View File

@ -2,11 +2,14 @@
int spell_check __ARGS((win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, int docount));
int spell_move_to __ARGS((win_T *wp, int dir, int allwords, int curline, hlf_T *attrp));
void spell_cat_line __ARGS((char_u *buf, char_u *line, int maxlen));
int get2c __ARGS((FILE *fd));
int get3c __ARGS((FILE *fd));
int get4c __ARGS((FILE *fd));
char_u *did_set_spelllang __ARGS((buf_T *buf));
void spell_free_all __ARGS((void));
void spell_reload __ARGS((void));
int spell_check_msm __ARGS((void));
void put_bytes __ARGS((FILE *fd, long_u nr, int len));
int put_bytes __ARGS((FILE *fd, long_u nr, int len));
void ex_mkspell __ARGS((exarg_T *eap));
void ex_spell __ARGS((exarg_T *eap));
void spell_add_word __ARGS((char_u *word, int len, int bad, int idx, int undo));

View File

@ -5,6 +5,9 @@ int u_savesub __ARGS((linenr_T lnum));
int u_inssub __ARGS((linenr_T lnum));
int u_savedel __ARGS((linenr_T lnum, long nlines));
int undo_allowed __ARGS((void));
void u_compute_hash __ARGS((char_u *hash));
void u_read_undo __ARGS((char_u *name, char_u *hash));
void u_write_undo __ARGS((char_u *name, int forceit, buf_T *buf, char_u *hash));
void u_undo __ARGS((int count));
void u_redo __ARGS((int count));
void undo_time __ARGS((long step, int sec, int absolute));

View File

@ -20,18 +20,9 @@
#include "vim.h"
#ifdef FEAT_CRYPT
#if defined(FEAT_CRYPT) || defined(FEAT_PERSISTENT_UNDO)
typedef struct {
UINT32_T total[2];
UINT32_T state[8];
char_u buffer[64];
} context_sha256_T;
static void sha256_starts __ARGS((context_sha256_T *ctx));
static void sha256_process __ARGS((context_sha256_T *ctx, char_u data[64]));
static void sha256_update __ARGS((context_sha256_T *ctx, char_u *input, UINT32_T length));
static void sha256_finish __ARGS((context_sha256_T *ctx, char_u digest[32]));
static char_u *sha256_bytes __ARGS((char_u *buf, int buflen));
static unsigned int get_some_time __ARGS((void));
@ -52,8 +43,8 @@ static unsigned int get_some_time __ARGS((void));
(b)[(i) + 3] = (char_u)((n) ); \
}
static void
sha256_starts(ctx)
void
sha256_start(ctx)
context_sha256_T *ctx;
{
ctx->total[0] = 0;
@ -203,7 +194,7 @@ sha256_process(ctx, data)
ctx->state[7] += H;
}
static void
void
sha256_update(ctx, input, length)
context_sha256_T *ctx;
char_u *input;
@ -250,7 +241,7 @@ static char_u sha256_padding[64] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static void
void
sha256_finish(ctx, digest)
context_sha256_T *ctx;
char_u digest[32];
@ -296,7 +287,7 @@ sha256_bytes(buf, buflen)
sha256_self_test();
sha256_starts(&ctx);
sha256_start(&ctx);
sha256_update(&ctx, buf, buflen);
sha256_finish(&ctx, sha256sum);
for (j = 0; j < 32; j++)
@ -368,7 +359,7 @@ sha256_self_test()
}
else
{
sha256_starts(&ctx);
sha256_start(&ctx);
memset(buf, 'a', 1000);
for (j = 0; j < 1000; j++)
sha256_update(&ctx, (char_u *)buf, 1000);
@ -416,7 +407,7 @@ sha2_seed(header, header_len)
for (i = 0; i < (int)sizeof(random_data) - 1; i++)
random_data[i] = (char_u)((get_some_time() ^ rand()) & 0xff);
sha256_starts(&ctx);
sha256_start(&ctx);
sha256_update(&ctx, (char_u *)random_data, sizeof(random_data));
sha256_finish(&ctx, sha256sum);
@ -424,4 +415,4 @@ sha2_seed(header, header_len)
header[i] = sha256sum[i % sizeof(sha256sum)];
}
#endif /* FEAT_CRYPT */
#endif /* FEAT_CRYPT || FEAT_PERSISTENT_UNDO */

View File

@ -854,9 +854,6 @@ static char_u *spell_enc __ARGS((void));
static void int_wordlist_spl __ARGS((char_u *fname));
static void spell_load_cb __ARGS((char_u *fname, void *cookie));
static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent));
static int get2c __ARGS((FILE *fd));
static int get3c __ARGS((FILE *fd));
static int get4c __ARGS((FILE *fd));
static time_t get8c __ARGS((FILE *fd));
static char_u *read_cnt_string __ARGS((FILE *fd, int cnt_bytes, int *lenp));
static char_u *read_string __ARGS((FILE *fd, int cnt));
@ -2988,7 +2985,7 @@ endOK:
/*
* Read 2 bytes from "fd" and turn them into an int, MSB first.
*/
static int
int
get2c(fd)
FILE *fd;
{
@ -3002,7 +2999,7 @@ get2c(fd)
/*
* Read 3 bytes from "fd" and turn them into an int, MSB first.
*/
static int
int
get3c(fd)
FILE *fd;
{
@ -3017,7 +3014,7 @@ get3c(fd)
/*
* Read 4 bytes from "fd" and turn them into an int, MSB first.
*/
static int
int
get4c(fd)
FILE *fd;
{
@ -8018,7 +8015,7 @@ node_equal(n1, n2)
/*
* Write a number to file "fd", MSB first, in "len" bytes.
*/
void
int
put_bytes(fd, nr, len)
FILE *fd;
long_u nr;
@ -8027,7 +8024,9 @@ put_bytes(fd, nr, len)
int i;
for (i = len - 1; i >= 0; --i)
putc((int)(nr >> (i * 8)), fd);
if (putc((int)(nr >> (i * 8)), fd) == EOF)
return FAIL;
return OK;
}
#ifdef _MSC_VER

View File

@ -1465,6 +1465,9 @@ struct file_buffer
char_u *b_p_dict; /* 'dictionary' local value */
char_u *b_p_tsr; /* 'thesaurus' local value */
#endif
#ifdef FEAT_PERSISTENT_UNDO
int b_p_udf; /* 'undofile' */
#endif
/* end of buffer options */
@ -2392,3 +2395,9 @@ typedef struct
#define CPT_KIND 2 /* "kind" */
#define CPT_INFO 3 /* "info" */
#define CPT_COUNT 4 /* Number of entries */
typedef struct {
UINT32_T total[2];
UINT32_T state[8];
char_u buffer[64];
} context_sha256_T;

View File

@ -69,7 +69,7 @@ test1.out: test1.in
fi \
else echo $* NO OUTPUT >>test.log; \
fi"
#-rm -rf X* test.ok viminfo
-rm -rf X* test.ok viminfo
test49.out: test49.vim

View File

@ -50,6 +50,53 @@ obbbbu:.w >>test.out
obbbb:set ul=100
:undojoin
occccu:.w >>test.out
:"
:" Test 'undofile': first a simple one-line change.
:set nocp ul=100 undofile
:e! Xtestfile
ggdGithis is one line:set ul=100
:s/one/ONE/
:set ul=100
:w
:bwipe!
:e Xtestfile
u:.w >>test.out
:"
:" Test 'undofile', change in original file fails check
:set noundofile
:e! Xtestfile
:s/line/Line/
:w
:set undofile
:bwipe!
:e Xtestfile
u:.w >>test.out
:"
:" Test 'undofile', add 10 lines, delete 6 lines, undo 3
:set undofile
ggdGione
two
three
four
five
six
seven
eight
nine
ten:set ul=100
3Gdd:set ul=100
dd:set ul=100
dd:set ul=100
dd:set ul=100
dd:set ul=100
dd:set ul=100
:w
:bwipe!
:e Xtestfile
uuu:w >>test.out
:"
:" Rename the undo file so that it gets cleaned up.
:call rename(".Xtestfile.un~", "Xtestundo")
:qa!
ENDTEST

View File

@ -22,3 +22,12 @@
123456abc
aaaa
aaaa
this is one line
this is ONE Line
one
two
six
seven
eight
nine
ten

View File

@ -99,6 +99,14 @@ static void u_freeheader __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp)
static void u_freebranch __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
static void u_freeentries __ARGS((buf_T *buf, u_header_T *uhp, u_header_T **uhpp));
static void u_freeentry __ARGS((u_entry_T *, long));
#ifdef FEAT_PERSISTENT_UNDO
static void unserialize_pos __ARGS((pos_T *pos, FILE *fp));
static void unserialize_visualinfo __ARGS((visualinfo_T *info, FILE *fp));
static char_u *u_get_undo_file_name __ARGS((char_u *, int reading));
static int serialize_uep __ARGS((u_entry_T *uep, FILE *fp));
static void serialize_pos __ARGS((pos_T pos, FILE *fp));
static void serialize_visualinfo __ARGS((visualinfo_T info, FILE *fp));
#endif
#ifdef U_USE_MALLOC
# define U_FREE_LINE(ptr) vim_free(ptr)
@ -119,6 +127,8 @@ static long u_newcount, u_oldcount;
*/
static int undo_undoes = FALSE;
static int lastmark = 0;
#ifdef U_DEBUG
/*
* Check the undo structures for being valid. Print a warning when something
@ -652,6 +662,795 @@ nomem:
return FAIL;
}
#ifdef FEAT_PERSISTENT_UNDO
# define UF_START_MAGIC 0xfeac /* magic at start of undofile */
# define UF_HEADER_MAGIC 0x5fd0 /* magic at start of header */
# define UF_END_MAGIC 0xe7aa /* magic after last header */
# define UF_VERSION 1 /* 2-byte undofile version number */
/*
* Compute the hash for the current buffer text into hash[UNDO_HASH_SIZE].
*/
void
u_compute_hash(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, STRLEN(p) + 1);
}
sha256_finish(&ctx, hash);
}
/*
* Unserialize the pos_T at the current position in fp.
*/
static void
unserialize_pos(pos, fp)
pos_T *pos;
FILE *fp;
{
pos->lnum = get4c(fp);
pos->col = get4c(fp);
#ifdef FEAT_VIRTUALEDIT
pos->coladd = get4c(fp);
#else
(void)get4c(fp);
#endif
}
/*
* Unserialize the visualinfo_T at the current position in fp.
*/
static void
unserialize_visualinfo(info, fp)
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);
}
/*
* 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.
*/
static char_u *
u_get_undo_file_name(buf_ffname, reading)
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;
struct stat st;
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, STRLEN(ffname) + 5);
if (undo_file_name == NULL)
break;
p = gettail(undo_file_name);
mch_memmove(p + 1, p, STRLEN(p) + 1);
*p = '.';
STRCAT(p, ".un~");
}
else
{
dir_name[dir_len] = NUL;
if (mch_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
|| mch_stat((char *)undo_file_name, &st) >= 0))
break;
vim_free(undo_file_name);
undo_file_name = NULL;
}
vim_free(munged_name);
return undo_file_name;
}
/*
* Load the undo tree from an undo file.
* If "name" is not NULL use it as the undo file name. This also means being
* a bit more verbose.
* Otherwise use curbuf->b_ffname to generate the undo file name.
* "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
*/
void
u_read_undo(name, hash)
char_u *name;
char_u *hash;
{
char_u *file_name;
FILE *fp;
long magic, version, str_len;
char_u *line_ptr = NULL;
linenr_T line_lnum;
colnr_T line_colnr;
linenr_T line_count;
int uep_len;
int line_len;
int num_head;
long old_header_seq, new_header_seq, cur_header_seq;
long seq_last, seq_cur;
short old_idx = -1, new_idx = -1, cur_idx = -1;
long num_read_uhps = 0;
time_t seq_time;
int i, j;
int c;
short found_first_uep = 0;
char_u **array;
char_u *line;
u_entry_T *uep, *last_uep, *nuep;
u_header_T *uhp;
u_header_T **uhp_table = NULL;
char_u read_hash[UNDO_HASH_SIZE];
if (name == NULL)
{
file_name = u_get_undo_file_name(curbuf->b_ffname, TRUE);
if (file_name == NULL)
return;
}
else
file_name = name;
if (p_verbose > 0)
smsg((char_u *)_("Reading undo file: %s"), file_name);
fp = mch_fopen((char *)file_name, "r");
if (fp == NULL)
{
if (name != NULL || p_verbose > 0)
EMSG2(_("E822: Cannot open undo file for reading: %s"), file_name);
goto error;
}
/* Begin overall file information */
magic = get2c(fp);
if (magic != UF_START_MAGIC)
{
EMSG2(_("E823: Corrupted undo file: %s"), file_name);
goto error;
}
version = get2c(fp);
if (version != UF_VERSION)
{
EMSG2(_("E824: Incompatible undo file: %s"), file_name);
goto error;
}
fread(read_hash, UNDO_HASH_SIZE, 1, fp);
line_count = (linenr_T)get4c(fp);
if (memcmp(hash, read_hash, UNDO_HASH_SIZE) != 0
|| line_count != curbuf->b_ml.ml_line_count)
{
if (p_verbose > 0 || name != NULL)
{
verbose_enter();
give_warning((char_u *)_("Undo file contents changed"), TRUE);
verbose_leave();
}
goto error;
}
/* Begin undo data for U */
str_len = get4c(fp);
if (str_len < 0)
goto error;
else if (str_len > 0)
{
if ((line_ptr = U_ALLOC_LINE(str_len)) == NULL)
goto error;
for (i = 0; i < str_len; i++)
line_ptr[i] = (char_u)getc(fp);
line_ptr[i] = NUL;
}
line_lnum = (linenr_T)get4c(fp);
line_colnr = (colnr_T)get4c(fp);
/* Begin general undo data */
old_header_seq = get4c(fp);
new_header_seq = get4c(fp);
cur_header_seq = get4c(fp);
num_head = get4c(fp);
seq_last = get4c(fp);
seq_cur = get4c(fp);
seq_time = get4c(fp);
/* uhp_table will store the freshly created undo headers we allocate
* until we insert them into curbuf. The table remains sorted by the
* sequence numbers of the headers. */
uhp_table = (u_header_T **)U_ALLOC_LINE(num_head * sizeof(u_header_T *));
if (uhp_table == NULL)
goto error;
vim_memset(uhp_table, 0, num_head * sizeof(u_header_T *));
c = get2c(fp);
while (c == UF_HEADER_MAGIC)
{
found_first_uep = 0;
uhp = (u_header_T *)U_ALLOC_LINE((unsigned)sizeof(u_header_T));
if (uhp == NULL)
goto error;
vim_memset(uhp, 0, sizeof(u_header_T));
/* 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)get4c(fp);
uhp->uh_prev = (u_header_T *)(long)get4c(fp);
uhp->uh_alt_next = (u_header_T *)(long)get4c(fp);
uhp->uh_alt_prev = (u_header_T *)(long)get4c(fp);
uhp->uh_seq = get4c(fp);
if (uhp->uh_seq <= 0)
{
EMSG2(_("E825: Undo file corruption: invalid uh_seq.: %s"),
file_name);
U_FREE_LINE(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 = get4c(fp);
/* Unserialize uep list. The first 4 bytes is the length of the
* entire uep in bytes minus the length of the strings within.
* -1 is a sentinel value meaning no more ueps.*/
last_uep = NULL;
while ((uep_len = get4c(fp)) != -1)
{
uep = (u_entry_T *)U_ALLOC_LINE((unsigned)sizeof(u_entry_T));
vim_memset(uep, 0, sizeof(u_entry_T));
if (uep == NULL)
goto error;
uep->ue_top = get4c(fp);
uep->ue_bot = get4c(fp);
uep->ue_lcount = get4c(fp);
uep->ue_size = get4c(fp);
uep->ue_next = NULL;
array = (char_u **)U_ALLOC_LINE(
(unsigned)(sizeof(char_u *) * uep->ue_size));
for (i = 0; i < uep->ue_size; i++)
{
line_len = get4c(fp);
/* U_ALLOC_LINE provides an extra byte for the NUL terminator.*/
line = (char_u *)U_ALLOC_LINE(
(unsigned) (sizeof(char_u) * line_len));
if (line == NULL)
goto error;
for (j = 0; j < line_len; j++)
{
line[j] = getc(fp);
}
line[j] = '\0';
array[i] = line;
}
uep->ue_array = array;
if (found_first_uep == 0)
{
uhp->uh_entry = uep;
found_first_uep = 1;
}
else
{
last_uep->ue_next = uep;
}
last_uep = uep;
}
/* Insertion sort the uhp into the table by its uh_seq. This is
* required because, while the number of uhps is limited to
* num_heads, and the uh_seq order is monotonic with respect to
* creation time, the starting uh_seq can be > 0 if any undolevel
* culling was done at undofile write time, and there can be uh_seq
* gaps in the uhps.
*/
for (i = num_read_uhps - 1; i >= -1; i--)
{
/* if i == -1, we've hit the leftmost side of the table, so insert
* at uhp_table[0]. */
if (i == -1 || uhp->uh_seq > uhp_table[i]->uh_seq)
{
/* If we've had to move from the rightmost side of the table,
* we have to shift everything to the right by one spot. */
if (i < num_read_uhps - 1)
{
memmove(uhp_table + i + 2, uhp_table + i + 1,
(num_read_uhps - i) * sizeof(u_header_T *));
}
uhp_table[i + 1] = uhp;
break;
}
else if (uhp->uh_seq == uhp_table[i]->uh_seq)
{
EMSG2(_("E826 Undo file corruption: duplicate uh_seq: %s"),
file_name);
goto error;
}
}
num_read_uhps++;
c = get2c(fp);
}
if (c != UF_END_MAGIC)
{
EMSG2(_("E827: Undo file corruption; no end marker: %s"), file_name);
goto error;
}
/* We've organized all of the uhps into a table sorted by uh_seq. Now we
* iterate through the table and swizzle each sequence number we've
* stored in uh_foo into a pointer corresponding to the header with that
* sequence number. Then free curbuf's old undo structure, give curbuf
* the updated {old,new,cur}head pointers, and then free the table. */
for (i = 0; i < num_head; i++)
{
uhp = uhp_table[i];
if (uhp == NULL)
continue;
for (j = 0; j < num_head; j++)
{
if (uhp_table[j] == NULL)
continue;
if (uhp_table[j]->uh_seq == (long)uhp->uh_next)
uhp->uh_next = uhp_table[j];
if (uhp_table[j]->uh_seq == (long)uhp->uh_prev)
uhp->uh_prev = uhp_table[j];
if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_next)
uhp->uh_alt_next = uhp_table[j];
if (uhp_table[j]->uh_seq == (long)uhp->uh_alt_prev)
uhp->uh_alt_prev = uhp_table[j];
}
if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq)
old_idx = i;
if (new_header_seq > 0 && new_idx < 0 && uhp->uh_seq == new_header_seq)
new_idx = i;
if (cur_header_seq > 0 && cur_idx < 0 && uhp->uh_seq == cur_header_seq)
cur_idx = i;
}
u_blockfree(curbuf);
curbuf->b_u_oldhead = old_idx < 0 ? 0 : uhp_table[old_idx];
curbuf->b_u_newhead = new_idx < 0 ? 0 : uhp_table[new_idx];
curbuf->b_u_curhead = cur_idx < 0 ? 0 : uhp_table[cur_idx];
curbuf->b_u_line_ptr = line_ptr;
curbuf->b_u_line_lnum = line_lnum;
curbuf->b_u_line_colnr = line_colnr;
curbuf->b_u_numhead = num_head;
curbuf->b_u_seq_last = seq_last;
curbuf->b_u_seq_cur = seq_cur;
curbuf->b_u_seq_time = seq_time;
U_FREE_LINE(uhp_table);
#ifdef U_DEBUG
u_check(TRUE);
#endif
if (name != NULL)
smsg((char_u *)_("Finished reading undo file %s"), file_name);
goto theend;
error:
if (line_ptr != NULL)
U_FREE_LINE(line_ptr);
if (uhp_table != NULL)
{
for (i = 0; i < num_head; i++)
{
if (uhp_table[i] != NULL)
{
uep = uhp_table[i]->uh_entry;
while (uep != NULL)
{
nuep = uep->ue_next;
u_freeentry(uep, uep->ue_size);
uep = nuep;
}
U_FREE_LINE(uhp_table[i]);
}
}
U_FREE_LINE(uhp_table);
}
theend:
if (fp != NULL)
fclose(fp);
if (file_name != name)
vim_free(file_name);
return;
}
/*
* Serialize "uep" to "fp".
*/
static int
serialize_uep(uep, fp)
u_entry_T *uep;
FILE *fp;
{
int i;
int uep_len;
int *entry_lens;
if (uep->ue_size > 0)
entry_lens = (int *)alloc(uep->ue_size * sizeof(int));
/* Define uep_len to be the size of the entire uep minus the size of its
* component strings, in bytes. The sizes of the component strings
* are written before each individual string.
* We have 4 entries each of 4 bytes, plus ue_size * 4 bytes
* of string size information. */
uep_len = uep->ue_size * 4;
/* Collect sizing information for later serialization. */
for (i = 0; i < uep->ue_size; i++)
{
entry_lens[i] = (int)STRLEN(uep->ue_array[i]);
uep_len += entry_lens[i];
}
put_bytes(fp, (long_u)uep_len, 4);
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++)
{
if (put_bytes(fp, (long_u)entry_lens[i], 4) == FAIL)
return FAIL;
fprintf(fp, "%s", uep->ue_array[i]);
}
if (uep->ue_size > 0)
vim_free(entry_lens);
return OK;
}
/*
* Serialize "pos" to "fp".
*/
static void
serialize_pos(pos, fp)
pos_T pos;
FILE *fp;
{
put_bytes(fp, (long_u)pos.lnum, 4);
put_bytes(fp, (long_u)pos.col, 4);
#ifdef FEAT_VIRTUALEDIT
put_bytes(fp, (long_u)pos.coladd, 4);
#else
put_bytes(fp, (long_u)0, 4);
#endif
}
/*
* Serialize "info" to "fp".
*/
static void
serialize_visualinfo(info, fp)
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);
}
static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s");
/*
* Write the undo tree in an undo file.
* When "name" is not NULL, use it as the name of the undo file.
* Otherwise use buf->b_ffname to generate the undo file name.
* "buf" must never be null, buf->b_ffname is used to obtain the original file
* permissions.
* "forceit" is TRUE for ":wundo!", FALSE otherwise.
* "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text.
*/
void
u_write_undo(name, forceit, buf, hash)
char_u *name;
int forceit;
buf_T *buf;
char_u *hash;
{
u_header_T *uhp;
u_entry_T *uep;
char_u *file_name;
int str_len, i, uep_len, mark;
int fd;
FILE *fp = NULL;
int perm;
int write_ok = FALSE;
#ifdef UNIX
struct stat st_old;
struct stat st_new;
#endif
if (name == NULL)
{
file_name = u_get_undo_file_name(buf->b_ffname, FALSE);
if (file_name == NULL)
return;
}
else
file_name = name;
#ifdef UNIX
if (mch_stat((char *)buf->b_ffname, &st_old) >= 0)
perm = st_old.st_mode;
else
perm = 0600;
#else
perm = mch_getperm(buf->b_ffname);
if (perm < 0)
perm = 0600;
#endif
/* set file protection same as original file, but strip s-bit */
perm = perm & 0777;
/* If the undo file exists, verify that it actually is an undo file, and
* delete it. */
if (mch_getperm(file_name) >= 0)
{
if (name == NULL || !forceit)
{
/* Check we can read it and it's an undo file. */
fd = mch_open((char *)file_name, O_RDONLY|O_EXTRA, 0);
if (fd < 0)
{
if (name != NULL || p_verbose > 0)
smsg((char_u *)_("Will not overwrite with undo file, cannot read: %s"),
file_name);
goto theend;
}
else
{
char_u buf[2];
vim_read(fd, buf, 2);
close(fd);
if ((buf[0] << 8) + buf[1] != UF_START_MAGIC)
{
if (name != NULL || p_verbose > 0)
smsg((char_u *)_("Will not overwrite, this is not an undo file: %s"),
file_name);
goto theend;
}
}
}
mch_remove(file_name);
}
fd = mch_open((char *)file_name,
O_CREAT|O_EXTRA|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
(void)mch_setperm(file_name, perm);
if (fd < 0)
{
EMSG2(_(e_not_open), file_name);
goto theend;
}
if (p_verbose > 0)
smsg((char_u *)_("Writing undo file: %s"), file_name);
#ifdef UNIX
/*
* Try to set the group of the undo file same as the original file. If
* this fails, set the protection bits for the group same as the
* protection bits for others.
*/
if (mch_stat((char *)file_name, &st_new) >= 0
&& st_new.st_gid != st_old.st_gid
# ifdef HAVE_FCHOWN /* sequent-ptx lacks fchown() */
&& fchown(fd, (uid_t)-1, st_old.st_gid) != 0
# endif
)
mch_setperm(file_name, (perm & 0707) | ((perm & 07) << 3));
# ifdef HAVE_SELINUX
mch_copy_sec(buf->b_ffname, file_name);
# endif
#endif
fp = fdopen(fd, "w");
if (fp == NULL)
{
EMSG2(_(e_not_open), file_name);
close(fd);
mch_remove(file_name);
goto theend;
}
/* Start writing, first overall file information */
put_bytes(fp, (long_u)UF_START_MAGIC, 2);
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 ? 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 */
uhp = buf->b_u_oldhead;
put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
uhp = buf->b_u_newhead;
put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
uhp = buf->b_u_curhead;
put_bytes(fp, (long_u)(uhp != NULL ? uhp->uh_seq : 0), 4);
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_bytes(fp, (long_u)buf->b_u_seq_time, 4);
/* Iteratively serialize UHPs and their UEPs from the top down. */
mark = ++lastmark;
uhp = buf->b_u_oldhead;
while (uhp != NULL)
{
/* Serialize current UHP if we haven't seen it */
if (uhp->uh_walk != mark)
{
if (put_bytes(fp, (long_u)UF_HEADER_MAGIC, 2) == FAIL)
goto write_error;
put_bytes(fp, (long_u)((uhp->uh_next != NULL)
? uhp->uh_next->uh_seq : 0), 4);
put_bytes(fp, (long_u)((uhp->uh_prev != NULL)
? uhp->uh_prev->uh_seq : 0), 4);
put_bytes(fp, (long_u)((uhp->uh_alt_next != NULL)
? uhp->uh_alt_next->uh_seq : 0), 4);
put_bytes(fp, (long_u)((uhp->uh_alt_prev != NULL)
? uhp->uh_alt_prev->uh_seq : 0), 4);
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);
#endif
put_bytes(fp, (long_u)uhp->uh_time, 4);
uep = uhp->uh_entry;
while (uep != NULL)
{
if (serialize_uep(uep, fp) == FAIL)
goto write_error;
uep = uep->ue_next;
}
/* Sentinel value: no more ueps */
uep_len = -1;
put_bytes(fp, (long_u)uep_len, 4);
uhp->uh_walk = mark;
}
/* 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)
uhp = uhp->uh_alt_next;
else if (uhp->uh_next != NULL && uhp->uh_alt_prev == NULL
&& uhp->uh_next->uh_walk != mark)
uhp = uhp->uh_next;
else if (uhp->uh_alt_prev != NULL)
uhp = uhp->uh_alt_prev;
else
uhp = uhp->uh_next;
}
if (put_bytes(fp, (long_u)UF_END_MAGIC, 2) == OK)
write_ok = TRUE;
write_error:
fclose(fp);
if (!write_ok)
EMSG2(_("E829: write error in undo file: %s"), file_name);
#if defined(MACOS_CLASSIC) || defined(WIN3264)
(void)mch_copy_file_attribute(buf->b_ffname, file_name);
#endif
#ifdef HAVE_ACL
{
vim_acl_T acl;
/* For systems that support ACL: get the ACL from the original file. */
acl = mch_get_acl(buf->b_ffname);
mch_set_acl(file_name, acl);
}
#endif
theend:
if (file_name != name)
vim_free(file_name);
}
#endif /* FEAT_PERSISTENT_UNDO */
/*
* If 'cpoptions' contains 'u': Undo the previous undo or redo (vi compatible).
* If 'cpoptions' does not contain 'u': Always undo.
@ -757,8 +1556,6 @@ u_doit(startcount)
u_undo_end(undo_undoes, FALSE);
}
static int lastmark = 0;
/*
* Undo or redo over the timeline.
* When "step" is negative go back in time, otherwise goes forward in time.
@ -927,7 +1724,7 @@ undo_time(step, sec, absolute)
if (absolute)
{
EMSGN(_("Undo number %ld not found"), step);
EMSGN(_("E830: Undo number %ld not found"), step);
return;
}

View File

@ -426,6 +426,11 @@ static char *(features[]) =
#else
"-perl",
#endif
#ifdef FEAT_PERSISTENT_UNDO
"+persistent_undo",
#else
"-persistent_undo",
#endif
#ifdef FEAT_PRINTER
# ifdef FEAT_POSTSCRIPT
"+postscript",

View File

@ -1398,6 +1398,9 @@ typedef enum
*/
#define MAXMAPLEN 50
/* Size in bytes of the hash used in the undo file. */
#define UNDO_HASH_SIZE 32
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif