diff options
| -rw-r--r-- | src/nvim/eval.c | 42 | ||||
| -rw-r--r-- | src/nvim/ex_docmd.c | 2 | ||||
| -rw-r--r-- | src/nvim/globals.h | 1 | ||||
| -rw-r--r-- | src/nvim/os/fs.c | 50 | ||||
| -rw-r--r-- | test/unit/os/fs_spec.lua | 63 | 
5 files changed, 127 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 diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index 2ffffb907f..20aca9109e 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -486,6 +486,16 @@ describe('fs function', function()        return fs.os_rmdir(to_cstr(path))      end +    local function os_mkdir_recurse(path, mode) +      local failed_str = ffi.new('char *[1]', {nil}) +      local ret = fs.os_mkdir_recurse(path, mode, failed_str) +      local str = failed_str[0] +      if str ~= nil then +        str = ffi.string(str) +      end +      return ret, str +    end +      describe('os_mkdir', function()        it('returns non-zero when given an already existing directory', function()          local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR @@ -501,6 +511,59 @@ describe('fs function', function()        end)      end) +    describe('os_mkdir_recurse', function() +      it('returns zero when given an already existing directory', function() +        local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR +        local ret, failed_str = os_mkdir_recurse('unit-test-directory', mode) +        eq(0, ret) +        eq(nil, failed_str) +      end) + +      it('fails to create a directory where there is a file', function() +        local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR +        local ret, failed_str = os_mkdir_recurse( +            'unit-test-directory/test.file', mode) +        neq(0, ret) +        eq('unit-test-directory/test.file', failed_str) +      end) + +      it('fails to create a directory where there is a file in path', function() +        local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR +        local ret, failed_str = os_mkdir_recurse( +            'unit-test-directory/test.file/test', mode) +        neq(0, ret) +        eq('unit-test-directory/test.file', failed_str) +      end) + +      it('succeeds to create a directory', function() +        local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR +        local ret, failed_str = os_mkdir_recurse( +            'unit-test-directory/new-dir-recurse', mode) +        eq(0, ret) +        eq(nil, failed_str) +        eq(true, os_isdir('unit-test-directory/new-dir-recurse')) +        lfs.rmdir('unit-test-directory/new-dir-recurse') +        eq(false, os_isdir('unit-test-directory/new-dir-recurse')) +      end) + +      it('succeeds to create a directory tree', function() +        local mode = ffi.C.kS_IRUSR + ffi.C.kS_IWUSR + ffi.C.kS_IXUSR +        local ret, failed_str = os_mkdir_recurse( +            'unit-test-directory/new-dir-recurse/1/2/3', mode) +        eq(0, ret) +        eq(nil, failed_str) +        eq(true, os_isdir('unit-test-directory/new-dir-recurse')) +        eq(true, os_isdir('unit-test-directory/new-dir-recurse/1')) +        eq(true, os_isdir('unit-test-directory/new-dir-recurse/1/2')) +        eq(true, os_isdir('unit-test-directory/new-dir-recurse/1/2/3')) +        lfs.rmdir('unit-test-directory/new-dir-recurse/1/2/3') +        lfs.rmdir('unit-test-directory/new-dir-recurse/1/2') +        lfs.rmdir('unit-test-directory/new-dir-recurse/1') +        lfs.rmdir('unit-test-directory/new-dir-recurse') +        eq(false, os_isdir('unit-test-directory/new-dir-recurse')) +      end) +    end) +      describe('os_rmdir', function()        it('returns non_zero when given a non-existing directory', function()          neq(0, (os_rmdir('non-existing-directory'))) | 
