aboutsummaryrefslogtreecommitdiff
path: root/dep/g3dlite/source/FileSystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dep/g3dlite/source/FileSystem.cpp')
-rw-r--r--dep/g3dlite/source/FileSystem.cpp369
1 files changed, 261 insertions, 108 deletions
diff --git a/dep/g3dlite/source/FileSystem.cpp b/dep/g3dlite/source/FileSystem.cpp
index f082937a908..32a84e77048 100644
--- a/dep/g3dlite/source/FileSystem.cpp
+++ b/dep/g3dlite/source/FileSystem.cpp
@@ -1,10 +1,10 @@
/**
- @file FileSystem.cpp
+ \file FileSystem.cpp
- @author Morgan McGuire, http://graphics.cs.williams.edu
+ \author Morgan McGuire, http://graphics.cs.williams.edu
- @author 2002-06-06
- @edited 2010-04-10
+ \author 2002-06-06
+ \edited 2010-10-10
*/
#include "G3D/FileSystem.h"
#include "G3D/System.h"
@@ -19,7 +19,7 @@
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
-#ifdef G3D_WIN32
+#ifdef G3D_WINDOWS
// Needed for _getcwd
# include <direct.h>
@@ -36,16 +36,16 @@
# include <unistd.h>
# define _getcwd getcwd
# define _stat stat
-#endif
-
-#ifdef __CYGWIN__
-#define stat64 stat
+# define stricmp strcasecmp
+# define strnicmp strncasecmp
#endif
namespace G3D {
static FileSystem* common = NULL;
+GMutex FileSystem::mutex;
+
FileSystem& FileSystem::instance() {
init();
return *common;
@@ -70,24 +70,24 @@ FileSystem::FileSystem() : m_cacheLifetime(10) {}
/////////////////////////////////////////////////////////////
-bool FileSystem::Dir::contains(const std::string& f) const {
+bool FileSystem::Dir::contains(const std::string& f, bool caseSensitive) const {
for (int i = 0; i < nodeArray.size(); ++i) {
-# ifdef G3D_WIN32
- if (stricmp(f.c_str(), nodeArray[i].name.c_str()) == 0) {
- return true;
- }
-# else
+ if (caseSensitive) {
if (f == nodeArray[i].name) {
return true;
}
-# endif
+ } else if (stricmp(f.c_str(), nodeArray[i].name.c_str()) == 0) {
+ return true;
+ }
}
return false;
}
+
-void FileSystem::Dir::computeZipListing(const std::string& zipfile, const std::string& pathInsideZipfile) {
+void FileSystem::Dir::computeZipListing(const std::string& zipfile, const std::string& _pathInsideZipfile) {
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
+ const std::string& pathInsideZipfile = FilePath::canonicalize(_pathInsideZipfile);
struct zip* z = zip_open( FilePath::removeTrailingSlash(zipfile).c_str(), ZIP_CHECKCONS, NULL );
debugAssert(z);
@@ -99,22 +99,23 @@ void FileSystem::Dir::computeZipListing(const std::string& zipfile, const std::s
zip_stat_index( z, i, ZIP_FL_NOCASE, &info );
// Fully-qualified name of a file inside zipfile
- std::string name = info.name;
+ std::string name = FilePath::canonicalize(info.name);
if (beginsWith(name, pathInsideZipfile)) {
// We found something inside the directory we were looking for,
- // so the directory itself must exist
+ // so the directory itself must exist
exists = true;
// For building the cached directory listing, extract only elements that do not contain
// additional subdirectories.
- int start = pathInsideZipfile.size();
- if ((int(name.length()) > start) && isSlash(name[start])) {
+ size_t start = pathInsideZipfile.size();
+ if ((name.length() > start) && isSlash(name[start])) {
++start;
}
- int end = findSlash(name, start);
- if (end == -1) {
+
+ size_t end = findSlash(name, start);
+ if (end == std::string::npos) {
// There are no more slashes; add this name
name = name.substr(start);
if (alreadyAdded.insert(name)) {
@@ -126,8 +127,8 @@ void FileSystem::Dir::computeZipListing(const std::string& zipfile, const std::s
// There are more slashes, indicating that this is a directory
name = name.substr(start, end);
if (alreadyAdded.insert(name)) {
- Entry& e = nodeArray.next();
- e.name = name;
+ Entry& e = nodeArray.next();
+ e.name = FilePath::removeTrailingSlash(name);
e.type = DIR_TYPE;
}
}
@@ -142,7 +143,7 @@ void FileSystem::Dir::computeZipListing(const std::string& zipfile, const std::s
FileSystem::Dir& FileSystem::getContents(const std::string& path, bool forceUpdate) {
const std::string& key =
-# if defined(G3D_WIN32)
+# if defined(G3D_WINDOWS)
FilePath::canonicalize(FilePath::removeTrailingSlash(toLower(FilePath::canonicalize(resolve(path)))));
# else
FilePath::canonicalize(FilePath::removeTrailingSlash(FilePath::canonicalize(resolve(path))));
@@ -156,10 +157,17 @@ FileSystem::Dir& FileSystem::getContents(const std::string& path, bool forceUpda
// Out of date: update
dir.lastChecked = now;
-
- struct _stat st;
- const bool exists = _stat(key.c_str(), &st) != -1;
- const bool isDirectory = (st.st_mode & S_IFDIR) != 0;
+# ifdef G3D_WINDOWS
+ // On windows, we have to use GetFileAttributes (http://msdn.microsoft.com/en-us/library/aa364944(v=vs.85).aspx) instead of
+ // stat in order to work with network shares
+ const DWORD st = GetFileAttributesA(key.c_str());
+ const bool exists = (st != INVALID_FILE_ATTRIBUTES);
+ const bool isDirectory = (st & FILE_ATTRIBUTE_DIRECTORY) != 0;
+# else
+ struct _stat st;
+ const bool exists = _stat(key.c_str(), &st) != -1;
+ const bool isDirectory = (st.st_mode & S_IFDIR) != 0;
+# endif
// Does this path exist on the real filesystem?
if (exists && isDirectory) {
@@ -168,7 +176,7 @@ FileSystem::Dir& FileSystem::getContents(const std::string& path, bool forceUpda
if (isDirectory) {
dir.exists = true;
// Update contents
-# ifdef G3D_WIN32
+# ifdef G3D_WINDOWS
const std::string& filespec = FilePath::concat(key, "*");
struct _finddata_t fileinfo;
intptr_t handle = _findfirst(filespec.c_str(), &fileinfo);
@@ -238,7 +246,6 @@ FileSystem::Dir& FileSystem::getContents(const std::string& path, bool forceUpda
// There is a zipfile somewhere in the path. Does
// the rest of the path exist inside the zipfile?
dir.inZipfile = true;
-
dir.computeZipListing(zip, path.substr(zip.length() + 1));
}
}
@@ -248,27 +255,24 @@ FileSystem::Dir& FileSystem::getContents(const std::string& path, bool forceUpda
}
-bool FileSystem::_inZipfile(const std::string& path, std::string& z) {
+bool FileSystem::_inZipfile(const std::string& _path, std::string& z) {
+ const std::string& path = FilePath::expandEnvironmentVariables(_path);
+
// Reject trivial cases before parsing
- if (path.find('.') == std::string::npos) {
- // There is no zipfile possible, since G3D requires
- // an extension on zipfiles.
- return false;
- }
// Look at all sub-paths containing periods.
// For each, ask if it is a zipfile.
- int current = 0;
+ size_t current = 0;
current = path.find('.', current);
- while (current != -1) {
+ while (current != std::string::npos) {
// xxxxx/foo.zip/yyyyy
current = path.find('.', current);
// Look forward for the next slash
- int s = findSlash(path, current);
+ size_t s = findSlash(path, current);
- if (s == -1) {
+ if (s == std::string::npos) {
// No more slashes
return false;
}
@@ -286,17 +290,17 @@ bool FileSystem::_inZipfile(const std::string& path, std::string& z) {
}
-bool FileSystem::_isZipfile(const std::string& filename) {
- if (FilePath::ext(filename).empty()) {
- return false;
- }
+bool FileSystem::_isZipfile(const std::string& _filename) {
+ const std::string& filename = FilePath::canonicalize(FilePath::expandEnvironmentVariables(_filename));
+
+
FILE* f = fopen(FilePath::removeTrailingSlash(filename).c_str(), "r");
if (f == NULL) {
return false;
}
uint8 header[4];
- fread(header, 4, 1, f);
+ (void)fread(header, 4, 1, f);
const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04};
for (int i = 0; i < 4; ++i) {
@@ -311,7 +315,9 @@ bool FileSystem::_isZipfile(const std::string& filename) {
}
-FILE* FileSystem::_fopen(const char* filename, const char* mode) {
+FILE* FileSystem::_fopen(const char* _filename, const char* mode) {
+ const std::string& filename = FilePath::canonicalize(FilePath::expandEnvironmentVariables(_filename));
+
for (const char* m = mode; *m != '\0'; ++m) {
if (*m == 'w') {
// Purge the cache entry for the parent of this directory
@@ -319,11 +325,15 @@ FILE* FileSystem::_fopen(const char* filename, const char* mode) {
break;
}
}
- return ::fopen(filename, mode);
+
+ markFileUsed(filename);
+ return ::fopen(filename.c_str(), mode);
}
-void FileSystem::_clearCache(const std::string& path) {
+void FileSystem::_clearCache(const std::string& _path) {
+ const std::string& path = FilePath::expandEnvironmentVariables(_path);
+
if ((path == "") || FilePath::isRoot(path)) {
m_cache.clear();
} else {
@@ -331,7 +341,7 @@ void FileSystem::_clearCache(const std::string& path) {
m_cache.getKeys(keys);
const std::string& prefix =
-# ifdef G3D_WIN32
+# ifdef G3D_WINDOWS
toLower(FilePath::canonicalize(FilePath::removeTrailingSlash(_resolve(path))));
# else
FilePath::canonicalize(FilePath::removeTrailingSlash(_resolve(path)));
@@ -353,8 +363,9 @@ void FileSystem::_setCacheLifetime(float t) {
}
-void FileSystem::_createDirectory(const std::string& dir) {
-
+void FileSystem::_createDirectory(const std::string& _dir) {
+ const std::string& dir = FilePath::expandEnvironmentVariables(_dir);
+
if (dir == "") {
return;
}
@@ -395,7 +406,7 @@ void FileSystem::_createDirectory(const std::string& dir) {
if (! _exists(p)) {
// Windows only requires one argument to mkdir,
// where as unix also requires the permissions.
-# ifndef G3D_WIN32
+# ifndef G3D_WINDOWS
mkdir(p.c_str(), 0777);
# else
_mkdir(p.c_str());
@@ -407,8 +418,10 @@ void FileSystem::_createDirectory(const std::string& dir) {
}
-void FileSystem::_copyFile(const std::string& source, const std::string& dest) {
-# ifdef G3D_WIN32
+void FileSystem::_copyFile(const std::string& _source, const std::string& _dest) {
+ const std::string& source = FilePath::expandEnvironmentVariables(_source);
+ const std::string& dest = FilePath::expandEnvironmentVariables(_dest);
+# ifdef G3D_WINDOWS
// TODO: handle case where srcPath is in a zipfile
CopyFileA(source.c_str(), dest.c_str(), FALSE);
_clearCache(FilePath::parent(_resolve(dest)));
@@ -422,10 +435,11 @@ void FileSystem::_copyFile(const std::string& source, const std::string& dest) {
}
-bool FileSystem::_exists(const std::string& f, bool trustCache) {
+bool FileSystem::_exists(const std::string& _f, bool trustCache, bool caseSensitive) {
+ const std::string& f = FilePath::expandEnvironmentVariables(_f);
if (FilePath::isRoot(f)) {
-# ifdef G3D_WIN32
+# ifdef G3D_WINDOWS
const std::string& winname = toLower(f.substr(0, 1)) + ":\\";
return _drives().contains(winname);
# else
@@ -433,10 +447,11 @@ bool FileSystem::_exists(const std::string& f, bool trustCache) {
# endif
}
- std::string path = FilePath::removeTrailingSlash(f);
- std::string parentPath = FilePath::parent(path);
+ const std::string& path = FilePath::removeTrailingSlash(f);
+ const std::string& parentPath = FilePath::parent(path);
- const Dir& entry = getContents(parentPath, ! trustCache);
+ const bool forceUpdate = ! trustCache;
+ const Dir& entry = getContents(parentPath, forceUpdate);
if (FilePath::containsWildcards(f)) {
if (! entry.exists) {
@@ -446,15 +461,9 @@ bool FileSystem::_exists(const std::string& f, bool trustCache) {
const std::string& pattern = FilePath::baseExt(path);
-# ifdef G3D_WIN32
- static const int flags = FNM_CASEFOLD;
-# else
- static const int flags = 0;
-# endif
-
// See if any element of entry matches the wild card
for (int i = 0; i < entry.nodeArray.size(); ++i) {
- if (FilePath::matches(entry.nodeArray[i].name, pattern, flags)) {
+ if (FilePath::matches(entry.nodeArray[i].name, pattern, caseSensitive)) {
return true;
}
}
@@ -463,12 +472,13 @@ bool FileSystem::_exists(const std::string& f, bool trustCache) {
return false;
} else {
- return entry.exists && entry.contains(FilePath::baseExt(path));
+ return entry.exists && entry.contains(FilePath::baseExt(path), caseSensitive);
}
}
-bool FileSystem::_isDirectory(const std::string& filename) {
+bool FileSystem::_isDirectory(const std::string& _filename) {
+ const std::string& filename = FilePath::expandEnvironmentVariables(_filename);
// TODO: work with zipfiles and cache
struct _stat st;
const bool exists = _stat(FilePath::removeTrailingSlash(filename).c_str(), &st) != -1;
@@ -476,14 +486,33 @@ bool FileSystem::_isDirectory(const std::string& filename) {
}
-std::string FileSystem::_resolve(const std::string& filename, const std::string& cwd) {
+void FileSystem::_removeFile(const std::string& path) {
+ alwaysAssertM(! inZipfile(path), "Cannot invoke removeFile() on files inside zipfiles.");
+ Array<std::string> files;
+ getFiles(path, files, true);
+
+ for (int i = 0; i < files.size(); ++i) {
+ const std::string& filename = files[i];
+ int retval = ::remove(filename.c_str());
+ (void)retval;
+ }
+
+ // Remove from cache
+ _clearCache(FilePath::parent(path));
+}
+
+
+std::string FileSystem::_resolve(const std::string& _filename, const std::string& _cwd) {
+ const std::string& filename = FilePath::expandEnvironmentVariables(_filename);
+ const std::string& cwd = FilePath::expandEnvironmentVariables(_cwd);
+
if (filename.size() >= 1) {
if (isSlash(filename[0])) {
// Already resolved
return filename;
} else {
- #ifdef G3D_WIN32
+ #ifdef G3D_WINDOWS
if ((filename.size() >= 2) && (filename[1] == ':')) {
// There is a drive spec on the front.
if ((filename.size() >= 3) && isSlash(filename[2])) {
@@ -511,12 +540,29 @@ std::string FileSystem::_currentDirectory() {
static const int N = 2048;
char buffer[N];
- _getcwd(buffer, N);
+ (void)_getcwd(buffer, N);
return std::string(buffer);
}
-bool FileSystem::_isNewer(const std::string& src, const std::string& dst) {
+static Set<std::string> _filesUsed;
+
+void FileSystem::markFileUsed(const std::string& filename) {
+ mutex.lock();
+ _filesUsed.insert(filename);
+ mutex.unlock();
+}
+
+
+const Set<std::string>& FileSystem::usedFiles() {
+ return _filesUsed;
+}
+
+
+bool FileSystem::_isNewer(const std::string& _src, const std::string& _dst) {
+ const std::string& src = FilePath::expandEnvironmentVariables(_src);
+ const std::string& dst = FilePath::expandEnvironmentVariables(_dst);
+
// TODO: work with cache and zipfiles
struct _stat sts;
bool sexists = _stat(src.c_str(), &sts) != -1;
@@ -528,7 +574,9 @@ bool FileSystem::_isNewer(const std::string& src, const std::string& dst) {
}
-int64 FileSystem::_size(const std::string& filename) {
+int64 FileSystem::_size(const std::string& _filename) {
+ const std::string& filename = FilePath::canonicalize(FilePath::expandEnvironmentVariables(_filename));
+
struct stat64 st;
int result = stat64(filename.c_str(), &st);
@@ -544,6 +592,7 @@ int64 FileSystem::_size(const std::string& filename) {
struct zip_stat info;
zip_stat_init( &info ); // Docs unclear if zip_stat_init is required.
int success = zip_stat( z, contents.c_str(), ZIP_FL_NOCASE, &info );
+ (void) success;
debugAssertM(success == 0, zip + ": " + contents + ": zip stat failed.");
requiredMem = info.size;
}
@@ -553,7 +602,7 @@ int64 FileSystem::_size(const std::string& filename) {
#endif
return -1;
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
- }
+ }
#endif
}
@@ -561,7 +610,12 @@ int64 FileSystem::_size(const std::string& filename) {
}
-void FileSystem::listHelper(const std::string& shortSpec, const std::string& parentPath, Array<std::string>& result, const ListSettings& settings) {
+void FileSystem::listHelper
+(const std::string& shortSpec,
+ const std::string& parentPath,
+ Array<std::string>& result,
+ const ListSettings& settings) {
+
Dir& dir = getContents(parentPath, false);
if (! dir.exists) {
@@ -573,8 +627,10 @@ void FileSystem::listHelper(const std::string& shortSpec, const std::string& par
// See if it matches the spec
if (FilePath::matches(entry.name, shortSpec, settings.caseSensitive)) {
- if ((entry.type == UNKNOWN) && ! (settings.files && settings.directories)) {
- // Update the type
+ if ((entry.type == UNKNOWN) &&
+ (! (settings.files && settings.directories) ||
+ settings.recursive)) {
+ // Update the type: it is unknown and we'll need to branch onit below
entry.type = isDirectory(FilePath::concat(parentPath, entry.name)) ? DIR_TYPE : FILE_TYPE;
}
@@ -589,15 +645,23 @@ void FileSystem::listHelper(const std::string& shortSpec, const std::string& par
}
}
} // match
+
+ if (settings.recursive) {
+ if (entry.type == UNKNOWN) {
+ entry.type = isDirectory(FilePath::concat(parentPath, entry.name)) ? DIR_TYPE : FILE_TYPE;
+ }
- if (settings.recursive && (entry.type == DIR_TYPE)) {
- listHelper(shortSpec, FilePath::concat(parentPath, entry.name), result, settings);
+ if (entry.type == DIR_TYPE) {
+ listHelper(shortSpec, FilePath::concat(parentPath, entry.name), result, settings);
+ }
}
} // for
}
-void FileSystem::_list(const std::string& spec, Array<std::string>& result, const ListSettings& settings) {
+void FileSystem::_list(const std::string& _spec, Array<std::string>& result, const ListSettings& settings) {
+ const std::string& spec = FilePath::expandEnvironmentVariables(_spec);
+
const std::string& shortSpec = FilePath::baseExt(spec);
const std::string& parentPath = FilePath::parent(spec);
@@ -606,7 +670,7 @@ void FileSystem::_list(const std::string& spec, Array<std::string>& result, cons
-#ifdef G3D_WIN32
+#ifdef G3D_WINDOWS
const Array<std::string>& FileSystem::_drives() {
if (m_winDrive.length() == 0) {
// See http://msdn.microsoft.com/en-us/library/aa364975(VS.85).aspx
@@ -615,10 +679,10 @@ const Array<std::string>& FileSystem::_drives() {
GetLogicalDriveStringsA(bufSize, bufData);
// Drive list is a series of NULL-terminated strings, itself terminated with a NULL.
- for (int i = 0; bufData[i] != '\0'; ++i) {
+ for (size_t i = 0; bufData[i] != '\0'; ++i) {
const char* thisString = bufData + i;
m_winDrive.append(toLower(thisString));
- i += strlen(thisString) + 1;
+ i += strlen(thisString);
}
}
@@ -629,7 +693,7 @@ const Array<std::string>& FileSystem::_drives() {
/////////////////////////////////////////////////////////////////////
bool FilePath::isRoot(const std::string& f) {
-# ifdef G3D_WIN32
+# ifdef G3D_WINDOWS
if (f.length() < 2) {
return false;
}
@@ -644,9 +708,17 @@ bool FilePath::isRoot(const std::string& f) {
}
}
- if (isSlash(f[0]) && isSlash(f[1])) {
- // e.g., "\\foo\"
- return true;
+ // Windows shares are considered roots, but only if this does not include a path inside the share
+ if (isSlash(f[0]) && isSlash(f[1])) {
+ size_t i = f.find("/", 3);
+ size_t j = f.find("\\", 3);
+
+ if (i == std::string::npos) {
+ i = j;
+ }
+
+ // e.g., "\\foo\", "\\foo"
+ return ((i == std::string::npos) || (i == f.length() - 1));
}
# else
if (f == "/") {
@@ -678,8 +750,8 @@ std::string FilePath::concat(const std::string& dirname, const std::string& file
std::string FilePath::ext(const std::string& filename) {
- int i = filename.rfind(".");
- if (i >= 0) {
+ size_t i = filename.rfind(".");
+ if (i != std::string::npos) {
return filename.substr(i + 1, filename.length() - i);
} else {
return "";
@@ -688,16 +760,16 @@ std::string FilePath::ext(const std::string& filename) {
std::string FilePath::baseExt(const std::string& filename) {
- int i = findLastSlash(filename);
+ size_t i = findLastSlash(filename);
-# ifdef G3D_WIN32
- int j = filename.rfind(":");
- if ((i == -1) && (j >= 0)) {
+# ifdef G3D_WINDOWS
+ size_t j = filename.rfind(":");
+ if ((i == std::string::npos) && (j != std::string::npos)) {
i = j;
}
# endif
- if (i == -1) {
+ if (i == std::string::npos) {
return filename;
} else {
return filename.substr(i + 1, filename.length() - i);
@@ -707,8 +779,8 @@ std::string FilePath::baseExt(const std::string& filename) {
std::string FilePath::base(const std::string& path) {
std::string filename = baseExt(path);
- int i = filename.rfind(".");
- if (i == -1) {
+ size_t i = filename.rfind(".");
+ if (i == std::string::npos) {
// No extension
return filename;
} else {
@@ -718,16 +790,16 @@ std::string FilePath::base(const std::string& path) {
std::string FilePath::parent(const std::string& path) {
- int i = findLastSlash(removeTrailingSlash(path));
+ size_t i = findLastSlash(removeTrailingSlash(path));
-# ifdef G3D_WIN32
- int j = path.rfind(":");
- if ((i == -1) && (j >= 0)) {
+# ifdef G3D_WINDOWS
+ size_t j = path.rfind(":");
+ if ((i == std::string::npos) && (j != std::string::npos)) {
i = j;
}
# endif
- if (i == -1) {
+ if (i == std::string::npos) {
return "";
} else {
return path.substr(0, i + 1);
@@ -759,7 +831,6 @@ std::string FilePath::canonicalize(std::string x) {
return x;
}
-
void FilePath::parse
(const std::string& filename,
std::string& root,
@@ -796,7 +867,7 @@ void FilePath::parse
}
- } else if ((f.size() >= 2) & isSlash(f[0]) && isSlash(f[1])) {
+ } else if ((f.size() >= 2) && isSlash(f[0]) && isSlash(f[1])) {
// e.g. //foo
root = f.substr(0, 2);
@@ -816,7 +887,7 @@ void FilePath::parse
if (i != std::string::npos) {
// Make sure it is after the last slash!
- size_t j = iMax(f.rfind('/'), f.rfind('\\'));
+ size_t j = findLastSlash(f);
if ((j == std::string::npos) || (i > j)) {
ext = f.substr(i + 1, f.size() - i - 1);
f = f.substr(0, i);
@@ -827,7 +898,7 @@ void FilePath::parse
// Pull the basename off
{
// Find the last slash
- size_t i = iMax(f.rfind('/'), f.rfind('\\'));
+ size_t i = findLastSlash(f);
if (i == std::string::npos) {
@@ -860,7 +931,7 @@ void FilePath::parse
j = f.size();
}
- cur = iMin(i, j);
+ cur = min(i, j);
if (cur == std::string::npos) {
cur = f.size();
@@ -871,4 +942,86 @@ void FilePath::parse
}
}
+
+std::string FilePath::expandEnvironmentVariables(const std::string& path) {
+ // Search for pattern
+ size_t end = path.find_first_of('$', 0);
+ if (end == std::string::npos) {
+ // Pattern does not exist
+ return path;
+ }
+
+ size_t start = 0;
+ std::string result;
+ while (end != std::string::npos) {
+ const std::string& before = path.substr(start, end - start);
+ result += before;
+ start = end + 1;
+ std::string var;
+ if (path[start] == '(') {
+ // Search for close paren
+ end = path.find_first_of(')', start + 1);
+ if (end == std::string::npos) {
+ throw std::string("Missing close paren in environment variable in \"") + path + "\"";
+ }
+ var = path.substr(start + 1, end - start - 1);
+ } else {
+ // Search for slash or end of string
+ end = path.find_first_of('/', start);
+ size_t i = path.find_first_of('\\', start);
+ if ((size_t(i) != std::string::npos) && ((end == std::string::npos) || (size_t(i) < end))) {
+ end = i;
+ }
+ if (end == std::string::npos) {
+ // If the varible goes to the end of the string, it is the rest of the string
+ end = path.size();
+ } else {
+ --end;
+ }
+ var = path.substr(start, end - start + 1);
+ }
+
+ if (! var.empty()) {
+ const char* value = getenv(var.c_str());
+
+ if (value == NULL) {
+ throw (std::string("LocalLightingEnvironment variable \"") + var + "\" not defined for path \"" + path + "\"");
+ } else {
+ result += value;
+ }
+ } else {
+ // We just parsed an "empty" variable, which was probably a default share on Windows, e.g.,
+ // "\\mycomputer\c$", and not a variable name.
+ result += "$";
+ }
+
+ start = end + 1;
+ end = path.find_first_of('$', start);
+ }
+
+ // Paste on the remainder of the source path
+ if (start < path.size()) {
+ result += path.substr(start);
+ }
+
+ return result;
+}
+
+
+/** Generate a unique filename based on the provided hint */
+std::string FilePath::makeLegalFilename(const std::string& f, size_t maxLength) {
+ std::string tentative;
+
+ for (size_t i = 0; i < G3D::min(maxLength, f.size()); ++i) {
+ const char c = f[i];
+ if (isLetter(c) || isDigit(c) || (c == '-') || (c == '+') || (c == '=') || (c == '(') || (c == ')')) {
+ tentative += c;
+ } else {
+ tentative += "_";
+ }
+ }
+
+ return tentative;
+}
+
}