/**
 * @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.");
}
Beispiel #2
0
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());
		}
	}
}