Awhile back I wrote a function to recursively create directories in C. It used a string builder to split the parts and rebuild the path.
The way mkdir
works is by taking a single directory that does not exist and creates it.
If there are multiple path parts that don’t exit it will error. Hence needing to split
the string into parts and create each part of the path separately. Earlier parts of the
path must exist before trying to add a later part.
For a project I was working on I needed to recursively make directories but I wasn’t able to utilize the string builder. So I wrote a version without it.
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
# include <Windows.h>
#else
# include <sys/stat.h>
#endif
#ifdef _WIN32
const char SEP = '\\';
#else
const char SEP = '/';
#endif
bool recurse_mkdir(const char *dirname)
{
const char *p;
char *temp;
bool ret = true;
temp = calloc(1, strlen(dirname)+1);
/* Skip Windows drive letter. */
#ifdef _WIN32
if ((p = strchr(dirname, ':') != NULL) {
p++;
} else {
#endif
p = dirname;
#ifdef _WIN32
}
#endif
while ((p = strchr(p, SEP)) != NULL) {
/* Skip empty elements. Could be a Windows UNC path or
just multiple separators which is okay. */
if (p != dirname && *(p-1) == SEP) {
p++;
continue;
}
/* Put the path up to this point into a temporary to
pass to the make directory function. */
memcpy(temp, dirname, p-dirname);
temp[p-dirname] = '\0';
p++;
#ifdef _WIN32
if (CreateDirectory(temp, NULL) == FALSE) {
if (GetLastError() != ERROR_ALREADY_EXISTS) {
ret = false;
break;
}
}
#else
if (mkdir(temp, 0774) != 0) {
if (errno != EEXIST) {
ret = false;
break;
}
}
#endif
}
free(temp);
return ret;
}
This version is similar but uses strchr
to search for parts and a memcpy
to a temporary
buffer to hold the path segment. The temporary is necessary because CreateDirectory
and mkdir
take NULL
terminated string, so we can’t pass a sub string directly.
Just like the previous version it will continue processing if it encounters a
directory that already exists. Also, it only returns false
on error so the caller
needs to check GetLastError
or errno
themselves if they want a detailed reason.