diff --git a/engine/src/C4Config.cpp b/engine/src/C4Config.cpp index fac252cfc..497e5b257 100644 --- a/engine/src/C4Config.cpp +++ b/engine/src/C4Config.cpp @@ -651,8 +651,7 @@ void C4ConfigGeneral::DeterminePaths(BOOL forceWorkingDirectory) AppendBackslash(ScreenshotPath); } // Create user path if it doesn't already exist - if (!DirectoryExists(UserDataPath)) - CreateDirectory(UserDataPath, NULL); + CreatePath(UserDataPath); } static bool GrabOldPlayerFile(const char *fn) @@ -746,7 +745,7 @@ const char *C4Config::AtScreenshotPath(const char *szFilename) if(len = SLen(AtPathFilename)) if(AtPathFilename[len-1] == DirectorySeparator) AtPathFilename[len-1] = '\0'; - if (!DirectoryExists(AtPathFilename) && !CreateDirectory(AtPathFilename, NULL)) + if (!CreatePath(AtPathFilename)) { SCopy(General.ExePath, General.ScreenshotPath, CFG_MaxString-1); SCopy(General.ScreenshotPath,AtPathFilename,_MAX_PATH); @@ -761,9 +760,8 @@ const char *C4Config::AtScreenshotPath(const char *szFilename) BOOL C4ConfigGeneral::CreateSaveFolder(const char *strDirectory, const char *strLanguageTitle) { // Create directory if needed - if (!DirectoryExists(strDirectory)) - if (!CreateDirectory(strDirectory, NULL)) - return FALSE; + if (!CreatePath(strDirectory)) + return FALSE; // Create title component if needed char lang[3]; SCopy(Config.General.Language, lang, 2); StdStrBuf strTitleFile; strTitleFile.Format("%s%c%s", strDirectory, DirectorySeparator, C4CFN_WriteTitle); diff --git a/engine/src/C4Group.cpp b/engine/src/C4Group.cpp index 53b2d4312..416adf5aa 100644 --- a/engine/src/C4Group.cpp +++ b/engine/src/C4Group.cpp @@ -378,7 +378,7 @@ BOOL C4Group_UnpackDirectory(const char *szFilename) char szFoldername[_MAX_PATH+1]; SCopy(szFilename,szFoldername,_MAX_PATH); MakeTempFilename(szFoldername); - if (!CreateDirectory(szFoldername,NULL)) { hGroup.Close(); return FALSE; } + if (!CreatePath(szFoldername)) { hGroup.Close(); return FALSE; } // Extract files to folder if (!hGroup.Extract("*",szFoldername)) { hGroup.Close(); return FALSE; } diff --git a/engine/src/C4Network2Res.cpp b/engine/src/C4Network2Res.cpp index d9cb90e4a..f28e801af 100644 --- a/engine/src/C4Network2Res.cpp +++ b/engine/src/C4Network2Res.cpp @@ -1706,7 +1706,7 @@ bool C4Network2ResList::CreateNetworkFolder() // does not exist? if(access(szNetworkPath, 00)) { - if(!CreateDirectory(szNetworkPath, 0)) + if(!CreatePath(szNetworkPath)) { LogFatal("Network: could not create network path!"); return false; } return true; } diff --git a/engine/src/C4Scenario.cpp b/engine/src/C4Scenario.cpp index 1341c649b..621769f12 100644 --- a/engine/src/C4Scenario.cpp +++ b/engine/src/C4Scenario.cpp @@ -727,7 +727,7 @@ bool C4ScenarioSection::EnsureTempStore(bool fExtractLandscape, bool fExtractObj // main section: extract section files from main scenario group (create group as open dir) if (!szFilename) { - if (!CreateDirectory(szTmp, NULL)) return false; + if (!CreatePath(szTmp)) return false; C4Group hGroup; if (!hGroup.Open(szTmp, TRUE)) { EraseItem(szTmp); return false; } // extract all desired section files diff --git a/engine/src/C4StartupMainDlg.cpp b/engine/src/C4StartupMainDlg.cpp index 5a74bfd44..38e10d457 100644 --- a/engine/src/C4StartupMainDlg.cpp +++ b/engine/src/C4StartupMainDlg.cpp @@ -434,7 +434,7 @@ void C4StartupMainDlg::HandleIncomingKeyfile(const char *strIncomingKey) if (GetScreen()->ShowMessageModal(strMessage.getData(), LoadResStr("IDS_DLG_REGISTRATION"), C4GUI::MessageDialog::btnYesNo, C4GUI::Ico_Confirm)) { // Create key path if it doesn't already exist - if (!DirectoryExists(Config.GetKeyPath())) CreateDirectory(Config.GetKeyPath(), NULL); + CreatePath(Config.GetKeyPath()); // Move key into key path StdStrBuf strTarget; strTarget.Format("%s%s", Config.GetKeyPath(), GetFilename(strKeyFilename.getData())); if (!MoveItem(strKeyFilename.getData(), strTarget.getData())) @@ -451,7 +451,7 @@ void C4StartupMainDlg::HandleIncomingKeyfile(const char *strIncomingKey) // Say thank you GetScreen()->ShowMessageModal(LoadResStr("IDS_CTL_REGISTERED"), LoadResStr("IDS_DLG_REGISTRATION"), C4GUI::MessageDialog::btnOK, C4GUI::Ico_Notify); // Create key path if it doesn't already exist - if (!DirectoryExists(Config.GetKeyPath())) CreateDirectory(Config.GetKeyPath(), NULL); + CreatePath(Config.GetKeyPath()); // Now try to copy it into the key path (preferred) StdStrBuf strTarget; strTarget.Format("%s%s", Config.GetKeyPath(), GetFilename(strKeyFilename.getData())); if (!CopyItem(strKeyFilename.getData(), strTarget.getData())) diff --git a/engine/src/C4Update.cpp b/engine/src/C4Update.cpp index 9a8d03458..a70c315ae 100644 --- a/engine/src/C4Update.cpp +++ b/engine/src/C4Update.cpp @@ -328,7 +328,7 @@ BOOL C4UpdatePackage::Execute(C4Group *pGroup) // GrpUpdate -> file must exist if(GrpUpdate) return FALSE; // create dir - CreateDirectory(strTarget, NULL); + CreatePath(strTarget); } *p = '\\'; lp = p + 1; } diff --git a/standard/inc/StdFile.h b/standard/inc/StdFile.h index 8fa8520c6..77db75aec 100644 --- a/standard/inc/StdFile.h +++ b/standard/inc/StdFile.h @@ -27,6 +27,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -38,19 +39,27 @@ #define _MAX_PATH PATH_MAX #define _MAX_FNAME NAME_MAX -bool CreateDirectory(const char * pathname, void* = 0); bool CopyFile(const char *szSource, const char *szTarget, bool FailIfExists); #endif #ifdef _WIN32 +static const char *DirectorySeparators = "/\\"; #define DirectorySeparator '\\' #define AltDirectorySeparator '/' #else +static const char *DirectorySeparators = "/"; #define DirectorySeparator '/' #define AltDirectorySeparator '\\' +#define DIRECTORYSEPARATORS "/" #endif #define Wildcard '*' +/** Create a directory and all of its parents. + * \p[in] path Directory to create + * \returns true on success, false otherwise. + */ +bool CreatePath(const std::string &path); + const char *GetWorkingDirectory(); bool SetWorkingDirectory(const char *szPath); char *GetFilename(char *path); diff --git a/standard/src/StdFile.cpp b/standard/src/StdFile.cpp index b03b301bd..134cdee97 100644 --- a/standard/src/StdFile.cpp +++ b/standard/src/StdFile.cpp @@ -43,6 +43,7 @@ #include #include #include +#include /* Path & Filename */ @@ -560,26 +561,53 @@ bool SetWorkingDirectory(const char *path) #endif } -#ifndef _WIN32 -// CreateDirectory: true on success -bool CreateDirectory(const char * pathname, void*) +bool CreatePath(const std::string &path) +{ + assert(!path.empty()); +#ifdef _WIN32 + if (!CreateDirectory(path.c_str(), NULL)) { - int r = mkdir(pathname,S_IREAD | S_IWRITE | S_IEXEC); - if (r && errno == ENOENT) + DWORD err = GetLastError(); + switch(err) { - StdCopyStrBuf parent(pathname); - char * slash = strrchr(parent.getMData(), '/'); - if (slash) + case ERROR_PATH_NOT_FOUND: + break; + case ERROR_ALREADY_EXISTS: + return true; + default: + // Something major has happened: Log { - *slash = 0; - CreateDirectory(parent.getData()); - return !mkdir(pathname,S_IREAD | S_IWRITE | S_IEXEC); + LPSTR str; + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, err, 0, (LPSTR)&str, 0, NULL)) + { + LogF("CreateDirectory failed: %s", str); + LocalFree(str); + } + return false; } } - // mkdir: 0 on success - return !r; + } +#else + if (!mkdir(path.c_str(), S_IREAD | S_IWRITE | S_IEXEC)) + return true; + switch(errno) + { + case ENOENT: + break; + case EEXIST: + // FIXME: Check whether the path is blocked by a non-directory + return true; + default: + return false; } #endif + // Recursively create parent path + std::string::size_type slash = path.find_last_of(DirectorySeparators); + if (slash == 0 || slash == std::string::npos) + return false; + return CreatePath(path.substr(0, slash)) && CreatePath(path); +} bool DirectoryExists(const char *szFilename) {