aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.c42
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/globals.h1
-rw-r--r--src/nvim/os/fs.c50
4 files changed, 64 insertions, 31 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 0e0ccc67de..18188696b9 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -11684,33 +11684,6 @@ static void f_min(typval_T *argvars, typval_T *rettv)
max_min(argvars, rettv, FALSE);
}
-
-/*
- * Create the directory in which "dir" is located, and higher levels when
- * needed.
- */
-static int mkdir_recurse(char_u *dir, int prot)
-{
- char_u *p;
- char_u *updir;
- int r = FAIL;
-
- /* Get end of directory name in "dir".
- * We're done when it's "/" or "c:/". */
- p = path_tail_with_sep(dir);
- if (p <= get_past_head(dir))
- return OK;
-
- /* If the directory exists we're done. Otherwise: create it.*/
- updir = vim_strnsave(dir, (int)(p - dir));
- if (os_isdir(updir))
- r = OK;
- else if (mkdir_recurse(updir, prot) == OK)
- r = vim_mkdir_emsg(updir, prot);
- xfree(updir);
- return r;
-}
-
/*
* "mkdir()" function
*/
@@ -11735,8 +11708,19 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN) {
if (argvars[2].v_type != VAR_UNKNOWN)
prot = get_tv_number_chk(&argvars[2], NULL);
- if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0)
- mkdir_recurse(dir, prot);
+ if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) {
+ char *failed_dir;
+ int ret = os_mkdir_recurse((char *) dir, prot, &failed_dir);
+ if (ret != 0) {
+ EMSG3(_(e_mkdir), failed_dir, os_strerror(ret));
+ xfree(failed_dir);
+ rettv->vval.v_number = FAIL;
+ return;
+ } else {
+ rettv->vval.v_number = OK;
+ return;
+ }
+ }
}
rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot);
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 4182822dfa..3c57537397 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -7549,7 +7549,7 @@ int vim_mkdir_emsg(char_u *name, int prot)
{
int ret;
if ((ret = os_mkdir((char *)name, prot)) != 0) {
- EMSG3(_("E739: Cannot create directory %s: %s"), name, os_strerror(ret));
+ EMSG3(_(e_mkdir), name, os_strerror(ret));
return FAIL;
}
return OK;
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 9c4f9e3642..e4dcad9afb 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -1117,6 +1117,7 @@ EXTERN char_u e_jobtblfull[] INIT(= N_("E901: Job table is full"));
EXTERN char_u e_jobexe[] INIT(= N_("E902: \"%s\" is not an executable"));
EXTERN char_u e_jobnotpty[] INIT(= N_("E904: Job is not connected to a pty"));
EXTERN char_u e_libcall[] INIT(= N_("E364: Library call failed for \"%s()\""));
+EXTERN char_u e_mkdir[] INIT(= N_("E739: Cannot create directory %s: %s"));
EXTERN char_u e_markinval[] INIT(= N_("E19: Mark has invalid line number"));
EXTERN char_u e_marknotset[] INIT(= N_("E20: Mark not set"));
EXTERN char_u e_modifiable[] INIT(= N_(
diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c
index 553dda5e88..5eeb275701 100644
--- a/src/nvim/os/fs.c
+++ b/src/nvim/os/fs.c
@@ -316,7 +316,7 @@ int os_rename(const char_u *path, const char_u *new_path)
/// Make a directory.
///
-/// @return `0` for success, non-zero for failure.
+/// @return `0` for success, -errno for failure.
int os_mkdir(const char *path, int32_t mode)
FUNC_ATTR_NONNULL_ALL
{
@@ -326,6 +326,54 @@ int os_mkdir(const char *path, int32_t mode)
return result;
}
+/// Make a directory, with higher levels when needed
+///
+/// @param[in] dir Directory to create.
+/// @param[in] mode Permissions for the newly-created directory.
+/// @param[out] failed_dir If it failed to create directory, then this
+/// argument is set to an allocated string containing
+/// the name of the directory which os_mkdir_recurse
+/// failed to create. I.e. it will contain dir or any
+/// of the higher level directories.
+///
+/// @return `0` for success, -errno for failure.
+int os_mkdir_recurse(const char *const dir, int32_t mode,
+ char **const failed_dir)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ // Get end of directory name in "dir".
+ // We're done when it's "/" or "c:/".
+ const size_t dirlen = strlen(dir);
+ char *const curdir = xmemdupz(dir, dirlen);
+ char *const past_head = (char *) get_past_head((char_u *) curdir);
+ char *e = curdir + dirlen;
+ const char *const real_end = e;
+ const char past_head_save = *past_head;
+ while (!os_isdir((char_u *) curdir)) {
+ e = (char *) path_tail_with_sep((char_u *) curdir);
+ if (e <= past_head) {
+ *past_head = NUL;
+ break;
+ }
+ *e = NUL;
+ }
+ while (e != real_end) {
+ if (e > past_head) {
+ *e = '/';
+ } else {
+ *past_head = past_head_save;
+ }
+ e += strlen(e);
+ int ret;
+ if ((ret = os_mkdir(curdir, mode)) != 0) {
+ *failed_dir = curdir;
+ return ret;
+ }
+ }
+ xfree(curdir);
+ return 0;
+}
+
/// Create a unique temporary directory.
///
/// @param[in] template Template of the path to the directory with XXXXXX