Avec C ++ 17 ou version ultérieure, il y a l'en-tête standard <filesystem>avec une fonction
std::filesystem::create_directories
qui devrait être utilisée dans les programmes C ++ modernes. Les fonctions standard C ++ n'ont cependant pas l'argument d'autorisations explicites (mode) spécifique à POSIX.
Cependant, voici une fonction C qui peut être compilée avec des compilateurs C ++.
/*
@(#)File: mkpath.c
@(#)Purpose: Create all directories in path
@(#)Author: J Leffler
@(#)Copyright: (C) JLSS 1990-2020
@(#)Derivation: mkpath.c 1.16 2020/06/19 15:08:10
*/
/*TABSTOP=4*/
#include "posixver.h"
#include "mkpath.h"
#include "emalloc.h"
#include <errno.h>
#include <string.h>
/* "sysstat.h" == <sys/stat.h> with fixup for (old) Windows - inc mode_t */
#include "sysstat.h"
typedef struct stat Stat;
static int do_mkdir(const char *path, mode_t mode)
{
Stat st;
int status = 0;
if (stat(path, &st) != 0)
{
/* Directory does not exist. EEXIST for race condition */
if (mkdir(path, mode) != 0 && errno != EEXIST)
status = -1;
}
else if (!S_ISDIR(st.st_mode))
{
errno = ENOTDIR;
status = -1;
}
return(status);
}
/**
** mkpath - ensure all directories in path exist
** Algorithm takes the pessimistic view and works top-down to ensure
** each directory in path exists, rather than optimistically creating
** the last element and working backwards.
*/
int mkpath(const char *path, mode_t mode)
{
char *pp;
char *sp;
int status;
char *copypath = STRDUP(path);
status = 0;
pp = copypath;
while (status == 0 && (sp = strchr(pp, '/')) != 0)
{
if (sp != pp)
{
/* Neither root nor double slash in path */
*sp = '\0';
status = do_mkdir(copypath, mode);
*sp = '/';
}
pp = sp + 1;
}
if (status == 0)
status = do_mkdir(path, mode);
FREE(copypath);
return (status);
}
#ifdef TEST
#include <stdio.h>
#include <unistd.h>
/*
** Stress test with parallel running of mkpath() function.
** Before the EEXIST test, code would fail.
** With the EEXIST test, code does not fail.
**
** Test shell script
** PREFIX=mkpath.$$
** NAME=./$PREFIX/sa/32/ad/13/23/13/12/13/sd/ds/ww/qq/ss/dd/zz/xx/dd/rr/ff/ff/ss/ss/ss/ss/ss/ss/ss/ss
** : ${MKPATH:=mkpath}
** ./$MKPATH $NAME &
** [...repeat a dozen times or so...]
** ./$MKPATH $NAME &
** wait
** rm -fr ./$PREFIX/
*/
int main(int argc, char **argv)
{
int i;
for (i = 1; i < argc; i++)
{
for (int j = 0; j < 20; j++)
{
if (fork() == 0)
{
int rc = mkpath(argv[i], 0777);
if (rc != 0)
fprintf(stderr, "%d: failed to create (%d: %s): %s\n",
(int)getpid(), errno, strerror(errno), argv[i]);
exit(rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
}
int status;
int fail = 0;
while (wait(&status) != -1)
{
if (WEXITSTATUS(status) != 0)
fail = 1;
}
if (fail == 0)
printf("created: %s\n", argv[i]);
}
return(0);
}
#endif /* TEST */
Les macros STRDUP()et FREE()sont des versions de vérification des erreurs de
strdup()et free(), déclarées dans emalloc.h(et implémentées dans
emalloc.cet estrdup.c). L'en- "sysstat.h"tête traite des versions cassées de <sys/stat.h>
et peut être remplacé par <sys/stat.h>sur les systèmes Unix modernes (mais il y avait de nombreux problèmes en 1990). Et "mkpath.h"déclare mkpath().
Le changement entre v1.12 (version originale de la réponse) et v1.13 (version modifiée de la réponse) était le test pour EEXISTdans
do_mkdir(). Cela a été signalé comme nécessaire par
Switch - merci, Switch. Le code de test a été mis à jour et reproduit le problème sur un MacBook Pro (Intel Core i7 2,3 GHz, exécutant Mac OS X 10.7.4), et suggère que le problème est résolu dans la révision (mais les tests ne peuvent montrer que la présence de bogues , jamais leur absence). Le code affiché est maintenant v1.16; des modifications cosmétiques ou administratives ont été apportées depuis la v1.13 (comme l'utilisation à la mkpath.hplace de jlss.het l'inclusion <unistd.h>inconditionnelle dans le code de test uniquement). Il est raisonnable de dire que cela "sysstat.h"devrait être remplacé par à
<sys/stat.h>moins que vous n'ayez un système inhabituellement récalcitrant.
(Vous êtes autorisé par la présente à utiliser ce code à toute fin avec attribution.)
Ce code est disponible dans mon référentiel SOQ
(Stack Overflow Questions) sur GitHub sous forme de fichiers mkpath.cet
mkpath.h(etc.) dans le
sous-répertoire src / so-0067-5039 .