/**
 * @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());
	}
}
/**
 * @brief removes a file from all writable data directories
 *
 * Returns true if at least one remove succeeded.
 */
bool UnixFileSystemHandler::remove(const std::string& file) const
{
	// if it's an absolute path, don't remove inside the data directories
	if (file[0] == '/')
		return ::remove(file.c_str()) == 0;

	return ::remove((GetWriteDir() + file).c_str()) == 0;
}
/**
 * @brief creates a rwxr-xr-x dir in the writedir
 *
 * Returns true if the postcondition of this function is that dir exists in
 * the write directory.
 *
 * Note that this function does not check access to the dir, ie. if you've
 * created it manually with 0000 permissions then this function may return
 * true, subsequent operation on files inside the directory may still fail.
 *
 * As a rule of thumb, set identical permissions on identical items in the
 * data directory, ie. all subdirectories the same perms, all files the same
 * perms.
 */
bool UnixFileSystemHandler::mkdir(const std::string& dir) const
{
	// if it's an absolute path, don't mkdir inside the data directories
	if (dir[0] == '/')
		return mkdir_helper(dir);

	return mkdir_helper(GetWriteDir() + dir);
}
/**
 * @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());
}
Example #5
0
std::string DataDirLocater::GetWriteDirPath() const
{
	const DataDir* writedir = GetWriteDir();
	assert(writedir && writedir->writable); // duh
	return writedir->path;
}
Example #6
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 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.");
}