Problem: Cannot recover from a swap file.
Solution: Do not expand environment variables in the swap file name.
Do not check the extension when we already know a file is a swap
file. (Ken Takata, closes 4415, closes #4369)
/* User selected Recover at ATTENTION prompt. */
msg_scroll = TRUE;
- ml_recover();
+ ml_recover(FALSE);
msg_puts("\n"); /* don't overwrite the last message */
cmdline_row = msg_row;
do_modelines(0);
if (i < alist->al_ga.ga_len
&& (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
|| fullpathcmp(alist_name(&AARGLIST(alist)[i]),
- buf->b_ffname, TRUE) & FPC_SAME))
+ buf->b_ffname, TRUE, TRUE) & FPC_SAME))
{
int weight = 1;
copy_option_part(&p, NameBuff, MAXPATHL, ",");
mustfree = FALSE;
rt = vim_getenv((char_u *)"VIMRUNTIME", &mustfree);
- if (rt != NULL && fullpathcmp(rt, NameBuff, FALSE) != FPC_SAME)
+ if (rt != NULL &&
+ fullpathcmp(rt, NameBuff, FALSE, TRUE) != FPC_SAME)
{
int fcount;
char_u **fnames;
*/
ga_init2(&ga, (int)sizeof(char_u *), 100);
if (add_help_tags || fullpathcmp((char_u *)"$VIMRUNTIME/doc",
- dir, FALSE) == FPC_SAME)
+ dir, FALSE, TRUE) == FPC_SAME)
{
if (ga_grow(&ga, 1) == FAIL)
got_int = TRUE;
&& (win->w_buffer->b_ffname == NULL
|| !(fullpathcmp(
alist_name(&WARGLIST(win)[win->w_arg_idx]),
- win->w_buffer->b_ffname, TRUE) & FPC_SAME))));
+ win->w_buffer->b_ffname, TRUE, TRUE) & FPC_SAME))));
}
/*
&& (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
|| (win->w_buffer->b_ffname != NULL
&& (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
- win->w_buffer->b_ffname, TRUE) & FPC_SAME))))
+ win->w_buffer->b_ffname, TRUE, TRUE) & FPC_SAME))))
arg_had_last = TRUE;
}
else
&& (*eap->arg == NUL
|| setfname(curbuf, eap->arg, NULL, TRUE) == OK))
- ml_recover();
+ ml_recover(TRUE);
recoverymode = FALSE;
}
#endif
if ( fullpathcmp((char_u *)USR_GVIMRC_FILE,
- (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)GVIMRC_FILE, FALSE, TRUE) != FPC_SAME
#ifdef SYS_GVIMRC_FILE
&& fullpathcmp((char_u *)SYS_GVIMRC_FILE,
- (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)GVIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
#ifdef USR_GVIMRC_FILE2
&& fullpathcmp((char_u *)USR_GVIMRC_FILE2,
- (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)GVIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
#ifdef USR_GVIMRC_FILE3
&& fullpathcmp((char_u *)USR_GVIMRC_FILE3,
- (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)GVIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
#ifdef USR_GVIMRC_FILE4
&& fullpathcmp((char_u *)USR_GVIMRC_FILE4,
- (char_u *)GVIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)GVIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
)
do_source((char_u *)GVIMRC_FILE, TRUE, DOSO_GVIMRC);
#if defined(UNIX)
&& csinfo[j].st_dev == sb->st_dev && csinfo[j].st_ino == sb->st_ino
#else
- /* compare pathnames first */
+ // compare pathnames first
&& ((fullpathcmp((char_u *)csinfo[j].fname,
- (char_u *)fname, FALSE) & FPC_SAME)
- /* test index file attributes too */
+ (char_u *)fname, FALSE, TRUE) & FPC_SAME)
+ // test index file attributes too
|| (csinfo[j].nVolume == bhfi.dwVolumeSerialNumber
&& csinfo[j].nIndexHigh == bhfi.nFileIndexHigh
&& csinfo[j].nIndexLow == bhfi.nFileIndexLow))
if (recoverymode) /* do recover */
{
msg_scroll = TRUE; /* scroll message up */
- ml_recover();
+ ml_recover(TRUE);
if (curbuf->b_ml.ml_mfp == NULL) /* failed */
getout(1);
do_modelines(0); /* do modelines */
i = FAIL;
if (fullpathcmp((char_u *)USR_VIMRC_FILE,
- (char_u *)VIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)VIMRC_FILE, FALSE, TRUE) != FPC_SAME
#ifdef USR_VIMRC_FILE2
&& fullpathcmp((char_u *)USR_VIMRC_FILE2,
- (char_u *)VIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)VIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
#ifdef USR_VIMRC_FILE3
&& fullpathcmp((char_u *)USR_VIMRC_FILE3,
- (char_u *)VIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)VIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
#ifdef SYS_VIMRC_FILE
&& fullpathcmp((char_u *)SYS_VIMRC_FILE,
- (char_u *)VIMRC_FILE, FALSE) != FPC_SAME
+ (char_u *)VIMRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
)
i = do_source((char_u *)VIMRC_FILE, TRUE, DOSO_VIMRC);
secure = 0;
#endif
if ( fullpathcmp((char_u *)USR_EXRC_FILE,
- (char_u *)EXRC_FILE, FALSE) != FPC_SAME
+ (char_u *)EXRC_FILE, FALSE, TRUE) != FPC_SAME
#ifdef USR_EXRC_FILE2
&& fullpathcmp((char_u *)USR_EXRC_FILE2,
- (char_u *)EXRC_FILE, FALSE) != FPC_SAME
+ (char_u *)EXRC_FILE, FALSE, TRUE) != FPC_SAME
#endif
)
(void)do_source((char_u *)EXRC_FILE, FALSE, DOSO_NONE);
/*
* Try to recover curbuf from the .swp file.
+ * If "checkext" is TRUE, check the extension and detect whether it is
+ * a swap file.
*/
void
-ml_recover(void)
+ml_recover(int checkext)
{
buf_T *buf = NULL;
memfile_T *mfp = NULL;
if (fname == NULL) /* When there is no file name */
fname = (char_u *)"";
len = (int)STRLEN(fname);
- if (len >= 4 &&
+ if (checkext && len >= 4 &&
#if defined(VMS)
STRNICMP(fname + len - 4, "_s", 2)
#else
if (num_names == 0)
num_files = 0;
else if (expand_wildcards(num_names, names, &num_files, &files,
- EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
+ EW_NOTENV|EW_KEEPALL|EW_FILE|EW_SILENT) == FAIL)
num_files = 0;
/*
&& (p = curbuf->b_ml.ml_mfp->mf_fname) != NULL)
{
for (i = 0; i < num_files; ++i)
- if (fullpathcmp(p, files[i], TRUE) & FPC_SAME)
+ // Do not expand wildcards, on windows would try to expand
+ // "%tmp%" in "%tmp%file".
+ if (fullpathcmp(p, files[i], TRUE, FALSE) & FPC_SAME)
{
- /* Remove the name from files[i]. Move further entries
- * down. When the array becomes empty free it here, since
- * FreeWild() won't be called below. */
+ // Remove the name from files[i]. Move further entries
+ // down. When the array becomes empty free it here, since
+ // FreeWild() won't be called below.
vim_free(files[i]);
if (--num_files == 0)
vim_free(files);
* FPC_DIFF if they both exist and are different files.
* FPC_NOTX if they both don't exist.
* FPC_DIFFX if one of them doesn't exist.
- * For the first name environment variables are expanded
+ * For the first name environment variables are expanded if "expandenv" is
+ * TRUE.
*/
int
fullpathcmp(
char_u *s1,
char_u *s2,
- int checkname) /* when both don't exist, check file names */
+ int checkname, // when both don't exist, check file names
+ int expandenv)
{
#ifdef UNIX
char_u exp1[MAXPATHL];
stat_T st1, st2;
int r1, r2;
- expand_env(s1, exp1, MAXPATHL);
+ if (expandenv)
+ expand_env(s1, exp1, MAXPATHL);
+ else
+ vim_strncpy(exp1, s1, MAXPATHL - 1);
r1 = mch_stat((char *)exp1, &st1);
r2 = mch_stat((char *)s2, &st2);
if (r1 != 0 && r2 != 0)
full1 = exp1 + MAXPATHL;
full2 = full1 + MAXPATHL;
- expand_env(s1, exp1, MAXPATHL);
+ if (expandenv)
+ expand_env(s1, exp1, MAXPATHL);
+ else
+ vim_strncpy(exp1, s1, MAXPATHL - 1);
r1 = vim_FullName(exp1, full1, MAXPATHL, FALSE);
r2 = vim_FullName(s2, full2, MAXPATHL, FALSE);
/*
* First expand environment variables, "~/" and "~user/".
*/
- if (has_env_var(p) || *p == '~')
+ if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~')
{
p = expand_env_save_opt(p, TRUE);
if (p == NULL)
void ml_close_all(int del_file);
void ml_close_notmod(void);
void ml_timestamp(buf_T *buf);
-void ml_recover(void);
+void ml_recover(int checkext);
int recover_names(char_u *fname, int list, int nr, char_u **fname_out);
char_u *make_percent_swname(char_u *dir, char_u *name);
void get_b0_dict(char_u *fname, dict_T *d);
int match_user(char_u *name);
void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, int one);
char_u *home_replace_save(buf_T *buf, char_u *src);
-int fullpathcmp(char_u *s1, char_u *s2, int checkname);
+int fullpathcmp(char_u *s1, char_u *s2, int checkname, int expandenv);
char_u *gettail(char_u *fname);
char_u *gettail_sep(char_u *fname);
char_u *getnextcomp(char_u *fname);
i = old_files;
if (i == max_path_depth)
break;
- if (fullpathcmp(new_fname, files[i].name, TRUE) & FPC_SAME)
+ if (fullpathcmp(new_fname, files[i].name, TRUE, TRUE)
+ & FPC_SAME)
{
if (type != CHECK_PATH &&
action == ACTION_SHOW_ALL && files[i].matched)
/* Check if we loaded this language before. */
for (slang = first_lang; slang != NULL; slang = slang->sl_next)
- if (fullpathcmp(lang, slang->sl_fname, FALSE) == FPC_SAME)
+ if (fullpathcmp(lang, slang->sl_fname, FALSE, TRUE) == FPC_SAME)
break;
}
else
* Loop over the languages, there can be several files for "lang".
*/
for (slang = first_lang; slang != NULL; slang = slang->sl_next)
- if (filename ? fullpathcmp(lang, slang->sl_fname, FALSE) == FPC_SAME
+ if (filename ? fullpathcmp(lang, slang->sl_fname, FALSE, TRUE)
+ == FPC_SAME
: STRICMP(lang, slang->sl_name) == 0)
{
region_mask = REGION_ALL;
for (c = 0; c < ga.ga_len; ++c)
{
p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname;
- if (p != NULL && fullpathcmp(spf_name, p, FALSE) == FPC_SAME)
+ if (p != NULL && fullpathcmp(spf_name, p, FALSE, TRUE)
+ == FPC_SAME)
break;
}
if (c < ga.ga_len)
/* Check if it was loaded already. */
for (slang = first_lang; slang != NULL; slang = slang->sl_next)
- if (fullpathcmp(spf_name, slang->sl_fname, FALSE) == FPC_SAME)
+ if (fullpathcmp(spf_name, slang->sl_fname, FALSE, TRUE)
+ == FPC_SAME)
break;
if (slang == NULL)
{
for (slang = first_lang; slang != NULL; slang = slang->sl_next)
{
- if (fullpathcmp(fname, slang->sl_fname, FALSE) == FPC_SAME)
+ if (fullpathcmp(fname, slang->sl_fname, FALSE, TRUE) == FPC_SAME)
{
slang_clear(slang);
if (spell_load_file(fname, NULL, slang, FALSE) == NULL)
* It might have been done already by spell_reload_one().
*/
for (slang = first_lang; slang != NULL; slang = slang->sl_next)
- if (fullpathcmp(wfname, slang->sl_fname, FALSE) == FPC_SAME)
+ if (fullpathcmp(wfname, slang->sl_fname, FALSE, TRUE) == FPC_SAME)
break;
if (slang == NULL)
{
fullname = expand_tag_fname(fname, tag_fname, TRUE);
if (fullname != NULL)
{
- retval = (fullpathcmp(fullname, buf_ffname, TRUE) & FPC_SAME);
+ retval = (fullpathcmp(fullname, buf_ffname, TRUE, TRUE) & FPC_SAME);
vim_free(fullname);
}
#ifdef FEAT_EMACS_TAGS
augroup END
augroup! test_swapfile_delete
endfunc
+
+func Test_swap_recover()
+ autocmd! SwapExists
+ augroup test_swap_recover
+ autocmd!
+ autocmd SwapExists * let v:swapchoice = 'r'
+ augroup END
+
+
+ call mkdir('Xswap')
+ let $Xswap = 'foo' " Check for issue #4369.
+ set dir=Xswap//
+ " Create a valid swapfile by editing a file.
+ split Xswap/text
+ call setline(1, ['one', 'two', 'three'])
+ write " file is written, not modified
+ " read the swapfile as a Blob
+ let swapfile_name = swapname('%')
+ let swapfile_bytes = readfile(swapfile_name, 'B')
+
+ " Close the file and recreate the swap file.
+ quit
+ call writefile(swapfile_bytes, swapfile_name)
+ " Edit the file again. This triggers recovery.
+ try
+ split Xswap/text
+ catch
+ " E308 should be caught, not E305.
+ call assert_exception('E308:') " Original file may have been changed
+ endtry
+ " The file should be recovered.
+ call assert_equal(['one', 'two', 'three'], getline(1, 3))
+ quit!
+
+ call delete('Xswap/text')
+ call delete(swapfile_name)
+ call delete('Xswap', 'd')
+ unlet $Xswap
+ set dir&
+ augroup test_swap_recover
+ autocmd!
+ augroup END
+ augroup! test_swap_recover
+endfunc
+
+func Test_swap_recover_ext()
+ autocmd! SwapExists
+ augroup test_swap_recover_ext
+ autocmd!
+ autocmd SwapExists * let v:swapchoice = 'r'
+ augroup END
+
+
+ " Create a valid swapfile by editing a file with a special extension.
+ split Xtest.scr
+ call setline(1, ['one', 'two', 'three'])
+ write " file is written, not modified
+ write " write again to make sure the swapfile is created
+ " read the swapfile as a Blob
+ let swapfile_name = swapname('%')
+ let swapfile_bytes = readfile(swapfile_name, 'B')
+
+ " Close and delete the file and recreate the swap file.
+ quit
+ call delete('Xtest.scr')
+ call writefile(swapfile_bytes, swapfile_name)
+ " Edit the file again. This triggers recovery.
+ try
+ split Xtest.scr
+ catch
+ " E308 should be caught, not E306.
+ call assert_exception('E308:') " Original file may have been changed
+ endtry
+ " The file should be recovered.
+ call assert_equal(['one', 'two', 'three'], getline(1, 3))
+ quit!
+
+ call delete('Xtest.scr')
+ call delete(swapfile_name)
+ augroup test_swap_recover_ext
+ autocmd!
+ augroup END
+ augroup! test_swap_recover_ext
+endfunc
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1371,
/**/
1370,
/**/
#define WILD_ICASE 0x100
#define WILD_ALLLINKS 0x200
-/* Flags for expand_wildcards() */
-#define EW_DIR 0x01 /* include directory names */
-#define EW_FILE 0x02 /* include file names */
-#define EW_NOTFOUND 0x04 /* include not found names */
-#define EW_ADDSLASH 0x08 /* append slash to directory name */
-#define EW_KEEPALL 0x10 /* keep all matches */
-#define EW_SILENT 0x20 /* don't print "1 returned" from shell */
-#define EW_EXEC 0x40 /* executable files */
-#define EW_PATH 0x80 /* search in 'path' too */
-#define EW_ICASE 0x100 /* ignore case */
-#define EW_NOERROR 0x200 /* no error for bad regexp */
-#define EW_NOTWILD 0x400 /* add match with literal name if exists */
-#define EW_KEEPDOLLAR 0x800 /* do not escape $, $var is expanded */
-/* Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND
- * is used when executing commands and EW_SILENT for interactive expanding. */
-#define EW_ALLLINKS 0x1000 /* also links not pointing to existing file */
-#define EW_SHELLCMD 0x2000 /* called from expand_shellcmd(), don't check
- * if executable is in $PATH */
-#define EW_DODOT 0x4000 /* also files starting with a dot */
-#define EW_EMPTYOK 0x8000 /* no matches is not an error */
+// Flags for expand_wildcards()
+#define EW_DIR 0x01 // include directory names
+#define EW_FILE 0x02 // include file names
+#define EW_NOTFOUND 0x04 // include not found names
+#define EW_ADDSLASH 0x08 // append slash to directory name
+#define EW_KEEPALL 0x10 // keep all matches
+#define EW_SILENT 0x20 // don't print "1 returned" from shell
+#define EW_EXEC 0x40 // executable files
+#define EW_PATH 0x80 // search in 'path' too
+#define EW_ICASE 0x100 // ignore case
+#define EW_NOERROR 0x200 // no error for bad regexp
+#define EW_NOTWILD 0x400 // add match with literal name if exists
+#define EW_KEEPDOLLAR 0x800 // do not escape $, $var is expanded
+// Note: mostly EW_NOTFOUND and EW_SILENT are mutually exclusive: EW_NOTFOUND
+// is used when executing commands and EW_SILENT for interactive expanding.
+#define EW_ALLLINKS 0x1000 // also links not pointing to existing file
+#define EW_SHELLCMD 0x2000 // called from expand_shellcmd(), don't check
+ // if executable is in $PATH
+#define EW_DODOT 0x4000 // also files starting with a dot
+#define EW_EMPTYOK 0x8000 // no matches is not an error
+#define EW_NOTENV 0x10000 // do not expand environment variables
/* Flags for find_file_*() functions. */
#define FINDFILE_FILE 0 /* only files */