diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2015-04-12 02:48:34 -0400 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2015-04-12 02:48:34 -0400 |
commit | ecc28fb2dd43551f349a071f9597cf7033f306f7 (patch) | |
tree | 6168d9aa93dbe58a0256b9a627b91b8cb3cb7448 | |
parent | e4975f82c9704aa5d52e18e578a449268654ee37 (diff) | |
parent | 1e767ad96fca7d64b2edcdd76337c7f2545475ef (diff) | |
download | rneovim-ecc28fb2dd43551f349a071f9597cf7033f306f7.tar.gz rneovim-ecc28fb2dd43551f349a071f9597cf7033f306f7.tar.bz2 rneovim-ecc28fb2dd43551f349a071f9597cf7033f306f7.zip |
Merge pull request #2117 from justinmk/fix1836
memline: fix segfaults
-rw-r--r-- | src/nvim/memline.c | 55 | ||||
-rw-r--r-- | src/nvim/path.c | 11 | ||||
-rw-r--r-- | src/nvim/testdir/Makefile | 2 | ||||
-rw-r--r-- | src/nvim/testdir/test78.in | 46 | ||||
-rw-r--r-- | src/nvim/testdir/test78.ok | 3 | ||||
-rw-r--r-- | test/functional/ex_cmds/recover_spec.lua | 71 | ||||
-rw-r--r-- | test/functional/helpers.lua | 11 | ||||
-rw-r--r-- | test/functional/legacy/078_swapfile_recover_spec.lua | 80 |
8 files changed, 181 insertions, 98 deletions
diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 93fbbd512f..448f15cc07 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -824,7 +824,7 @@ void ml_recover(void) (void)recover_names(fname, FALSE, i, &fname_used); } if (fname_used == NULL) - goto theend; /* out of memory */ + goto theend; // user chose invalid number. /* When called from main() still need to initialize storage structure */ if (called_from_main && ml_open(curbuf) == FAIL) @@ -1244,8 +1244,10 @@ theend: mf_put(mfp, hp, false, false); mf_close(mfp, false); /* will also free(mfp->mf_fname) */ } - free(buf->b_ml.ml_stack); - free(buf); + if (buf != NULL) { //may be NULL if swap file not found. + free(buf->b_ml.ml_stack); + free(buf); + } if (serious_error && called_from_main) ml_close(curbuf, TRUE); else { @@ -1321,53 +1323,35 @@ recover_names ( if (dir_name[0] == '.' && dir_name[1] == NUL) { /* check current dir */ if (fname == NULL) { names[0] = vim_strsave((char_u *)"*.sw?"); -#if defined(UNIX) || defined(WIN3264) /* For Unix names starting with a dot are special. MS-Windows * supports this too, on some file systems. */ names[1] = vim_strsave((char_u *)".*.sw?"); names[2] = vim_strsave((char_u *)".sw?"); num_names = 3; -#else - num_names = 1; -#endif } else num_names = recov_file_names(names, fname_res, TRUE); } else { /* check directory dir_name */ if (fname == NULL) { names[0] = concat_fnames(dir_name, (char_u *)"*.sw?", TRUE); -#if defined(UNIX) || defined(WIN3264) /* For Unix names starting with a dot are special. MS-Windows * supports this too, on some file systems. */ names[1] = concat_fnames(dir_name, (char_u *)".*.sw?", TRUE); names[2] = concat_fnames(dir_name, (char_u *)".sw?", TRUE); num_names = 3; -#else - num_names = 1; -#endif } else { -#if defined(UNIX) || defined(WIN3264) p = dir_name + STRLEN(dir_name); if (after_pathsep(dir_name, p) && p[-1] == p[-2]) { /* Ends with '//', Use Full path for swap name */ tail = make_percent_swname(dir_name, fname_res); - } else -#endif - tail = path_tail(fname_res); - tail = concat_fnames(dir_name, tail, TRUE); + } else { + tail = path_tail(fname_res); + tail = concat_fnames(dir_name, tail, TRUE); + } num_names = recov_file_names(names, tail, FALSE); free(tail); } } - // check for out-of-memory - for (int i = 0; i < num_names; ++i) { - if (names[i] == NULL) { - for (int j = 0; j < num_names; ++j) - free(names[j]); - num_names = 0; - break; - } - } if (num_names == 0) num_files = 0; else if (expand_wildcards(num_names, names, &num_files, &files, @@ -1453,7 +1437,6 @@ recover_names ( return file_count; } -#if defined(UNIX) || defined(WIN3264) /* Need _very_ long file names */ /* * Append the full path to name with path separators made into percent * signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"") @@ -1475,7 +1458,6 @@ static char_u *make_percent_swname(char_u *dir, char_u *name) } return d; } -#endif #ifdef UNIX static int process_still_running; @@ -1576,17 +1558,12 @@ static time_t swapfile_info(char_u *fname) } static int recov_file_names(char_u **names, char_u *path, int prepend_dot) + FUNC_ATTR_NONNULL_ALL { - int num_names; - char_u *p; - int i; - - num_names = 0; + int num_names = 0; - /* - * May also add the file name with a dot prepended, for swap file in same - * dir as original file. - */ + // May also add the file name with a dot prepended, for swap file in same + // dir as original file. if (prepend_dot) { names[num_names] = modname(path, (char_u *)".sw?", TRUE); if (names[num_names] == NULL) @@ -1597,8 +1574,8 @@ static int recov_file_names(char_u **names, char_u *path, int prepend_dot) // Form the normal swap file name pattern by appending ".sw?". names[num_names] = concat_fnames(path, (char_u *)".sw?", FALSE); if (num_names >= 1) { /* check if we have the same name twice */ - p = names[num_names - 1]; - i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]); + char_u *p = names[num_names - 1]; + int i = (int)STRLEN(names[num_names - 1]) - (int)STRLEN(names[num_names]); if (i > 0) p += i; /* file name has been expanded to full path */ @@ -3088,7 +3065,6 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name char_u fname_buf[MAXPATHL]; #endif -#if defined(UNIX) || defined(WIN3264) /* Need _very_ long file names */ s = dir_name + STRLEN(dir_name); if (after_pathsep(dir_name, s) && s[-1] == s[-2]) { /* Ends with '//', Use Full path */ r = NULL; @@ -3098,7 +3074,6 @@ char_u *makeswapname(char_u *fname, char_u *ffname, buf_T *buf, char_u *dir_name } return r; } -#endif #ifdef HAVE_READLINK /* Expand symlink in the file name, so that we put the swap file with the diff --git a/src/nvim/path.c b/src/nvim/path.c index 9515205643..80f1947ccf 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -83,15 +83,12 @@ FileComparison path_full_compare(char_u *s1, char_u *s2, int checkname) return kDifferentFiles; } -/// Get the tail of a path: the file name. +/// Gets the tail (i.e., the filename segment) of a path `fname`. /// -/// @param fname A file path. -/// @return -/// - Empty string, if fname is NULL. -/// - The position of the last path separator + 1. (i.e. empty string, if -/// fname ends in a slash). -/// - Never NULL. +/// @return pointer just past the last path separator (empty string, if fname +/// ends in a slash), or empty string if fname is NULL. char_u *path_tail(char_u *fname) + FUNC_ATTR_NONNULL_RET { if (fname == NULL) { return (char_u *)""; diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index a530e3cf23..0a7c16e2cb 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -24,7 +24,7 @@ SCRIPTS := test_autoformat_join.out \ test61.out test62.out test63.out test64.out test65.out \ test68.out test69.out \ test71.out test73.out test74.out \ - test76.out test78.out test79.out test80.out \ + test76.out test79.out test80.out \ test82.out test83.out \ test86.out test87.out test88.out \ test96.out \ diff --git a/src/nvim/testdir/test78.in b/src/nvim/testdir/test78.in deleted file mode 100644 index cb0e51edd5..0000000000 --- a/src/nvim/testdir/test78.in +++ /dev/null @@ -1,46 +0,0 @@ -Inserts 10000 lines with text to fill the swap file with two levels of pointer -blocks. Then recovers from the swap file and checks all text is restored. - -We need about 10000 lines of 100 characters to get two levels of pointer -blocks. - -STARTTEST -:so small.vim -:set fileformat=unix undolevels=-1 -:e! Xtest -ggdG -:let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789" -:let i = 1 -:let linecount = 10000 -:while i <= linecount | call append(i - 1, i . text) | let i += 1 | endwhile -:preserve -:" get the name of the swap file -:redir => swapname -:swapname -:redir END -:let swapname = substitute(swapname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', '') -:" make a copy of the swap file in Xswap -:set bin -:exe 'sp ' . swapname -:w! Xswap -:echo swapname -:set nobin -:new -:only! -:bwipe! Xtest -:call rename('Xswap', swapname) -:recover Xtest -:call delete(swapname) -:new -:call append(0, 'recovery start') -:wincmd w -:let linedollar = line('$') -:if linedollar < linecount | exe 'wincmd w' | call append(line('$'), "expected " . linecount . " lines but found only " . linedollar) | exe 'wincmd w' | let linecount = linedollar | endif -:let i = 1 -:while i <= linecount | if getline(i) != i . text | exe 'wincmd w' | call append(line('$'), i . ' differs') | exe 'wincmd w' | endif | let i += 1 | endwhile -:q! -:call append(line('$'), 'recovery end') -:w! test.out -:qa! -ENDTEST - diff --git a/src/nvim/testdir/test78.ok b/src/nvim/testdir/test78.ok deleted file mode 100644 index 6c3ecefe3c..0000000000 --- a/src/nvim/testdir/test78.ok +++ /dev/null @@ -1,3 +0,0 @@ -recovery start - -recovery end diff --git a/test/functional/ex_cmds/recover_spec.lua b/test/functional/ex_cmds/recover_spec.lua new file mode 100644 index 0000000000..d4c9477133 --- /dev/null +++ b/test/functional/ex_cmds/recover_spec.lua @@ -0,0 +1,71 @@ +-- Tests for :recover + +local helpers = require('test.functional.helpers') +local execute, eq, clear, eval, feed, expect, source = + helpers.execute, helpers.eq, helpers.clear, helpers.eval, helpers.feed, + helpers.expect, helpers.source + +describe(':recover', function() + before_each(clear) + + it('fails if given a non-existent swapfile', function() + local swapname = 'bogus-swapfile' + execute('recover '..swapname) -- This should not segfault. #2117 + eq('E305: No swap file found for '..swapname, eval('v:errmsg')) + end) + +end) + +describe(':preserve', function() + local swapdir = lfs.currentdir()..'/testdir_recover_spec' + before_each(function() + clear() + os.remove(swapdir) + lfs.mkdir(swapdir) + end) + after_each(function() + os.remove(swapdir) + end) + + it("saves to custom 'directory' and (R)ecovers (issue #1836)", function() + local testfile = 'testfile_recover_spec' + local init = [[ + set swapfile fileformat=unix undolevels=-1 + set directory^=]]..swapdir..[[// + ]] + + source(init) + execute('set swapfile fileformat=unix undolevels=-1') + -- Put swapdir at the start of the 'directory' list. #1836 + execute('set directory^='..swapdir..'//') + execute('edit '..testfile) + feed('isometext<esc>') + execute('preserve') + source('redir => g:swapname | swapname | redir END') + + local swappath1 = eval('g:swapname') + + --TODO(justinmk): this is an ugly hack to force `helpers` to support + --multiple sessions. + local nvim2 = helpers.spawn({helpers.nvim_prog, '-u', 'NONE', '--embed'}) + helpers.set_session(nvim2) + + source(init) + + -- Use the "SwapExists" event to choose the (R)ecover choice at the dialog. + execute('autocmd SwapExists * let v:swapchoice = "r"') + execute('silent edit '..testfile) + source('redir => g:swapname | swapname | redir END') + + local swappath2 = eval('g:swapname') + + -- swapfile from session 1 should end in .swp + assert(testfile..'.swp' == string.match(swappath1, '[^%%]+$')) + + -- swapfile from session 2 should end in .swo + assert(testfile..'.swo' == string.match(swappath2, '[^%%]+$')) + + expect('sometext') + end) + +end) diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 393b42dda5..27c94c34a8 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -18,6 +18,10 @@ if nvim_dir == nvim_prog then nvim_dir = "." end +-- Nvim "Unit Under Test" http://en.wikipedia.org/wiki/Device_under_test +local NvimUUT = {} +NvimUUT.__index = NvimUUT + local prepend_argv if os.getenv('VALGRIND') then @@ -49,6 +53,10 @@ end local session, loop_running, loop_stopped, last_error +local function set_session(s) + session = s +end + local function request(method, ...) local status, rv = session:request(method, ...) if not status then @@ -305,5 +313,6 @@ return { curwin = curwin, curtab = curtab, curbuf_contents = curbuf_contents, - wait = wait + wait = wait, + set_session = set_session } diff --git a/test/functional/legacy/078_swapfile_recover_spec.lua b/test/functional/legacy/078_swapfile_recover_spec.lua new file mode 100644 index 0000000000..e250c91441 --- /dev/null +++ b/test/functional/legacy/078_swapfile_recover_spec.lua @@ -0,0 +1,80 @@ +-- Inserts 10000 lines with text to fill the swap file with two levels of +-- pointer blocks. Then recovers from the swap file and checks all text is +-- restored. We need about 10000 lines of 100 characters to get two levels of +-- pointer blocks. + +local helpers = require('test.functional.helpers') +local feed, insert, source = helpers.feed, helpers.insert, helpers.source +local clear, execute, expect, source = helpers.clear, helpers.execute, helpers.expect, helpers.source +local eval = helpers.eval + +describe('78', function() + setup(clear) + teardown(function() + os.remove(".Xtest.swp") + os.remove(".Xtest.swo") + end) + + it('is working', function() + source([=[ + set swapfile fileformat=unix undolevels=-1 + e! Xtest + let text = "\tabcdefghijklmnoparstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnoparstuvwxyz0123456789" + let i = 1 + let linecount = 10000 + while i <= linecount | call append(i - 1, i . text) | let i += 1 | endwhile + preserve + + " Get the name of the swap file, and clean up the :redir capture. + redir => g:swapname | swapname | redir END + let g:swapname = substitute(g:swapname, '[[:blank:][:cntrl:]]*\(.\{-}\)[[:blank:][:cntrl:]]*$', '\1', 'g') + let g:swapname = fnameescape(g:swapname) + + " Make a copy of the swap file in Xswap + set bin + exe 'sp ' . g:swapname + w! Xswap + + set nobin + new + only! + bwipe! Xtest + call rename('Xswap', g:swapname) + + "TODO(jkeyes): without 'silent', this hangs the test " at message: + " 'Recovery completed. You should check if everything is OK.' + silent recover Xtest + + call delete(g:swapname) + new + call append(0, 'recovery start') + wincmd w + + let g:linedollar = line('$') + if g:linedollar < linecount + wincmd w + call append(line('$'), "expected " . linecount + \ . " lines but found only " . g:linedollar) + wincmd w + let linecount = g:linedollar + endif + + let i = 1 + while i <= linecount + if getline(i) != i . text + exe 'wincmd w' + call append(line('$'), i . ' differs') + exe 'wincmd w' + endif + let i += 1 + endwhile + q! + call append(line('$'), 'recovery end') + ]=]) + + expect([[ + recovery start + + recovery end]]) + end) +end) |