Recursive Create Directory in C

C has a very large gap when it comes to working with files and directories. It is C, so all the building blocks are there. Thankfully, it’s pretty easy to put together something useful. In a project I’ve been working on I needed to create a directory. This is pretty trivial but I needed to create a directory tree where all the parts might not already exist. Unfortunately C’s directory creation functions can’t make directories recursively. When I say C in this context, I really mean C code using either the POSIX or Win32 API’s.

One thing necessary to recursively create directories is a good string splitting helper function. Oh, and we’ll also need a string builder.

#ifdef _WIN32
const char SEP = '\';
#else
const char SEP = '/';
#endif

bool rw_create_dir(const char *name)
{
    str_builder_t  *sb;
    char          **parts;
    size_t          num_parts;
    size_t          i;
    bool            ret = true;

    if (name == NULL || *name == '\0')
        return false;

    parts = str_split(name, strlen(name), SEP, &num_parts, 0);
    if (parts == NULL || num_parts == 0) {
        str_split_free(parts, num_parts);
        return false;
    }

    sb = str_builder_create();
    i  = 0;
#ifdef _WIN32
    /* If the first part has a ':' it's a drive. E.g 'C:'. We don't
     * want to try creating it because we can't. We'll add it to base
     * and move forward. The next part will be a directory we need
     * to try creating. */
    if (strchr(parts[0], ':')) {
        i++;
        str_builder_add_str(sb, parts[0], strlen(parts[0]));
        str_builder_add_char(sb, SEP);
    }
#else
    if (*name == '/') {
        str_builder_add_char(sb, SEP);
    }
#endif

    for ( ; i<num_parts; i++) {
        if (parts[i] == NULL || *(parts[i]) == '\0') {
            continue;
        }

        str_builder_add_str(sb, parts[i], strlen(parts[i]));
        str_builder_add_char(sb, SEP);

#ifdef _WIN32
        if (CreateDirectory(str_builder_peek(sb), NULL) == FALSE) {
            if (GetLastError() != ERROR_ALREADY_EXISTS) {
                ret = false;
                goto done;
            }
        }
#else
        if (mkdir(str_builder_peek(sb), 0774) != 0)
            if (errno != EEXIST) {
                ret = false;
                goto done;
            }
#endif
    }

done:
    str_split_free(parts, num_parts);
    str_builder_destroy(sb);
    return ret;
}

This is actually a pretty simple function. The `#ifdef`’s make it look more complex than it is. This is the flow.

  1. Split the path into parts.
  2. Loop through the parts appending each one to the string builder on every iteration.
  3. On each iteration of the loop try to create the path at that point.
  4. If creating the directory fails with an error indicating the directory exists, keep going.
  5. Stop if any other error is returned.
  6. Once you’ve run out of parts and no errors are generated you know the directory and all parents were successfully created.

We do need to take special care with Windows because we don’t want to try and create the drive letter. We’ll check if the first component has a ‘:’ which can only exist with the drive letter. This will tell us if it’s an absolute path and we need to skip the first path component. Otherwise, it’s a relative path and we need to include it as part of the loop.

*nix also needs special handling for absolute paths. If the path starts with a ‘/’ it will be removed and replaced when the path is split into components. We need to check if it is an absolute path and add it to the string builder. Otherwise, we end up with a relative path.