Recursive mkdir() system call on Unix
Asked Answered
U

17

81

After reading the mkdir(2) man page for the Unix system call with that name, it appears that the call doesn't create intermediate directories in a path, only the last directory in the path. Is there any way (or other function) to create all the directories in the path without resorting to manually parsing my directory string and individually creating each directory ?

Unplumbed answered 25/2, 2010 at 17:41 Comment(1)
Please don't add any more implementations of this function as answers.Bradley
F
113

There is not a system call to do it for you, unfortunately. I'm guessing that's because there isn't a way to have really well-defined semantics for what should happen in error cases. Should it leave the directories that have already been created? Delete them? What if the deletions fail? And so on...

It is pretty easy to roll your own, however, and a quick google for 'recursive mkdir' turned up a number of solutions. Here's one that was near the top:

http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html

static void _mkdir(const char *dir) {
    char tmp[256];
    char *p = NULL;
    size_t len;

    snprintf(tmp, sizeof(tmp),"%s",dir);
    len = strlen(tmp);
    if (tmp[len - 1] == '/')
        tmp[len - 1] = 0;
    for (p = tmp + 1; *p; p++)
        if (*p == '/') {
            *p = 0;
            mkdir(tmp, S_IRWXU);
            *p = '/';
        }
    mkdir(tmp, S_IRWXU);
}
Footmark answered 25/2, 2010 at 17:42 Comment(6)
The only thing I would change is tmp[256] to tmp[PATH_MAX] also #include <limits.h>Couvade
Improved version: gist.github.com/JonathonReinhart/…Rutabaga
snprintf() returns the length of the string formatted, the call to strlen() is superfluous. len = snprintf(tmp, ... . You can check for buffer overflow that way if(len >= sizeof tmp). With the strlen() it's not possible.Lozada
What if mkdir in loop failed to create a directory? For instance of permissions? I would strongly recommend mentioned version from github gist.github.com/JonathonReinhart/…Reisfield
@Couvade PATH_MAX may not be an improvement as PATH_MAX will not be defined on POSIX-compliant systems where the value varies between different file systems (bolding mine): "A definition of one of the symbolic constants in the following list shall be omitted from the <limits.h> header on specific implementations where the corresponding value is equal to or greater than the stated minimum, but where the value can vary depending on the file to which it is applied."Permatron
Since PATH_MAX may vary between devices and for other reasons, I would recommend calling snprintf() once to find the size of the buffer required; malloc that (or malloca()), then use that buffer (and free it)(unless you used malloca(), which is freed upon return as it allocates space from the stack).Chaisson
R
69

hmm I thought that mkdir -p does that?

mkdir -p this/is/a/full/path/of/stuff

Ronda answered 11/7, 2012 at 4:43 Comment(9)
Yes it does, but the question pertains to a C function call.Zeta
Indeed - the upvotes presumably reflect that this has been a useful answer for many, but it is an answer to a different question than the one which was asked.Pellagra
One could however have a look at the source code for mkdir to see how it does it. Doing a quick google, it seems the relevant code is in mkancestdirs.c in coreutilsIllfated
This is an answer for different question asked.Hickman
@gamen, The link in your comment is stale.Naseby
@Naseby thanks for noticing. The latest equivalent URL (for coreutils 8.26) is: fossies.org/dox/coreutils-8.26/mkancesdirs_8c_source.html. Reflecting on past criticisms of this suggested answer, I agree. It should rather have been a comment to the question.Illfated
@CraigMcQueen Sure. system("mkdir -p this/is/a/full/path/of/stuff");Bradley
@JasonC Perhaps, though using system("mkdir -p ...") is risky if the directory name is user input; then the user input requires careful sanitisation/validation. C system() function vulnerability - Code Review Stack ExchangeZeta
@CraigMcQueen Truth. Then: !fork() && execlp("mkdir", "mkdir", "-p", pathname, NULL).Bradley
A
30

Here is my solution. By calling the function below you ensure that all dirs leading to the file path specified exist. Note that file_path argument is not directory name here but rather a path to a file that you are going to create after calling mkpath().

Eg., mkpath("/home/me/dir/subdir/file.dat", 0755) shall create /home/me/dir/subdir if it does not exist. mkpath("/home/me/dir/subdir/", 0755) does the same.

Works with relative paths as well.

Returns -1 and sets errno in case of an error.

int mkpath(char* file_path, mode_t mode) {
    assert(file_path && *file_path);
    for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) {
        *p = '\0';
        if (mkdir(file_path, mode) == -1) {
            if (errno != EEXIST) {
                *p = '/';
                return -1;
            }
        }
        *p = '/';
    }
    return 0;
}

