memline: Automatically create swap file directory for last directory

This commit is contained in:
ZyX 2015-10-19 15:27:53 +03:00
parent fefcc01cc1
commit a8e18d9b5a
2 changed files with 80 additions and 50 deletions

View File

@ -2133,7 +2133,8 @@ A jump table for the options with a short description can be found at |Q_op|.
global global
List of directory names for the swap file, separated with commas. List of directory names for the swap file, separated with commas.
- The swap file will be created in the first directory where this is - The swap file will be created in the first directory where this is
possible. possible. If it is not possible in any directory, but last
directory listed in the option does not exist, it is created.
- Empty means that no swap file will be used (recovery is - Empty means that no swap file will be used (recovery is
impossible!). impossible!).
- A directory "." means to put the swap file in the same directory as - A directory "." means to put the swap file in the same directory as

View File

@ -406,10 +406,12 @@ void ml_setname(buf_T *buf)
* Try all directories in the 'directory' option. * Try all directories in the 'directory' option.
*/ */
dirp = p_dir; dirp = p_dir;
bool found_existing_dir = false;
for (;; ) { for (;; ) {
if (*dirp == NUL) /* tried all directories, fail */ if (*dirp == NUL) /* tried all directories, fail */
break; break;
fname = findswapname(buf, &dirp, mfp->mf_fname); fname = (char_u *)findswapname(buf, (char **)&dirp, (char *)mfp->mf_fname,
&found_existing_dir);
/* alloc's fname */ /* alloc's fname */
if (dirp == NULL) /* out of memory */ if (dirp == NULL) /* out of memory */
break; break;
@ -504,13 +506,15 @@ void ml_open_file(buf_T *buf)
* Try all directories in 'directory' option. * Try all directories in 'directory' option.
*/ */
dirp = p_dir; dirp = p_dir;
bool found_existing_dir = false;
for (;; ) { for (;; ) {
if (*dirp == NUL) if (*dirp == NUL)
break; break;
/* There is a small chance that between choosing the swap file name // There is a small chance that between choosing the swap file name
* and creating it, another Vim creates the file. In that case the // and creating it, another Vim creates the file. In that case the
* creation will fail and we will use another directory. */ // creation will fail and we will use another directory.
fname = findswapname(buf, &dirp, NULL); /* allocates fname */ fname = (char_u *)findswapname(buf, (char **)&dirp, NULL,
&found_existing_dir);
if (dirp == NULL) if (dirp == NULL)
break; /* out of memory */ break; /* out of memory */
if (fname == NULL) if (fname == NULL)
@ -3222,45 +3226,56 @@ static int do_swapexists(buf_T *buf, char_u *fname)
return 0; return 0;
} }
/* /// Find out what name to use for the swap file for buffer 'buf'.
* Find out what name to use for the swap file for buffer 'buf'. ///
* /// Several names are tried to find one that does not exist. Last directory in
* Several names are tried to find one that does not exist /// option is automatically created.
* Returns the name in allocated memory or NULL. ///
* When out of memory "dirp" is set to NULL. /// @note If BASENAMELEN is not correct, you will get error messages for
* /// not being able to open the swap or undo file.
* Note: If BASENAMELEN is not correct, you will get error messages for /// @note May trigger SwapExists autocmd, pointers may change!
* not being able to open the swap or undo file ///
* Note: May trigger SwapExists autocmd, pointers may change! /// @param[in] buf Buffer for which swap file names needs to be found.
*/ /// @param[in,out] dirp Pointer to a list of directories. When out of memory,
static char_u * /// is set to NULL. Is advanced to the next directory in
findswapname ( /// the list otherwise.
buf_T *buf, /// @param[in] old_fname Allowed existing swap file name. Except for this
char_u **dirp, /* pointer to list of directories */ /// case, name of the non-existing file is used.
char_u *old_fname /* don't give warning for this file name */ /// @param[in,out] found_existing_dir If points to true, then new directory
) /// for swap file is not created. At first
/// findswapname() call this argument must
/// point to false. This parameter may only
/// be set to true by this function, it is
/// never set to false.
///
/// @return [allocated] Name of the swap file.
static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
bool *found_existing_dir)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1, 2, 4)
{ {
char_u *fname; char *fname;
int n; size_t n;
char_u *dir_name; char *dir_name;
char_u *buf_fname = buf->b_fname; char *buf_fname = (char *) buf->b_fname;
/* /*
* Isolate a directory name from *dirp and put it in dir_name. * Isolate a directory name from *dirp and put it in dir_name.
* First allocate some memory to put the directory name in. * First allocate some memory to put the directory name in.
*/ */
dir_name = xmalloc(STRLEN(*dirp) + 1); const size_t dir_len = strlen(*dirp);
(void)copy_option_part(dirp, dir_name, 31000, ","); dir_name = xmalloc(dir_len + 1);
(void)copy_option_part((char_u **) dirp, (char_u *) dir_name, dir_len, ",");
/* /*
* 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
*/ */
fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name); fname = (char *)makeswapname((char_u *)buf_fname, buf->b_ffname, buf,
(char_u *)dir_name);
for (;; ) { for (;; ) {
if (fname == NULL) /* must be out of memory */ if (fname == NULL) /* must be out of memory */
break; break;
if ((n = (int)STRLEN(fname)) == 0) { /* safety check */ if ((n = strlen(fname)) == 0) { /* safety check */
xfree(fname); xfree(fname);
fname = NULL; fname = NULL;
break; break;
@ -3269,7 +3284,7 @@ findswapname (
// Extra security check: When a swap file is a symbolic link, this // Extra security check: When a swap file is a symbolic link, this
// is most likely a symlink attack. // is most likely a symlink attack.
FileInfo file_info; FileInfo file_info;
bool file_or_link_found = os_fileinfo_link((char *)fname, &file_info); bool file_or_link_found = os_fileinfo_link(fname, &file_info);
if (!file_or_link_found) { if (!file_or_link_found) {
break; break;
} }
@ -3300,7 +3315,7 @@ findswapname (
* Try to read block 0 from the swap file to get the original * Try to read block 0 from the swap file to get the original
* file name (and inode number). * file name (and inode number).
*/ */
fd = os_open((char *)fname, O_RDONLY, 0); fd = os_open(fname, O_RDONLY, 0);
if (fd >= 0) { if (fd >= 0) {
if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) {
/* /*
@ -3311,7 +3326,7 @@ findswapname (
if (b0.b0_flags & B0_SAME_DIR) { if (b0.b0_flags & B0_SAME_DIR) {
if (fnamecmp(path_tail(buf->b_ffname), if (fnamecmp(path_tail(buf->b_ffname),
path_tail(b0.b0_fname)) != 0 path_tail(b0.b0_fname)) != 0
|| !same_directory(fname, buf->b_ffname)) { || !same_directory((char_u *) fname, buf->b_ffname)) {
/* Symlinks may point to the same file even /* Symlinks may point to the same file even
* when the name differs, need to check the * when the name differs, need to check the
* inode too. */ * inode too. */
@ -3351,12 +3366,12 @@ findswapname (
* user anyway. * user anyway.
*/ */
if (swap_exists_action != SEA_NONE if (swap_exists_action != SEA_NONE
&& has_autocmd(EVENT_SWAPEXISTS, buf_fname, buf)) && has_autocmd(EVENT_SWAPEXISTS, (char_u *) buf_fname, buf))
choice = do_swapexists(buf, fname); choice = do_swapexists(buf, (char_u *) fname);
if (choice == 0) { if (choice == 0) {
/* Show info about the existing swap file. */ /* Show info about the existing swap file. */
attention_message(buf, fname); attention_message(buf, (char_u *) fname);
/* We don't want a 'q' typed at the more-prompt /* We don't want a 'q' typed at the more-prompt
* interrupt loading a file. */ * interrupt loading a file. */
@ -3364,20 +3379,21 @@ findswapname (
} }
if (swap_exists_action != SEA_NONE && choice == 0) { if (swap_exists_action != SEA_NONE && choice == 0) {
char_u *name; char *name;
name = xmalloc(STRLEN(fname) const size_t fname_len = strlen(fname);
+ STRLEN(_("Swap file \"")) name = xmalloc(fname_len
+ STRLEN(_("\" already exists!")) + 5); + strlen(_("Swap file \""))
STRCPY(name, _("Swap file \"")); + strlen(_("\" already exists!")) + 5);
home_replace(NULL, fname, name + STRLEN(name), strcpy(name, _("Swap file \""));
1000, TRUE); home_replace(NULL, (char_u *) fname, (char_u *)&name[strlen(name)],
STRCAT(name, _("\" already exists!")); fname_len, true);
strcat(name, _("\" already exists!"));
choice = do_dialog(VIM_WARNING, choice = do_dialog(VIM_WARNING,
(char_u *)_("VIM - ATTENTION"), (char_u *)_("VIM - ATTENTION"),
name == NULL (char_u *)(name == NULL
? (char_u *)_("Swap file already exists!") ? _("Swap file already exists!")
: name, : name),
# if defined(UNIX) # if defined(UNIX)
process_still_running process_still_running
? (char_u *)_( ? (char_u *)_(
@ -3409,7 +3425,7 @@ findswapname (
swap_exists_action = SEA_RECOVER; swap_exists_action = SEA_RECOVER;
break; break;
case 4: case 4:
os_remove((char *)fname); os_remove(fname);
break; break;
case 5: case 5:
swap_exists_action = SEA_QUIT; swap_exists_action = SEA_QUIT;
@ -3421,7 +3437,7 @@ findswapname (
} }
/* If the file was deleted this fname can be used. */ /* If the file was deleted this fname can be used. */
if (!os_file_exists(fname)) if (!os_file_exists((char_u *) fname))
break; break;
} else } else
{ {
@ -3454,6 +3470,19 @@ findswapname (
--fname[n - 1]; /* ".swo", ".swn", etc. */ --fname[n - 1]; /* ".swo", ".swn", etc. */
} }
if (os_isdir((char_u *) dir_name)) {
*found_existing_dir = true;
} else if (!*found_existing_dir && **dirp == NUL) {
int ret;
char *failed_dir;
if ((ret = os_mkdir_recurse(dir_name, 0755, &failed_dir)) != 0) {
EMSG3(_("E303: Unable to create directory \"%s\" for swap file, "
"recovery impossible: %s"),
failed_dir, os_strerror(ret));
xfree(failed_dir);
}
}
xfree(dir_name); xfree(dir_name);
return fname; return fname;
} }