/** * @brief locate spring data directory * * On *nix platforms, attempts to locate * and change to the spring data directory * * The data directory to chdir to is determined by the following, in this * order (first items override lower items): * * - 'SpringData=/path/to/data' declaration in '~/.springrc'. (colon separated list) * - 'SPRING_DATADIR' environment variable. (colon separated list, like PATH) * - In the same order any line in '/etc/spring/datadir', if that file exists. * - 'datadir=/path/to/data' option passed to 'scons configure'. * - 'prefix=/install/path' option passed to scons configure. The datadir is * assumed to be at '$prefix/games/taspring' in this case. * - the default datadir in the default prefix, ie. '/usr/local/games/taspring' * * All of the above methods support environment variable substitution, eg. * '$HOME/myspringdatadir' will be converted by spring to something like * '/home/username/myspringdatadir'. * * If it fails to chdir to the above specified directory spring will asume the * current working directory is the data directory. */ void UnixFileSystemHandler::LocateDataDirs() { // Construct the list of datadirs from various sources. datadirs.clear(); std::string cfg = configHandler.GetString("SpringData",""); if (!cfg.empty()) AddDirs(SubstEnvVars(cfg)); char* env = getenv("SPRING_DATADIR"); if (env && *env) AddDirs(SubstEnvVars(env)); FILE* f = ::fopen("/etc/spring/datadir", "r"); if (f) { char buf[1024]; while (fgets(buf, sizeof(buf), f)) { char* newl = strchr(buf, '\n'); if (newl) *newl = 0; datadirs.push_back(SubstEnvVars(buf)); } fclose(f); } datadirs.push_back(SubstEnvVars(SPRING_DATADIR)); // Figure out permissions of all datadirs DeterminePermissions(); if (!writedir) { // add current working directory to search path & try again fprintf(stderr, "WARNING: adding current working directory to search path\n"); char buf[4096]; getcwd(buf, sizeof(buf)); buf[sizeof(buf) - 1] = 0; datadirs.push_back(DataDir(buf)); DeterminePermissions(datadirs.size() - 1); } if (!writedir) { // bail out throw content_error("not a single read-write data directory found!"); } // for now, chdir to the datadirectory as a safety measure: // all AIs still just assume it's ok to put their stuff in the current directory after all chdir(GetWriteDir().c_str()); }
/** * @brief locate spring data directory * * On *nix platforms, attempts to locate * and change to the spring data directory * * The data directory to chdir to is determined by the following, in this * order (first items override lower items): * * - 'SPRING_DATADIR' environment variable. (colon separated list, like PATH) * - 'SpringData=/path/to/data' declaration in '~/.springrc'. (colon separated list) * This defaults to "$HOME/.spring" * - In the same order any line in '/etc/spring/datadir', if that file exists. * - 'datadir=/path/to/data' option passed to 'scons configure'. * - 'prefix=/install/path' option passed to scons configure. The datadir is * assumed to be at '$prefix/games/spring' in this case. * - the default datadirs in the default prefix, ie. '/usr/local/games/spring' * (This is set by the build system, ie. SPRING_DATADIR and SPRING_DATADIR_2 * preprocessor definitions.) * * All of the above methods support environment variable substitution, eg. * '$HOME/myspringdatadir' will be converted by spring to something like * '/home/username/myspringdatadir'. * * If it fails to chdir to the above specified directory spring will asume the * current working directory is the data directory. */ void DataDirLocater::LocateDataDirs() { // Construct the list of datadirs from various sources. datadirs.clear(); char* env = getenv("SPRING_DATADIR"); if (env && *env) AddDirs(SubstEnvVars(env)); std::string cfg = configHandler.GetString("SpringData", "$HOME/.spring"); if (!cfg.empty()) { AddDirs(SubstEnvVars(cfg)); } FILE* f = ::fopen("/etc/spring/datadir", "r"); if (f) { char buf[1024]; while (fgets(buf, sizeof(buf), f)) { char* newl = strchr(buf, '\n'); if (newl) *newl = 0; datadirs.push_back(SubstEnvVars(buf)); } fclose(f); } #ifdef SPRING_DATADIR datadirs.push_back(SubstEnvVars(SPRING_DATADIR)); #endif #ifdef SPRING_DATADIR_2 datadirs.push_back(SubstEnvVars(SPRING_DATADIR_2)); #endif // Figure out permissions of all datadirs DeterminePermissions(); if (!writedir) { // bail out throw content_error("Not a single writable data directory found!\n\n" "Configure a writable data directory using either:\n" "- the SPRING_DATADIR environment variable,\n" "- a SpringData=/path/to/data declaration in ~/.springrc or\n" "- the configuration file /etc/spring/datadir"); } // for now, chdir to the datadirectory as a safety measure: // all AIs still just assume it's ok to put their stuff in the current directory after all // Not only safety anymore, it's just easier if other code can safely assume that // writedir == current working directory chdir(GetWriteDir()->path.c_str()); // Logging MAY NOT start before the chdir, otherwise the logfile ends up // in the wrong directory. for (std::vector<DataDir>::const_iterator d = datadirs.begin(); d != datadirs.end(); ++d) { if (d->writable) logOutput.Print("Using read-write data directory: %s", d->path.c_str()); else logOutput.Print("Using read-only data directory: %s", d->path.c_str()); } }
void DataDirLocater::FilterUsableDataDirs() { std::vector<DataDir> newDatadirs; std::string previous; // used to filter out consecutive duplicates // (I did not bother filtering out non-consecutive duplicates because then // there is the question which of the multiple instances to purge.) for (std::vector<DataDir>::iterator d = dataDirs.begin(); d != dataDirs.end(); ++d) { if (d->path != previous) { if (DeterminePermissions(&*d)) { newDatadirs.push_back(*d); previous = d->path; if (d->writable) { LOG("Using read-write data directory: %s", d->path.c_str()); } else { LOG("Using read-only data directory: %s", d->path.c_str()); } } else { LOG_L(L_DEBUG, "Potentional data directory: %s", d->path.c_str()); } } } dataDirs = newDatadirs; }
void DataDirLocater::DeterminePermissions() { std::vector<DataDir> newDatadirs; std::string previous; // used to filter out consecutive duplicates // (I did not bother filtering out non-consecutive duplicates because then // there is the question which of the multiple instances to purge.) writeDir = NULL; for (std::vector<DataDir>::iterator d = dataDirs.begin(); d != dataDirs.end(); ++d) { if ((d->path != previous) && DeterminePermissions(&*d)) { newDatadirs.push_back(*d); previous = d->path; } } dataDirs = newDatadirs; }
void DataDirLocater::LocateDataDirs() { // Prepare the data-dirs defined in different places // environment variable std::string dd_env = ""; { char* env = getenv("SPRING_DATADIR"); if (env && *env) { dd_env = SubstEnvVars(env); } } // If this is true, ie the var is present in env, we will only add the dir // where both binary and unitysnc lib reside in Portable mode, // or the parent dir, if it is a versioned data-dir. const bool isolationMode = (getenv("SPRING_ISOLATED") != NULL); #if defined(UNITSYNC) const std::string dd_curWorkDir = Platform::GetModulePath(); #else // defined(UNITSYNC) const std::string dd_curWorkDir = Platform::GetProcessExecutablePath(); #endif // defined(UNITSYNC) #if defined(WIN32) // fetch my documents path TCHAR pathMyDocs[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pathMyDocs); // fetch app-data path TCHAR pathAppData[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, pathAppData); std::string dd_myDocs = pathMyDocs; // e.g. F:\Dokumente und Einstellungen\Karl-Robert\Eigene Dateien\Spring dd_myDocs += "\\Spring"; std::string dd_myDocsMyGames = pathMyDocs; // My Documents\My Games seems to be the MS standard even if no official guidelines exist // most if not all new Games For Windows(TM) games use this dir dd_myDocsMyGames += "\\My Games\\Spring"; std::string dd_appData = pathAppData; // e.g. F:\Dokumente und Einstellungen\All Users\Anwendungsdaten\Spring dd_appData += "\\Spring"; #else // *nix (-OSX) // settings in /etc std::string dd_etc = ""; { FILE* fileH = ::fopen("/etc/spring/datadir", "r"); if (fileH) { const char whiteSpaces[3] = {'\t', ' ', '\0'}; char lineBuf[1024]; while (fgets(lineBuf, sizeof(lineBuf), fileH)) { char* newLineCharPos = strchr(lineBuf, '\n'); if (newLineCharPos) { // remove the new line char *newLineCharPos = '\0'; } // ignore lines consisting of only whitespaces if ((strlen(lineBuf) > 0) && strspn(lineBuf, whiteSpaces) != strlen(lineBuf)) { // append, separated by sPD (depending on OS): ';' or ':' dd_etc = dd_etc + (dd_etc.empty() ? "" : sPD) + SubstEnvVars(lineBuf); } } fclose(fileH); } } #endif // defined(WIN32), defined(MACOSX_BUNDLE), else // Construct the list of dataDirs from various sources. dataDirs.clear(); // The first dir added will be the writeable data dir. if (isolationMode) { AddCwdOrParentDir(dd_curWorkDir, true); // "./" or "../" } else { // same on all platforms AddDirs(dd_env); // ENV{SPRING_DATADIR} // user defined in spring config handler // (Linux: ~/.springrc, Windows: .\springsettings.cfg) AddDirs(SubstEnvVars(configHandler->GetString("SpringData"))); #ifdef WIN32 // All MS Windows variants AddCwdOrParentDir(dd_curWorkDir); // "./" or "../" AddDirs(dd_myDocsMyGames); // "C:/.../My Documents/My Games/Spring/" AddDirs(dd_myDocs); // "C:/.../My Documents/Spring/" AddDirs(dd_appData); // "C:/.../All Users/Applications/Spring/" #elif defined(MACOSX_BUNDLE) // Mac OS X Application Bundle (*.app) - single file install // directory structure (Apple standard): // Spring.app/Contents/MacOS/springlobby // Spring.app/Contents/Resources/bin/spring // Spring.app/Contents/Resources/lib/unitsync.dylib // Spring.app/Contents/Resources/share/games/spring/base/ // This corresponds to Spring.app/Contents/Resources/ const std::string bundleResourceDir = FileSystem::GetParent(dd_curWorkDir); // This has to correspond with the value in the build-script const std::string dd_curWorkDirData = bundleResourceDir + "/share/games/spring"; // we need this as default writeable dir, because the Bundle.pp dir // might not be writeable by the user starting the game AddDirs(SubstEnvVars("$HOME/.spring")); // "~/.spring/" AddDirs(dd_curWorkDirData); // "Spring.app/Contents/Resources/share/games/spring" AddDirs(dd_etc); // from /etc/spring/datadir #else // Linux, FreeBSD, Solaris, Apple non-bundle AddCwdOrParentDir(dd_curWorkDir); // "./" or "../" AddDirs(SubstEnvVars("$HOME/.spring")); // "~/.spring/" AddDirs(dd_etc); // from /etc/spring/datadir #endif #ifdef SPRING_DATADIR AddDirs(SubstEnvVars(SPRING_DATADIR)); // from -DSPRING_DATADIR #endif } // Figure out permissions of all dataDirs DeterminePermissions(); if (!writeDir) { // bail out const std::string errstr = "Not a single writable data directory found!\n\n" "Configure a writable data directory using either:\n" "- the SPRING_DATADIR environment variable,\n" #ifdef WIN32 "- a SpringData=C:/path/to/data declaration in spring's config file ./springsettings.cfg\n" "- by giving you write access to the installation directory"; #else "- a SpringData=/path/to/data declaration in ~/.springrc or\n" "- the configuration file /etc/spring/datadir"; #endif throw content_error(errstr); } // for now, chdir to the data directory as a safety measure: // Not only safety anymore, it's just easier if other code can safely assume that // writeDir == current working directory FileSystem::ChDir(GetWriteDir()->path.c_str()); // Initialize the log. Only after this moment log will be written to file. logOutput.Initialize(); // Logging MAY NOT start before the chdir, otherwise the logfile ends up // in the wrong directory. // Update: now it actually may start before, log has preInitLog. for (std::vector<DataDir>::const_iterator d = dataDirs.begin(); d != dataDirs.end(); ++d) { if (d->writable) { LOG("Using read-write data directory: %s", d->path.c_str()); // tag the cache dir const std::string cacheDir = d->path + "cache"; if (FileSystem::CreateDirectory(cacheDir)) { CacheDir::SetCacheDir(cacheDir, true); } } else { LOG("Using read-only data directory: %s", d->path.c_str()); } } }
/** * @brief Adds the directories in the colon separated string to the datadir handler. */ void DataDirLocater::AddDirs(const std::string& in) { size_t prev_colon = 0, colon; #ifndef _WIN32 while ((colon = in.find(':', prev_colon)) != std::string::npos) { #else while ((colon = in.find(';', prev_colon)) != std::string::npos) { #endif datadirs.push_back(in.substr(prev_colon, colon - prev_colon)); #ifdef DEBUG logOutput.Print("Adding %s to directories" , in.substr(prev_colon, colon - prev_colon).c_str()); #endif prev_colon = colon + 1; } #ifdef DEBUG logOutput.Print("Adding %s to directories" , in.substr(prev_colon).c_str()); #endif datadirs.push_back(in.substr(prev_colon)); } /** * @brief Figure out permissions we have for a single data directory d. * @returns whether we have permissions to read the data directory. */ bool DataDirLocater::DeterminePermissions(DataDir* d) { #ifndef _WIN32 if (d->path.c_str()[0] != '/' || d->path.find("..") != std::string::npos) #else if (d->path.find("..") != std::string::npos) #endif throw content_error("specify data directories using absolute paths please"); // Figure out whether we have read/write permissions // First check read access, if we got that check write access too // (no support for write-only directories) // Note: we check for executable bit otherwise we can't browse the directory // Note: we fail to test whether the path actually is a directory // Note: modifying permissions while or after this function runs has undefined behaviour #ifndef _WIN32 if (access(d->path.c_str(), R_OK | X_OK | F_OK) == 0) { // Note: disallow multiple write directories. // There isn't really a use for it as every thing is written to the first one anyway, // and it may give funny effects on errors, e.g. it probably only gives funny things // like network mounted datadir lost connection and suddenly files end up in some // other random writedir you didn't even remember you had added it. if (!writedir && access(d->path.c_str(), W_OK) == 0) { #else if (_access(d->path.c_str(), 4) == 0) { if (!writedir && _access(d->path.c_str(), 2) == 0) { #endif d->writable = true; writedir = &*d; } return true; } else { if (filesystem.CreateDirectory(d->path)) { // it didn't exist before, now it does and we just created it with rw access, // so we just assume we still have read-write acces... if (!writedir) { d->writable = true; writedir = d; } return true; } } return false; } /** * @brief Figure out permissions we have for the data directories. */ void DataDirLocater::DeterminePermissions() { std::vector<DataDir> newDatadirs; std::string previous; // used to filter out consecutive duplicates // (I didn't bother filtering out non-consecutive duplicates because then // there is the question which of the multiple instances to purge.) writedir = NULL; for (std::vector<DataDir>::iterator d = datadirs.begin(); d != datadirs.end(); ++d) { if (d->path != previous && DeterminePermissions(&*d)) { newDatadirs.push_back(*d); previous = d->path; } } datadirs = newDatadirs; } /** * @brief locate spring data directory * * On *nix platforms, attempts to locate * and change to the spring data directory * * In Unixes, the data directory to chdir to is determined by the following, in this * order (first items override lower items): * * - 'SPRING_DATADIR' environment variable. (colon separated list, like PATH) * - 'SpringData=/path/to/data' declaration in '~/.springrc'. (colon separated list) * - "$HOME/.spring" * - In the same order any line in '/etc/spring/datadir', if that file exists. * - 'datadir=/path/to/data' option passed to 'scons configure'. * - 'prefix=/install/path' option passed to scons configure. The datadir is * assumed to be at '$prefix/games/spring' in this case. * - the default datadirs in the default prefix, ie. '/usr/local/games/spring' * (This is set by the build system, ie. SPRING_DATADIR and SPRING_DATADIR_2 * preprocessor definitions.) * * In Windows, its: * - SPRING_DATADIR env-variable * - user configurable (SpringData in registry) * - location of the binary dir (like it has been until 0.76b1) * - the Users 'Documents'-directory (in subdirectory Spring), unless spring is configured to use another * - all users app-data (in subdirectory Spring) * - compiler flags SPRING_DATADIR and SPRING_DATADIR_2 * * All of the above methods support environment variable substitution, eg. * '$HOME/myspringdatadir' will be converted by spring to something like * '/home/username/myspringdatadir'. * * If it fails to chdir to the above specified directory spring will asume the * current working directory is the data directory. */ void DataDirLocater::LocateDataDirs() { // Construct the list of datadirs from various sources. datadirs.clear(); // environment variable char* env = getenv("SPRING_DATADIR"); if (env && *env) AddDirs(SubstEnvVars(env)); // user defined (in spring config handler (Linux: ~/.spring, Windows: registry)) std::string userDef = configHandler.GetString("SpringData", ""); if (!userDef.empty()) { AddDirs(SubstEnvVars(userDef)); } #ifdef WIN32 TCHAR currentDir[MAX_PATH]; ::GetCurrentDirectory(sizeof(currentDir) - 1, currentDir); std::string curPath = currentDir; AddDirs(std::string(currentDir)); // my documents TCHAR strPath[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, strPath); std::string cfg = strPath; cfg += "\\Spring"; // e.g. F:\Dokumente und Einstellungen\Karl-Robert\Eigene Dateien\Spring AddDirs(cfg); cfg.clear(); // appdata SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, strPath); cfg = strPath; cfg += "\\Spring"; // e.g. F:\Dokumente und Einstellungen\All Users\Anwendungsdaten\Spring AddDirs(cfg); #else // home AddDirs(SubstEnvVars("$HOME/.spring")); // settings in /etc FILE* f = ::fopen("/etc/spring/datadir", "r"); if (f) { char buf[1024]; while (fgets(buf, sizeof(buf), f)) { char* newl = strchr(buf, '\n'); if (newl) *newl = 0; char white[3] = {'\t', ' ', 0}; if (strlen(buf) > 0 && strspn(buf, white) != strlen(buf)) // don't count lines of whitespaces / tabulators AddDirs(SubstEnvVars(buf)); } fclose(f); } #endif // compiler flags #ifdef SPRING_DATADIR datadirs.push_back(SubstEnvVars(SPRING_DATADIR)); #endif // should not be needed because you can seperate directories with a ':' in SPRING_DATADIR(1) #ifdef SPRING_DATADIR_2 datadirs.push_back(SubstEnvVars(SPRING_DATADIR_2)); #endif // Figure out permissions of all datadirs DeterminePermissions(); if (!writedir) { // bail out throw content_error("Not a single writable data directory found!\n\n" "Configure a writable data directory using either:\n" "- the SPRING_DATADIR environment variable,\n" "- a SpringData=/path/to/data declaration in ~/.springrc or\n" "- the configuration file /etc/spring/datadir"); } // for now, chdir to the datadirectory as a safety measure: // all AIs still just assume it's ok to put their stuff in the current directory after all // Not only safety anymore, it's just easier if other code can safely assume that // writedir == current working directory #ifndef _WIN32 chdir(GetWriteDir()->path.c_str()); #else _chdir(GetWriteDir()->path.c_str()); #endif // Logging MAY NOT start before the chdir, otherwise the logfile ends up // in the wrong directory. for (std::vector<DataDir>::const_iterator d = datadirs.begin(); d != datadirs.end(); ++d) { if (d->writable) logOutput.Print("Using read-write data directory: %s", d->path.c_str()); else logOutput.Print("Using read-only data directory: %s", d->path.c_str()); } }
void DataDirLocater::LocateDataDirs() { // Prepare the data-dirs defined in different places // environment variable std::string dd_env = ""; { char* env = getenv("SPRING_DATADIR"); if (env && *env) { dd_env = SubstEnvVars(env); } } #if defined(UNITSYNC) const std::string dd_curWorkDir = Platform::GetModulePath(); #else // defined(UNITSYNC) const std::string dd_curWorkDir = Platform::GetProcessExecutablePath(); #endif // defined(UNITSYNC) // This is useful in case of multiple engine/unitsync versions installed // together in a sub-dir of the data-dir // The data-dir structure then might look similar to this: // maps/ // games/ // engines/engine-0.83.0.0.exe // engines/engine-0.83.1.0.exe // unitsyncs/unitsync-0.83.0.0.exe // unitsyncs/unitsync-0.83.1.0.exe const std::string dd_curWorkDirParent = FileSystemHandler::GetParent(dd_curWorkDir); #if defined(WIN32) // fetch my documents path TCHAR pathMyDocs[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pathMyDocs); // fetch app-data path TCHAR pathAppData[MAX_PATH]; SHGetFolderPath( NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, pathAppData); std::string dd_myDocs = pathMyDocs; // e.g. F:\Dokumente und Einstellungen\Karl-Robert\Eigene Dateien\Spring dd_myDocs += "\\Spring"; std::string dd_myDocsMyGames = pathMyDocs; // My Documents\My Games seems to be the MS standard even if no official guidelines exist // most if not all new Games For Windows(TM) games use this dir dd_myDocsMyGames += "\\My Games\\Spring"; std::string dd_appData = pathAppData; // e.g. F:\Dokumente und Einstellungen\All Users\Anwendungsdaten\Spring dd_appData += "\\Spring"; #elif defined(MACOSX_BUNDLE) const std::string dd_curWorkDirData = dd_curWorkDir + "/" + SubstEnvVars(DATADIR); const std::string dd_curWorkDirLib = dd_curWorkDir + "/" + SubstEnvVars(LIBDIR); #else // *nix (-OSX) // settings in /etc std::string dd_etc = ""; { FILE* fileH = ::fopen("/etc/spring/datadir", "r"); if (fileH) { const char whiteSpaces[3] = {'\t', ' ', '\0'}; char lineBuf[1024]; while (fgets(lineBuf, sizeof(lineBuf), fileH)) { char* newLineCharPos = strchr(lineBuf, '\n'); if (newLineCharPos) { // remove the new line char *newLineCharPos = '\0'; } // ignore lines consisting of only whitespaces if ((strlen(lineBuf) > 0) && strspn(lineBuf, whiteSpaces) != strlen(lineBuf)) { // append, separated by sPD (depending on OS): ';' or ':' dd_etc = dd_etc + (dd_etc.empty() ? "" : sPD) + SubstEnvVars(lineBuf); } } fclose(fileH); } } #endif // defined(WIN32), defined(MACOSX_BUNDLE), else // Construct the list of datadirs from various sources. datadirs.clear(); // The first dir added will be the writeable data dir. // same on all platforms AddDirs(dd_env); // ENV{SPRING_DATADIR} // user defined in spring config handler // (Linux: ~/.springrc, Windows: .\springsettings.cfg) AddDirs(SubstEnvVars(configHandler->GetString("SpringData", ""))); #ifdef WIN32 // All MS Windows variants if ((dd_curWorkDirParent != "") && LooksLikeMultiVersionDataDir(dd_curWorkDirParent)) { AddDirs(dd_curWorkDirParent); // "../" } else if (IsPortableMode()) { // we can not add both ./ and ../ as data-dir AddDirs(dd_curWorkDir); // "./" } AddDirs(dd_myDocsMyGames); // "C:/.../My Documents/My Games/Spring/" AddDirs(dd_myDocs); // "C:/.../My Documents/Spring/" AddDirs(dd_appData); // "C:/.../All Users/Applications/Spring/" #elif defined(MACOSX_BUNDLE) // Mac OS X // Maps and mods are supposed to be located in spring's executable location on Mac, but unitsync // cannot find them since it does not know spring binary path. I have no idea but to force users // to locate lobby executables in the same as spring's dir and add its location to search dirs. #ifdef UNITSYNC AddDirs(dd_curWorkDir); // "./" #endif // libs and data are supposed to be located in subdirectories of spring executable, so they // sould be added instead of SPRING_DATADIR definition. AddDirs(dd_curWorkDirData); // "./data/" AddDirs(dd_curWorkDirLib); // "./lib/" #else // Linux, FreeBSD, Solaris, Apple non-bundle if ((dd_curWorkDirParent != "") && LooksLikeMultiVersionDataDir(dd_curWorkDirParent)) { AddDirs(dd_curWorkDirParent); // "../" } else if (IsPortableMode()) { // we can not add both ./ and ../ as data-dir // always using this would be unclean, because spring and unitsync // would end up with different sets of data-dirs AddDirs(dd_curWorkDir); // "./" } AddDirs(SubstEnvVars("$HOME/.spring")); // "~/.spring/" AddDirs(dd_etc); // from /etc/spring/datadir #endif #ifdef SPRING_DATADIR AddDirs(SubstEnvVars(SPRING_DATADIR)); // from -DSPRING_DATADIR #endif // Figure out permissions of all datadirs DeterminePermissions(); if (!writedir) { // bail out const std::string errstr = "Not a single writable data directory found!\n\n" "Configure a writable data directory using either:\n" "- the SPRING_DATADIR environment variable,\n" #ifdef WIN32 "- a SpringData=C:/path/to/data declaration in spring's config file ./springsettings.cfg\n" "- by giving you write access to the installation directory"; #else "- a SpringData=/path/to/data declaration in ~/.springrc or\n" "- the configuration file /etc/spring/datadir"; #endif throw content_error(errstr); } // for now, chdir to the data directory as a safety measure: // Not only safety anymore, it's just easier if other code can safely assume that // writedir == current working directory FileSystemHandler::GetInstance().Chdir(GetWriteDir()->path.c_str()); // Initialize the log. Only after this moment log will be written to file. logOutput.Initialize(); // Logging MAY NOT start before the chdir, otherwise the logfile ends up // in the wrong directory. // Update: now it actually may start before, log has preInitLog. for (std::vector<DataDir>::const_iterator d = datadirs.begin(); d != datadirs.end(); ++d) { if (d->writable) { logOutput.Print("Using read-write data directory: %s", d->path.c_str()); // tag the cache dir const std::string cacheDir = d->path + "cache"; if (filesystem.CreateDirectory(cacheDir)) { CacheDir::SetCacheDir(cacheDir, true); } } else { logOutput.Print("Using read-only data directory: %s", d->path.c_str()); } } }
/** * @brief locate spring data directory * * On *nix platforms, attempts to locate * and change to the spring data directory * * The data directory to chdir to is determined by the following, in this * order (first items override lower items): * * - 'SpringData=/path/to/data' declaration in '~/.springrc'. (colon separated list) * - 'SPRING_DATADIR' environment variable. (colon separated list, like PATH) * - In the same order any line in '/etc/spring/datadir', if that file exists. * - 'datadir=/path/to/data' option passed to 'scons configure'. * - 'prefix=/install/path' option passed to scons configure. The datadir is * assumed to be at '$prefix/games/taspring' in this case. * - the default datadir in the default prefix, ie. '/usr/local/games/taspring' * * All of the above methods support environment variable substitution, eg. * '$HOME/myspringdatadir' will be converted by spring to something like * '/home/username/myspringdatadir'. * * If it fails to chdir to the above specified directory spring will asume the * current working directory is the data directory. */ void UnixFileSystemHandler::LocateDataDirs() { // Construct the list of datadirs from various sources. datadirs.clear(); char* env = getenv("SPRING_DATADIR"); if (env && *env) AddDirs(SubstEnvVars(env)); std::string cfg = configHandler.GetString("SpringData",""); if (!cfg.empty()) AddDirs(SubstEnvVars(cfg)); FILE* f = ::fopen("/etc/spring/datadir", "r"); if (f) { char buf[1024]; while (fgets(buf, sizeof(buf), f)) { char* newl = strchr(buf, '\n'); if (newl) *newl = 0; datadirs.push_back(SubstEnvVars(buf)); } fclose(f); } #ifdef SPRING_DATADIR datadirs.push_back(SubstEnvVars(SPRING_DATADIR)); #endif #ifdef SPRING_DATADIR_2 datadirs.push_back(SubstEnvVars(SPRING_DATADIR_2)); #endif // Figure out permissions of all datadirs bool cwdWarning = false; DeterminePermissions(); if (!writedir) { // add current working directory to search path & try again char buf[4096]; getcwd(buf, sizeof(buf)); buf[sizeof(buf) - 1] = 0; datadirs.push_back(DataDir(buf)); DeterminePermissions(datadirs.size() - 1); cwdWarning = true; } if (!writedir) { // bail out throw content_error("not a single read-write data directory found!"); } // for now, chdir to the datadirectory as a safety measure: // all AIs still just assume it's ok to put their stuff in the current directory after all // Not only safety anymore, it's just easier if other code can safely assume that // writedir == current working directory chdir(GetWriteDir().c_str()); // delayed warning message (needs to go after chdir otherwise log file ends up in wrong directory) if (cwdWarning) logOutput.Print("Warning: Adding current working directory to search path."); }