Note that file_path is modified during the action but gets restored afterwards. Therefore file_path is not strictly const.

Athanasia answered 9/2, 2012 at 12:37 Comment(4)
better than the accepted answer; with error handling!Gervase
The only problem is that it uses a non-const char * as parameter because it changes the contents of the original pointer. This is not ideal since it wont work with static const strings, for example, and it has an unnecessary API requirement.Parasitic
@FelipeTonello You can easily allocate memory and make a copy of the parameter as suggested in other answers. My goal was performance so I tried to avoid expensive operations such as memory allocation.Athanasia
*My goal was performance so I tried to avoid expensive operations such as memory allocation -- @YaroslavStavnichiy going to disk to conduct file operations is going to be perhaps hundreds of thousands of times slower than the memory allocation. It is always good to keep performance in mind, of course, and code from one situation is often cut and pasted into other situations. malloca() is good when you have multiple return points like this, as it automatically frees (since it allocates on the stack) and is quite fast.Chaisson
G
14

Here's another take on mkpath(), using recursion, which is both small and readable. It makes use of strdupa() to avoid altering the given dir string argument directly and to avoid using malloc() & free(). Make sure to compile with -D_GNU_SOURCE to activate strdupa() ... meaning this code only works on GLIBC, EGLIBC, uClibc, and other GLIBC compatible C libraries.

int mkpath(char *dir, mode_t mode)
{
    if (!dir) {
        errno = EINVAL;
        return 1;
    }

    if (strlen(dir) == 1 && dir[0] == '/')
        return 0;

    mkpath(dirname(strdupa(dir)), mode);

    return mkdir(dir, mode);
}

After input both here and from Valery Frolov, in the Inadyn project, the following revised version of mkpath() has now been pushed to libite

int mkpath(char *dir, mode_t mode)
{
    struct stat sb;

    if (!dir) {
        errno = EINVAL;
        return 1;
    }

    if (!stat(dir, &sb))
        return 0;

    mkpath(dirname(strdupa(dir)), mode);

    return mkdir(dir, mode);
}

It uses one more syscall, but otoh the code is more readable now.

Grapevine answered 17/12, 2013 at 13:1 Comment(9)
Wouldn't this leak memory every time strdupa is called?Leandraleandre
Nope :) When the function goes out of context the memory allocated (on the stack) by strdupa() is freed automatically. See the man page for more details.Grapevine
Ah, sorry, you're right. I thought of strdup all the time. :)Leandraleandre
I know this is a very old thread, but I was looking for a solution to this and I liked this answer the best out of everything I've seen. HOWEVER, this does not work with a relative path. Adding a check for dir[0] == '.' to the check for dir[0]=='/' would correct this as that is what dirname ends up with.Loupgarou
This still isn't ideal as it tries to create the ".." folder, but that just returns a file exists error and keeps going. You could also do a mkdir first, and only recurse if mkdir returns ENOENT. Downside to that is you would have to call mkdir twice in the method.Loupgarou
Thanks, my personal use-case didn't trigger that. Got input on this relative path problem also in the Inadyn project. See revised code above.Grapevine
Very elegant solution! I ran it under valgrind and indeed it confirms there is no leak memory but the man page states the memory has to be released calling free: http://linux.die.net/man/3/strdupa. How ever, adding the #include statements would make it a bit easier to test it... :-)Proximity
@AlBundy check the man page again, the statement regarding free() is for strdup(), not strdupa().Grapevine
De verdad! Mea culpa muchisomo! I implemented it as a lambda function and it works perfectly!Proximity
G
9

