Refactor: unify UNIX/WIN32 BLI_dir_create_recursive & some improvements
- Don't duplicate/allocate the path on each recursion, instead make a single copy which is modified in-place. - Move slash-stepping to a BLI_path utility function that accesses the end position of the parent directory, handles multiple slashes and '/./'.
This commit is contained in:
parent
f7d3115d97
commit
de5aca9418
@ -411,6 +411,20 @@ bool BLI_path_parent_dir(char *path) ATTR_NONNULL(1);
|
||||
*/
|
||||
bool BLI_path_parent_dir_until_exists(char *path) ATTR_NONNULL(1);
|
||||
|
||||
/**
|
||||
* In the simple case this is similar to `BLI_path_slash_rfind(dirname)`
|
||||
* however it behaves differently when there are redundant characters:
|
||||
*
|
||||
* `/test///dir/./file`
|
||||
* ^
|
||||
* `/test/dir/subdir//file`
|
||||
* ^
|
||||
* \return The position after the parent paths last character or NULL on failure.
|
||||
* Neither `path` or `&path[path_len - 1]` are ever returned.
|
||||
*/
|
||||
const char *BLI_path_parent_dir_end(const char *path, size_t path_len)
|
||||
ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* If path begins with "//", strips that and replaces it with `basepath` directory.
|
||||
*
|
||||
|
@ -285,6 +285,89 @@ bool BLI_file_touch(const char *filepath)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool dir_create_recursive(char *dirname, int len)
|
||||
{
|
||||
BLI_assert(strlen(dirname) == len);
|
||||
BLI_assert(BLI_exists(dirname) == 0);
|
||||
/* Caller must ensure the path doesn't have trailing slashes. */
|
||||
BLI_assert(len && !BLI_path_slash_is_native_compat(dirname[len - 1]));
|
||||
|
||||
bool ret = true;
|
||||
char *dirname_parent_end = (char *)BLI_path_parent_dir_end(dirname, len);
|
||||
if (dirname_parent_end) {
|
||||
const char dirname_parent_end_value = *dirname_parent_end;
|
||||
*dirname_parent_end = '\0';
|
||||
#ifdef WIN32
|
||||
/* Check special case `c:\foo`, don't try create `c:`, harmless but unnecessary. */
|
||||
if (dirname[0] && !(isalpha(dirname[0]) && (dirname[1] == ':') && dirname[2] == '\0'))
|
||||
#endif
|
||||
{
|
||||
const int mode = BLI_exists(dirname);
|
||||
if (mode != 0) {
|
||||
if (!S_ISDIR(mode)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
else if (!dir_create_recursive(dirname, dirname_parent_end - dirname)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
*dirname_parent_end = dirname_parent_end_value;
|
||||
}
|
||||
if (ret) {
|
||||
#ifdef WIN32
|
||||
if (umkdir(dirname) == -1) {
|
||||
ret = false;
|
||||
}
|
||||
#else
|
||||
if (mkdir(dirname, 0777) != 0) {
|
||||
ret = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool BLI_dir_create_recursive(const char *dirname)
|
||||
{
|
||||
const int mode = BLI_exists(dirname);
|
||||
if (mode != 0) {
|
||||
/* The file exists, either it's a directory (ok), or not,
|
||||
* in which case this function can't do anything useful
|
||||
* (the caller could remove it and re-run this function). */
|
||||
return S_ISDIR(mode) ? true : false;
|
||||
}
|
||||
|
||||
size_t len;
|
||||
char *dirname_mut;
|
||||
|
||||
#ifdef MAXPATHLEN
|
||||
char static_buf[MAXPATHLEN];
|
||||
len = BLI_strncpy_rlen(static_buf, dirname, sizeof(static_buf));
|
||||
dirname_mut = static_buf;
|
||||
#else
|
||||
len = strlen(dirname);
|
||||
dirname_mut = MEM_mallocN(len + 1, __func__);
|
||||
memcpy(dirname_mut, dirname, len + 1)
|
||||
#endif
|
||||
|
||||
while ((len > 0) && BLI_path_slash_is_native_compat(dirname_mut[len - 1])) {
|
||||
len--;
|
||||
}
|
||||
dirname_mut[len] = '\0';
|
||||
|
||||
const bool ret = (len > 0) && dir_create_recursive(dirname_mut, len);
|
||||
|
||||
/* Ensure the string was properly restored. */
|
||||
BLI_assert(memcmp(dirname, dirname_mut, len) == 0);
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
MEM_freeN(dirname_mut);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool BLI_file_ensure_parent_dir_exists(const char *filepath)
|
||||
{
|
||||
char di[FILE_MAX];
|
||||
@ -609,52 +692,6 @@ int BLI_create_symlink(const char *file, const char *to)
|
||||
# endif
|
||||
|
||||
/** \return true on success (i.e. given path now exists on FS), false otherwise. */
|
||||
bool BLI_dir_create_recursive(const char *dirname)
|
||||
{
|
||||
char *lslash;
|
||||
char tmp[MAXPATHLEN];
|
||||
bool ret = true;
|
||||
|
||||
/* First remove possible slash at the end of the dirname.
|
||||
* This routine otherwise tries to create
|
||||
* blah1/blah2/ (with slash) after creating
|
||||
* blah1/blah2 (without slash) */
|
||||
|
||||
BLI_strncpy(tmp, dirname, sizeof(tmp));
|
||||
BLI_path_slash_native(tmp);
|
||||
BLI_path_slash_rstrip(tmp);
|
||||
|
||||
/* check special case "c:\foo", don't try create "c:", harmless but prints an error below */
|
||||
if (isalpha(tmp[0]) && (tmp[1] == ':') && tmp[2] == '\0') {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (BLI_is_dir(tmp)) {
|
||||
return true;
|
||||
}
|
||||
else if (BLI_exists(tmp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lslash = (char *)BLI_path_slash_rfind(tmp);
|
||||
|
||||
if (lslash) {
|
||||
/* Split about the last slash and recurse */
|
||||
*lslash = 0;
|
||||
if (!BLI_dir_create_recursive(tmp)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret && dirname[0]) { /* patch, this recursive loop tries to create a nameless directory */
|
||||
if (umkdir(dirname) == -1) {
|
||||
printf("Unable to create directory %s\n", dirname);
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int BLI_rename(const char *from, const char *to)
|
||||
{
|
||||
if (!BLI_exists(from)) {
|
||||
@ -1286,56 +1323,6 @@ int BLI_create_symlink(const char *file, const char *to)
|
||||
}
|
||||
# endif
|
||||
|
||||
bool BLI_dir_create_recursive(const char *dirname)
|
||||
{
|
||||
char *lslash;
|
||||
size_t size;
|
||||
# ifdef MAXPATHLEN
|
||||
char static_buf[MAXPATHLEN];
|
||||
# endif
|
||||
char *tmp;
|
||||
bool ret = true;
|
||||
|
||||
const int mode = BLI_exists(dirname);
|
||||
if (mode != 0) {
|
||||
/* The file exists, either it's a directory (ok), or not,
|
||||
* in which case this function can't do anything useful
|
||||
* (the caller could remove it and re-run this function). */
|
||||
return S_ISDIR(mode) ? true : false;
|
||||
}
|
||||
|
||||
# ifdef MAXPATHLEN
|
||||
size = MAXPATHLEN;
|
||||
tmp = static_buf;
|
||||
# else
|
||||
size = strlen(dirname) + 1;
|
||||
tmp = MEM_mallocN(size, __func__);
|
||||
# endif
|
||||
|
||||
BLI_strncpy(tmp, dirname, size);
|
||||
|
||||
/* Avoids one useless recursion in case of '/foo/bar/' path... */
|
||||
BLI_path_slash_rstrip(tmp);
|
||||
|
||||
lslash = (char *)BLI_path_slash_rfind(tmp);
|
||||
if (lslash) {
|
||||
/* Split about the last slash and recurse */
|
||||
*lslash = 0;
|
||||
if (!BLI_dir_create_recursive(tmp)) {
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
|
||||
# ifndef MAXPATHLEN
|
||||
MEM_freeN(tmp);
|
||||
# endif
|
||||
|
||||
if (ret) {
|
||||
ret = (mkdir(dirname, 0777) == 0);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int BLI_rename(const char *from, const char *to)
|
||||
{
|
||||
if (!BLI_exists(from)) {
|
||||
|
@ -752,6 +752,33 @@ bool BLI_path_suffix(char *path, size_t path_maxncpy, const char *suffix, const
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *BLI_path_parent_dir_end(const char *path, size_t path_len)
|
||||
{
|
||||
const char *path_end = path + path_len - 1;
|
||||
const char *p = path_end;
|
||||
while (p >= path) {
|
||||
if (BLI_path_slash_is_native_compat(*p)) {
|
||||
break;
|
||||
}
|
||||
p--;
|
||||
}
|
||||
while (p > path) {
|
||||
if (BLI_path_slash_is_native_compat(*(p - 1))) {
|
||||
p -= 1; /* Skip `/`. */
|
||||
}
|
||||
else if ((p + 1 > path) && (*(p - 1) == '.') && BLI_path_slash_is_native_compat(*p - 2)) {
|
||||
p -= 2; /* Skip `/.` (actually `/./` but the last slash was already skipped) */
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((p > path) && (p != path_end)) {
|
||||
return p;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool BLI_path_parent_dir(char *path)
|
||||
{
|
||||
/* Use #BLI_path_name_at_index instead of checking if the strings ends with `parent_dir`
|
||||
|
Loading…
Reference in New Issue
Block a user