diff --git a/src/fileio.c b/src/fileio.c index 4333f4e..b56b672 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -444,7 +444,6 @@ bool save_game (int num) char *codeset; int saved_errno; char *prev_locale; - struct stat statbuf; int i, j, x, y; unsigned int crypt_key; unsigned int *crypt_key_p; @@ -466,25 +465,19 @@ bool save_game (int num) // Create the data directory, if needed data_dir = data_directory(); if (data_dir != NULL) { - if (mkdir(data_dir, S_IRWXU | S_IRWXG | S_IRWXO) != 0) { + if (xmkdir(data_dir, S_IRWXU | S_IRWXG | S_IRWXO) != 0) { + // Data directory could not be created saved_errno = errno; - if (saved_errno == EEXIST && stat(data_dir, &statbuf) == 0 - && S_ISDIR(statbuf.st_mode)) { - ; // Do nothing: directory already exists - } else { - // Data directory could not be created - txdlgbox(MAX_DLG_LINES, 60, 7, WCENTER, attr_error_window, - attr_error_title, attr_error_highlight, - attr_error_normal, 0, attr_error_waitforkey, - _(" Game Not Saved "), - _("Game %d could not be saved to disk.\n\n" - "^{Directory %s: %s^}"), num, data_dir, - strerror(saved_errno)); + txdlgbox(MAX_DLG_LINES, 60, 7, WCENTER, attr_error_window, + attr_error_title, attr_error_highlight, attr_error_normal, + 0, attr_error_waitforkey, _(" Game Not Saved "), + _("Game %d could not be saved to disk.\n\n" + "^{Directory %s: %s^}"), num, data_dir, + strerror(saved_errno)); - free(buf); - free(encbuf); - return false; - } + free(buf); + free(encbuf); + return false; } } diff --git a/src/utils.c b/src/utils.c index 4fe5af0..fa9c8de 100644 --- a/src/utils.c +++ b/src/utils.c @@ -967,7 +967,7 @@ size_t b64encode (const void *restrict in, size_t inlen, if (padding > 0) { assert(count + 2 < outlen); - for (; padding < 3; padding++) { + for ( ; padding < 3; padding++) { *u_out++ = SCRAMBLE_PAD_CHAR; count++; } @@ -1067,6 +1067,67 @@ ssize_t b64decode (const void *restrict in, size_t inlen, // These functions are documented in the file "utils.h" +/***********************************************************************/ +// xmkdir: Check and create directory with its parents + +int xmkdir (const char *pathname, mode_t mode) +{ + const char *dirsep = DIRSEP; + struct stat statbuf; + char *pathcopy; + char *pcomp, *pend; + int ret; + + + assert(strlen(dirsep) == 1); + + if (pathname == NULL || *pathname == '\0') { + errno = ENOENT; // As documented by POSIX + return -1; + } + + // Check that pathname already exists and is a directory + if (stat(pathname, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + return 0; + } else { + errno = ENOTDIR; + return -1; + } + } + + // Try creating the directory + ret = mkdir(pathname, mode); + if (ret == 0 || (errno != ENOENT && errno != ENOTDIR)) { + return ret; + } + + // Try creating directory components, except the last, one by one + pathcopy = xstrdup(pathname); + pcomp = pend = pathcopy; + for ( ; *pend != '\0'; pend++) { + if (*pend == dirsep[0] && pcomp != pend) { + *pend = '\0'; + + ret = mkdir(pathcopy, mode); + if (ret != 0 && errno != EEXIST) { + free(pathcopy); + return ret; + } + + *pend = dirsep[0]; + pcomp = pend + 1; + } + } + + // Try creating the last directory component + ret = mkdir(pathcopy, mode); + + free(pathcopy); + return ret; +} + + /***********************************************************************/ // xmalloc: Allocate a new block of memory, with checking diff --git a/src/utils.h b/src/utils.h index a513f7f..54089cd 100644 --- a/src/utils.h +++ b/src/utils.h @@ -324,6 +324,20 @@ extern char *unscramble (char *restrict dest, const char *restrict src, * Miscellaneous function prototypes * ************************************************************************/ +/* + Function: xmkdir - Check and create directory with its parents + Parameters: pathname - Directory to create + mode - Mode for any new directories + Returns: int - 0 on success, -1 if an error occurred + + This function checks whether pathname exists and is a directory: if so, + 0 is returned. Otherwise, it creates the directory and any parents + that do not already exist. If an error occurs, -1 is returned and + errno is set appropriately. +*/ +extern int xmkdir (const char *pathname, mode_t mode); + + /* Function: xmalloc - Allocate a new block of memory, with checking Parameters: size - Size of new block of memory in bytes