Take a look at the bash source code here, and specifically look in examples/loadables/mkdir.c especially lines 136-210. If you don't want to do that, here's some of the source that deals with this (taken straight from the tar.gz that I've linked):

/* Make all the directories leading up to PATH, then create PATH.  Note that
   this changes the process's umask; make sure that all paths leading to a
   return reset it to ORIGINAL_UMASK */

static int
make_path (path, nmode, parent_mode)
     char *path;
     int nmode, parent_mode;
{
  int oumask;
  struct stat sb;
  char *p, *npath;

  if (stat (path, &sb) == 0)
  {
      if (S_ISDIR (sb.st_mode) == 0)
      {
          builtin_error ("`%s': file exists but is not a directory", path);
          return 1;
      }

      if (chmod (path, nmode))
      {
          builtin_error ("%s: %s", path, strerror (errno));
          return 1;
      }

      return 0;
  }

  oumask = umask (0);
  npath = savestring (path);    /* So we can write to it. */

  /* Check whether or not we need to do anything with intermediate dirs. */

  /* Skip leading slashes. */
  p = npath;
  while (*p == '/')
    p++;

  while (p = strchr (p, '/'))
  {
      *p = '\0';
      if (stat (npath, &sb) != 0)
      {
          if (mkdir (npath, parent_mode))
          {
              builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
              umask (original_umask);
              free (npath);
              return 1;
          }
      }
      else if (S_ISDIR (sb.st_mode) == 0)
      {
          builtin_error ("`%s': file exists but is not a directory", npath);
          umask (original_umask);
          free (npath);
          return 1;
      }

      *p++ = '/';   /* restore slash */
      while (*p == '/')
          p++;
  }

  /* Create the final directory component. */
  if (stat (npath, &sb) && mkdir (npath, nmode))
  {
      builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
      umask (original_umask);
      free (npath);
      return 1;
  }

  umask (original_umask);
  free (npath);
  return 0;
}

You can probably get away with a less general implementation.

Geodynamics answered 25/2, 2010 at 18:3 Comment(0)
T
6

Apparently not, my two suggestions are:

char dirpath[80] = "/path/to/some/directory";
sprintf(mkcmd, "mkdir -p %s", dirpath);
system(mkcmd);

Or if you don't want to use system() try looking at the coreutils mkdir source code and see how they implemented the -p option.

Thrill answered 25/2, 2010 at 17:52 Comment(9)
I pretty much can't use the system command because I'm using a severely impaired (even for busybox) version of busybox. I can't assume that any command has all the standard arguments, or is even installed for that matter, because I'm working on an embedded system. I went with Carl Norum's answer because it works the best in my specific scenario, but your answer was good as well.Unplumbed
OMG - it's 2014, file paths have usually spaces now. Please don't code like thisNerine
@Nerine Perhaps you didn't realize that this answer was made more than 4 years ago, so OMG - it's 2010 would be more apt. Perhaps it's just me, but a lack of quotes around %s hardly seems appropriate to bring a deity into the mix. If you'd like to suggest the edit, please feel free to do so.Thrill
@Nerine There's also the bad choice to use system() for this in the first place, the security flaw from not using an absolute path to mkdir, and the incorrectly coded 'char dirpath[]' line; every line has something wrong with it so had to downvote it; sorry SiegeX, but this was a really bad answer, but as you said, it was in 2010 ;)Simonton
@Simonton I can see a security flaw if I was passing a user-provided path to system(), but I am not, the path is hard coded so other than portable limitations, I don't see the issue. Also, what was "incorrectly coded" in the char dirpath[] line, the extra 56 bytes allocated on the stack?Thrill
/bin/sh will use the PATH environment variable to locate mkdir. If an executable called mkdir is in a path location before /bin, that will be executed instead.Simonton
Oops buffer overflow. And you better hope no-one creates a path ending in ;rm -rf ~Strychnic
Wow that's a lot of completely non-constructive comments. The issues raised can be avoided with e.g. !fork() && execlp("mkdir", "mkdir", "-p", dirpath, NULL) instead. Same idea. With system, though -- buffer safety: asprintf or snprintf_s, space support: you'll have to escape quotes then use \"%s\", sanitization: non-trivial which is kind of a dealbreaker.Bradley
Wow that's a lot of completely non-constructive comments. -- @JasonC the comments are constructive if you can figure out from the comment what is wrong. Unconstructive would be "that sucks" or "every line has a bug" or the classic StackOverflow silent downvote or something. But it suffices to point out that fixed buffers don't work; you don't have to supply an alternate version to be constructive.Chaisson
P
2

I'm not allowed to comment on the first (and accepted) answer (not enough rep), so I'll post my comments as code in a new answer. The code below is based on the first answer, but fixes a number of problems:

  • If called with a zero-length path, this does not read or write the character before the beginning of array opath[] (yes, "why would you call it that way?", but on the other hand "why would you not fix the vulnerability?")
  • the size of opath is now PATH_MAX (which isn't perfect, but is better than a constant)
  • if the path is as long as or longer than sizeof(opath) then it is properly terminated when copied (which strncpy() doesn't do)
  • you can specify the mode of the written directory, just as you can with the standard mkdir() (although if you specify non-user-writeable or non-user-executable then the recursion won't work)
  • main() returns the (required?) int
  • removed a few unnecessary #includes
  • I like the function name better ;)
// Based on http://nion.modprobe.de/blog/archives/357-Recursive-directory-creation.html
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>

static void mkdirRecursive(const char *path, mode_t mode) {
    char opath[PATH_MAX];
    char *p;
    size_t len;

    strncpy(opath, path, sizeof(opath));
    opath[sizeof(opath) - 1] = '\0';
    len = strlen(opath);
    if (len == 0)
        return;
    else if (opath[len - 1] == '/')
        opath[len - 1] = '\0';
    for(p = opath; *p; p++)
        if (*p == '/') {
            *p = '\0';
            if (access(opath, F_OK))
                mkdir(opath, mode);
            *p = '/';
        }
    if (access(opath, F_OK))         /* if path is not terminated with / */
        mkdir(opath, mode);
}


int main (void) {
    mkdirRecursive("/Users/griscom/one/two/three", S_IRWXU);
    return 0;
}
Porty answered 16/1, 2015 at 0:15 Comment(1)
I would change this to return int. so its just a 1 word change to refactor.Vasquez
T
1

My recursive way of doing this:

#include <libgen.h> /* Only POSIX version of dirname() */
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

static void recursive_mkdir(const char *path, mode_t mode)
{
    char *spath = NULL;
    const char *next_dir = NULL;

    /* dirname() modifies input! */
    spath = strdup(path);
    if (spath == NULL)
    {
        /* Report error, no memory left for string duplicate. */
        goto done;
    }

    /* Get next path component: */
    next_dir = dirname(spath);

    if (access(path, F_OK) == 0)
    {
        /* The directory in question already exists! */
        goto done;
    }

    if (strcmp(next_dir, ".") == 0 || strcmp(next_dir, "/") == 0)
    {
        /* We reached the end of recursion! */
        goto done;
    }

    recursive_mkdir(next_dir, mode);
    if (mkdir(path, mode) != 0)
    {
       /* Report error on creating directory */
    }

done:
    free(spath);
    return;
}

EDIT: fixed my old code snippet, bug-report by Namchester

Tabret answered 9/7, 2013 at 15:14 Comment(3)
@Jeff, Yup. Any problem with that? In this case looks easier to understand than plenty of ifs or something like that. It's readable and works well.Tabret
Doesn't create the last directory. If recursive_mkdir("/home/test/hello",mode), doesn't create hello directory.Vocal
@Namchester, apparently it does not work that well with absolute paths. Fixing.Tabret
D
1

Simply works without recursion

int mkdir_p(const char *path, int mode)
{
    char *buf = strdup(path);
    char *p = buf;
    int ret = 0;

    if (buf == NULL) {
        return -1;
    }

    mode |= 0700;

    do {
        p = strchr(p + 1, '/');
        if (p) {
            *p = '\0';
        }
        if (mkdir(buf, mode) != 0) {
            if (errno != EEXIST) {
                ret = errno;
                break;
            }
        }
        if (p) {
            *p = '/';
        }
    } while (p);

    free(buf);

    return (ret);
}

int main(int argc, const char *argv[])
{
    mkdir_p("/home/swei/data_xx/algo/trade/session/1", 0666);
}

Desta answered 8/3, 2023 at 11:13 Comment(0)
N
1

I'm late to the game, but I still wanted to share this very simple function. I don't get why people make things so complicated.

#include <errno.h>
#include <string.h>
#include <sys/stat.h>

int mkdir_p(char *path, mode_t mode)
{
    char *p = path;
    while ((p = strchr(p + 1, '/'))) {
        *p = '\0';
        int ret = mkdir(path, mode);
        *p = '/';
        if (ret == -1 && errno != EEXIST)
            return -1;
    }
    return 0;
}

Note that mkdir_p("this/is/a/path", 0755) creates the directory "this/is/a/", as if it were a file path.

Nielson answered 6/4 at 12:31 Comment(0)
A
0

The two other answers given are for mkdir(1) and not mkdir(2) like you ask for, but you can look at the source code for that program and see how it implements the -p options which calls mkdir(2) repeatedly as needed.

Algology answered 25/2, 2010 at 17:51 Comment(2)
The actual question was "Is there any way (or other function) to create all the directories in the path without resorting to manually parsing my directory string and individually creating each directory " so mkdir(1) is another way!Loiretcher
The make_dir_parents() function is probably the most interesting bit, but it's not in that file. It is in mkdir-p.c in the gnulib repository.Zeta
S
0

My solution:

int mkrdir(const char *path, int index, int permission)
{
    char bf[NAME_MAX];
    if(*path == '/')
        index++;
    char *p = strchr(path + index, '/');
    int len;
    if(p) {
        len = MIN(p-path, sizeof(bf)-1);
        strncpy(bf, path, len);
        bf[len]=0;
    } else {
        len = MIN(strlen(path)+1, sizeof(bf)-1);
        strncpy(bf, path, len);
        bf[len]=0;
    }

    if(access(bf, 0)!=0) {
        mkdir(bf, permission);
        if(access(bf, 0)!=0) {
            return -1;
        }
    }
    if(p) {
        return mkrdir(path, p-path+1, permission);
    }
    return 0;
}
Scheldt answered 15/1, 2014 at 16:39 Comment(0)
B
0

Here's my shot at a more general solution:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

typedef int (*dirhandler_t)( const char*, void* );
/// calls itfunc for each directory in path (except for . and ..)
int iterate_path( const char* path, dirhandler_t itfunc, void* udata )
{
    int rv = 0;
    char tmp[ 256 ];
    char *p = tmp;
    char *lp = tmp;
    size_t len;
    size_t sublen;
    int ignore_entry;

    strncpy( tmp, path, 255 );

    tmp[ 255 ] = '\0';
    len = strlen( tmp );

    if( 0 == len ||
        (1 == len && '/' == tmp[ 0 ]) )
        return 0;

    if( tmp[ len - 1 ] == '/' )
        tmp[ len - 1 ] = 0;

    while( (p = strchr( p, '/' )) != NULL )
    {
        ignore_entry = 0;
        *p = '\0';
        lp = strrchr( tmp, '/' );

        if( NULL == lp ) { lp = tmp; }
        else { lp++; }

        sublen = strlen( lp );

        if( 0 == sublen )   /* ignore things like '//' */
            ignore_entry = 1;
        else if( 1 == sublen &&  /* ignore things like '/./' */
                 '.' == lp[ 0 ] )
            ignore_entry = 1;
        else if( 2 == sublen &&    /* also ignore things like '/../' */
                 '.' == lp[ 0 ] &&
                 '.' == lp[ 1 ] )
            ignore_entry = 1;

        if( ! ignore_entry )
        {
            if( (rv = itfunc( tmp, udata )) != 0 )
                return rv;
        }

        *p = '/';
        p++;
        lp = p;
    }

    if( strcmp( lp, "." ) && strcmp( lp, ".." ) )
        return itfunc( tmp, udata );

    return 0;
}

mode_t get_file_mode( const char* path )
{
    struct stat statbuf;
    memset( &statbuf, 0, sizeof( statbuf ) );

    if( NULL == path ) { return 0; }

    if( 0 != stat( path, &statbuf ) )
    {
        fprintf( stderr, "failed to stat '%s': %s\n",
                 path, strerror( errno ) );
        return 0;
    }

    return statbuf.st_mode;
}

static int mymkdir( const char* path, void* udata )
{
    (void)udata;
    int rv = mkdir( path, S_IRWXU );
    int errnum = errno;

    if( 0 != rv )
    {
        if( EEXIST == errno &&
            S_ISDIR( get_file_mode( path ) ) )  /* it's all good, the directory already exists */
            return 0;

        fprintf( stderr, "mkdir( %s ) failed: %s\n",
                 path, strerror( errnum ) );
    }
//     else
//     {
//         fprintf( stderr, "created directory: %s\n", path );
//     }

    return rv;
}

int mkdir_with_leading( const char* path )
{
    return iterate_path( path, mymkdir, NULL );
}

int main( int argc, const char** argv )
{
    size_t i;
    int rv;

    if( argc < 2 )
    {
        fprintf( stderr, "usage: %s <path> [<path>...]\n",
                 argv[ 0 ] );
        exit( 1 );
    }

    for( i = 1; i < argc; i++ )
    {
        rv = mkdir_with_leading( argv[ i ] );
        if( 0 != rv )
            return rv;
    }

    return 0;
}
Bracteole answered 31/3, 2015 at 8:42 Comment(0)
J
0

If you like recursion because it's fun!

#include <string.h>
#include <sys/stat.h> /* mkdir(2) */
#include <limits.h> /* PATH_MAX */    
int mkdirp(const char *dir, const mode_t mode){
        struct stat sb;
        //if dir already exists and is a directory
        if (stat(dir, &sb) == 0){
            if (S_ISDIR(sb.st_mode)) {
                return 0;
            }
            else return -1;
        }
        else {
            char tmp[PATH_MAX];
            size_t len = strnlen(dir, PATH_MAX);
            memcpy(tmp, dir, len);
            //remove trailing slash
            if (tmp[len-1]=='/'){
                tmp[len-1]='\0';
            }
            char *p = strrchr(tmp, '/');
            *p='\0';
            int ret = mkdirp(tmp, mode);
            if (ret == 0){
                return mkdir(dir, mode);
            }
        }
        return 0;
}
Julijulia answered 20/7, 2021 at 18:55 Comment(0)
P
-1

A very simple solution, just pass in input: mkdir dirname

void execute_command_mkdir(char *input)
{
     char rec_dir[500];
     int s;
     if(strcmp(input,"mkdir") == 0)
        printf("mkdir: operand required");
    else
     {
        char *split = strtok(input," \t");
        while(split)
        {
            if(strcmp(split,"create_dir") != 0)
                strcpy(rec_dir,split);
            split = strtok(NULL, " \t");
        }
        char *split2 = strtok(rec_dir,"/");
        char dir[500];
        strcpy(dir, "");
        while(split2)
        {
            strcat(dir,split2);
            strcat(dir,"/");
            printf("%s %s\n",split2,dir);
            s = mkdir(dir,0700);
            split2 = strtok(NULL,"/");
        }
        strcpy(output,"ok");
    }
        if(s < 0)
            printf(output,"Error!! Cannot Create Directory!!");
}
Proliferation answered 18/1, 2016 at 9:55 Comment(0)
C
-1

Quite straight. This can be a good starting point

int makeDir(char *fullpath, mode_t permissions){
int i=0;
char *arrDirs[20];
char aggrpaz[255];
arrDirs[i] = strtok(fullpath,"/");
strcpy(aggrpaz, "/");
while(arrDirs[i]!=NULL)
{
    arrDirs[++i] = strtok(NULL,"/");
    strcat(aggrpaz, arrDirs[i-1]);
    mkdir(aggrpaz,permissions);
    strcat(aggrpaz, "/");
}
i=0;
return 0;
}

You parse this function a full path plus the permissions you want, i.e S_IRUSR, for a full list of modes go here https://techoverflow.net/2013/04/05/how-to-use-mkdir-from-sysstat-h/

The fullpath string will be split by the "/" character and individual dirs will be appended to the aggrpaz string one at a time. Each loop iteration calls the mkdir function, passing it the aggregate path so far plus the permissions. This example can be improved, I am not checking the mkdir function output and this function only works with absolute paths.

Chartist answered 10/4, 2017 at 16:34 Comment(0)
E
-1

here is my solution

void mkpath(char *p) {
    char *path = strdup(p);
    char  *save_path = path;
    char *sep1;
    char *sep2=0;
    do {
        int idx = (sep2-path)<0 ? 0 : sep2-path;
        sep1 = strchr(path + idx , '/');    
        sep2 = strchr(sep1+1, '/');
        if (sep2) {
            path[sep2-path]=0;
        }
        if(mkdir(path, 0777) && errno != EEXIST)
            break;
        if (sep2) {
            path[sep2-path]='/';
        }
    } while (sep2);

    free(save_path);

}

.
.
.
mkpath ("./the/new/path")
Emotionalize answered 31/12, 2020 at 17:8 Comment(1)
There are 14 other answers on this decade-old question. What does your answer provide that none of the others do not?Rutabaga

© 2022 - 2024 — McMap. All rights reserved.