/** * @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); } #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."); }
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 pathMyDocsC[MAX_PATH]; SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pathMyDocsC); const std::string pathMyDocs = pathMyDocsC; // e.g. F:\Dokumente und Einstellungen\Karl-Robert\Eigene Dateien\Spring const std::string dd_myDocs = pathMyDocs + "\\Spring"; // 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 const std::string dd_myDocsMyGames = pathMyDocs + "\\My Games\\Spring"; // fetch common app-data path TCHAR pathAppDataC[MAX_PATH]; SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, pathAppDataC); const std::string pathAppData = pathAppDataC; // e.g. F:\Dokumente und Einstellungen\All Users\Anwendungsdaten\Spring const std::string dd_appData = pathAppData + "\\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(Platform::GetUserDir() + "/.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(Platform::GetUserDir() + "/.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()); } } }