Created
May 31, 2013 21:50
-
-
Save kahrl/5688237 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/builtin/mainmenu.lua b/builtin/mainmenu.lua | |
index 808c165..ddf500a 100644 | |
--- a/builtin/mainmenu.lua | |
+++ b/builtin/mainmenu.lua | |
@@ -554,7 +554,7 @@ function tabbuilder.handle_delete_world_buttons(fields) | |
if fields["world_delete_confirm"] then | |
if menu.last_world > 0 and | |
- menu.last_world < #menu.worldlist then | |
+ menu.last_world <= #menu.worldlist then | |
engine.delete_world(menu.last_world) | |
menu.worldlist = engine.get_worlds() | |
menu.last_world = 1 | |
diff --git a/src/filesys.cpp b/src/filesys.cpp | |
index 06b0e48..7434dda 100644 | |
--- a/src/filesys.cpp | |
+++ b/src/filesys.cpp | |
@@ -146,6 +146,11 @@ bool IsDir(std::string path) | |
(attr & FILE_ATTRIBUTE_DIRECTORY)); | |
} | |
+bool IsDirDelimiter(char c) | |
+{ | |
+ return c == '/' || c == '\\'; | |
+} | |
+ | |
bool RecursiveDelete(std::string path) | |
{ | |
infostream<<"Recursively deleting \""<<path<<"\""<<std::endl; | |
@@ -221,6 +226,22 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path) | |
} | |
} | |
+std::string TempPath() { | |
+ DWORD bufsize = GetTempPath(0, ""); | |
+ if(bufsize == 0){ | |
+ errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl; | |
+ return ""; | |
+ } | |
+ std::vector<char> buf(bufsize); | |
+ DWORD len = GetTempPath(bufsize, &buf[0]); | |
+ if(len == 0 || len > bufsize){ | |
+ errorstream<<"GetTempPath failed, error = "<<GetLastError()<<std::endl; | |
+ return ""; | |
+ } | |
+ return std::string(buf.begin(), buf.begin() + len); | |
+} | |
+ | |
+ | |
#else // POSIX | |
#include <sys/types.h> | |
@@ -335,6 +356,11 @@ bool IsDir(std::string path) | |
return ((statbuf.st_mode & S_IFDIR) == S_IFDIR); | |
} | |
+bool IsDirDelimiter(char c) | |
+{ | |
+ return c == '/'; | |
+} | |
+ | |
bool RecursiveDelete(std::string path) | |
{ | |
/* | |
@@ -398,6 +424,10 @@ bool DeleteSingleFileOrEmptyDirectory(std::string path) | |
} | |
} | |
+std::string TempPath() { | |
+ return "/tmp"; | |
+} | |
+ | |
#endif | |
void GetRecursiveSubPaths(std::string path, std::vector<std::string> &dst) | |
@@ -448,16 +478,14 @@ bool RecursiveDeleteContent(std::string path) | |
bool CreateAllDirs(std::string path) | |
{ | |
- size_t pos; | |
std::vector<std::string> tocreate; | |
std::string basepath = path; | |
while(!PathExists(basepath)) | |
{ | |
tocreate.push_back(basepath); | |
- pos = basepath.rfind(DIR_DELIM_C); | |
- if(pos == std::string::npos) | |
+ basepath = RemoveLastPathComponent(basepath); | |
+ if(basepath.empty()) | |
break; | |
- basepath = basepath.substr(0,pos); | |
} | |
for(int i=tocreate.size()-1;i>=0;i--) | |
if(!CreateDir(tocreate[i])) | |
@@ -527,5 +555,133 @@ bool CopyDir(std::string source,std::string target) | |
} | |
} | |
+bool PathStartsWith(std::string path, std::string prefix) | |
+{ | |
+ size_t pathsize = path.size(); | |
+ size_t pathpos = 0; | |
+ size_t prefixsize = prefix.size(); | |
+ size_t prefixpos = 0; | |
+ for(;;){ | |
+ bool delim1 = pathpos == pathsize | |
+ || IsDirDelimiter(path[pathpos]); | |
+ bool delim2 = prefixpos == prefixsize | |
+ || IsDirDelimiter(prefix[prefixpos]); | |
+ | |
+ if(delim1 != delim2) | |
+ return false; | |
+ | |
+ if(delim1){ | |
+ while(pathpos < pathsize && IsDirDelimiter(path[pathpos])) | |
+ ++pathpos; | |
+ while(prefixpos < prefixsize && IsDirDelimiter(prefix[prefixpos])) | |
+ ++prefixpos; | |
+ if(prefixpos == prefixsize) | |
+ return true; | |
+ if(pathpos == pathsize) | |
+ return false; | |
+ } | |
+ else{ | |
+ size_t len = 0; | |
+ do{ | |
+ char pathchar = path[pathpos+len]; | |
+ char prefixchar = prefix[prefixpos+len]; | |
+ if(FILESYS_CASE_INSENSITIVE){ | |
+ pathchar = tolower(pathchar); | |
+ prefixchar = tolower(prefixchar); | |
+ } | |
+ if(pathchar != prefixchar) | |
+ return false; | |
+ ++len; | |
+ } while(pathpos+len < pathsize | |
+ && !IsDirDelimiter(path[pathpos+len]) | |
+ && prefixpos+len < prefixsize | |
+ && !IsDirDelimiter(prefix[prefixsize+len])); | |
+ pathpos += len; | |
+ prefixpos += len; | |
+ } | |
+ } | |
+} | |
+ | |
+std::string RemoveLastPathComponent(std::string path, | |
+ std::string *removed, int count) | |
+{ | |
+ if(removed) | |
+ *removed = ""; | |
+ | |
+ size_t remaining = path.size(); | |
+ | |
+ for(int i = 0; i < count; ++i){ | |
+ // strip a dir delimiter | |
+ while(remaining != 0 && IsDirDelimiter(path[remaining-1])) | |
+ remaining--; | |
+ // strip a path component | |
+ size_t component_end = remaining; | |
+ while(remaining != 0 && !IsDirDelimiter(path[remaining-1])) | |
+ remaining--; | |
+ size_t component_start = remaining; | |
+ // strip a dir delimiter | |
+ while(remaining != 0 && IsDirDelimiter(path[remaining-1])) | |
+ remaining--; | |
+ if(removed){ | |
+ std::string component = path.substr(component_start, | |
+ component_end - component_start); | |
+ if(i) | |
+ *removed = component + DIR_DELIM + *removed; | |
+ else | |
+ *removed = component; | |
+ } | |
+ } | |
+ return path.substr(0, remaining); | |
+} | |
+ | |
+std::string RemoveRelativePathComponents(std::string path) | |
+{ | |
+ size_t pos = path.size(); | |
+ size_t dotdot_count = 0; | |
+ while(pos != 0){ | |
+ size_t component_with_delim_end = pos; | |
+ // skip a dir delimiter | |
+ while(pos != 0 && IsDirDelimiter(path[pos-1])) | |
+ pos--; | |
+ // strip a path component | |
+ size_t component_end = pos; | |
+ while(pos != 0 && !IsDirDelimiter(path[pos-1])) | |
+ pos--; | |
+ size_t component_start = pos; | |
+ | |
+ std::string component = path.substr(component_start, component_end - component_start); | |
+ bool remove_this_component = false; | |
+ if(component == "."){ | |
+ remove_this_component = true; | |
+ } | |
+ else if(component == ".."){ | |
+ remove_this_component = true; | |
+ dotdot_count += 1; | |
+ } | |
+ else if(dotdot_count != 0){ | |
+ remove_this_component = true; | |
+ dotdot_count -= 1; | |
+ } | |
+ | |
+ if(remove_this_component){ | |
+ while(pos != 0 && IsDirDelimiter(path[pos-1])) | |
+ pos--; | |
+ path = path.substr(0, pos) + DIR_DELIM + | |
+ path.substr(component_with_delim_end, std::string::npos); | |
+ pos++; | |
+ } | |
+ } | |
+ | |
+ if(dotdot_count > 0) | |
+ return ""; | |
+ | |
+ // remove trailing dir delimiters | |
+ pos = path.size(); | |
+ while(pos != 0 && IsDirDelimiter(path[pos-1])) | |
+ pos--; | |
+ return path.substr(0, pos); | |
+} | |
+ | |
+ | |
} // namespace fs | |
diff --git a/src/filesys.h b/src/filesys.h | |
index 15fa0ad..b264935 100644 | |
--- a/src/filesys.h | |
+++ b/src/filesys.h | |
@@ -26,10 +26,10 @@ | |
#ifdef _WIN32 // WINDOWS | |
#define DIR_DELIM "\\" | |
-#define DIR_DELIM_C '\\' | |
+#define FILESYS_CASE_INSENSITIVE 1 | |
#else // POSIX | |
#define DIR_DELIM "/" | |
-#define DIR_DELIM_C '/' | |
+#define FILESYS_CASE_INSENSITIVE 0 | |
#endif | |
namespace fs | |
@@ -49,12 +49,20 @@ struct DirListNode | |
bool IsDir(std::string path); | |
+bool IsDirDelimiter(char c); | |
+ | |
// Only pass full paths to this one. True on success. | |
// NOTE: The WIN32 version returns always true. | |
bool RecursiveDelete(std::string path); | |
bool DeleteSingleFileOrEmptyDirectory(std::string path); | |
+// Get absolute path from a given relative one | |
+std::string AbsolutePath(std::string path); | |
+ | |
+// Returns path to temp directory, can return "" on error | |
+std::string TempPath(); | |
+ | |
/* Multiplatform */ | |
// The path itself not included | |
@@ -72,8 +80,22 @@ struct DirListNode | |
// Copy directory and all subdirectorys | |
bool CopyDir(std::string source,std::string target); | |
-//get absolute path from a given relative one | |
-std::string AbsolutePath(std::string path); | |
+// Check if one path is prefix of another | |
+// For example, "/tmp" is a prefix of "/tmp" and "/tmp/file" but not "/tmp2" | |
+// Ignores case differences and '/' vs. '\\' on Windows | |
+bool PathStartsWith(std::string path, std::string prefix); | |
+ | |
+// Remove last path component and the dir delimiter before and/or after it, | |
+// returns "" if there is only one path component. | |
+// removed: If non-NULL, receives the removed component(s). | |
+// count: Number of components to remove | |
+std::string RemoveLastPathComponent(std::string path, | |
+ std::string *removed = NULL, int count = 1); | |
+ | |
+// Remove "." and ".." path components and for every ".." removed, remove | |
+// the last normal path component before it. Unlike AbsolutePath, | |
+// this does not resolve symlinks and check for existence of directories. | |
+std::string RemoveRelativePathComponents(std::string path); | |
}//fs | |
diff --git a/src/guiLuaApi.cpp b/src/guiLuaApi.cpp | |
index db345d3..5175887 100644 | |
--- a/src/guiLuaApi.cpp | |
+++ b/src/guiLuaApi.cpp | |
@@ -714,9 +714,9 @@ int guiLuaApi::l_get_dirlist(lua_State *L) { | |
/******************************************************************************/ | |
int guiLuaApi::l_create_dir(lua_State *L) { | |
- const char *path = luaL_checkstring(L, 1); | |
+ std::string path = luaL_checkstring(L, 1); | |
- if (guiLuaApi::isMinetestPath(path)) { | |
+ if (guiLuaApi::isMinetestPath(path, true)) { | |
lua_pushboolean(L,fs::CreateAllDirs(path)); | |
return 1; | |
} | |
@@ -726,12 +726,10 @@ int guiLuaApi::l_create_dir(lua_State *L) { | |
/******************************************************************************/ | |
int guiLuaApi::l_delete_dir(lua_State *L) { | |
- const char *path = luaL_checkstring(L, 1); | |
- | |
- std::string absolute_path = fs::AbsolutePath(path); | |
+ std::string path = luaL_checkstring(L, 1); | |
- if (guiLuaApi::isMinetestPath(absolute_path)) { | |
- lua_pushboolean(L,fs::RecursiveDelete(absolute_path)); | |
+ if (guiLuaApi::isMinetestPath(path, false)) { | |
+ lua_pushboolean(L,fs::RecursiveDelete(path)); | |
return 1; | |
} | |
lua_pushboolean(L,false); | |
@@ -740,8 +738,8 @@ int guiLuaApi::l_delete_dir(lua_State *L) { | |
/******************************************************************************/ | |
int guiLuaApi::l_copy_dir(lua_State *L) { | |
- const char *source = luaL_checkstring(L, 1); | |
- const char *destination = luaL_checkstring(L, 2); | |
+ std::string source = luaL_checkstring(L, 1); | |
+ std::string destination = luaL_checkstring(L, 2); | |
bool keep_source = true; | |
@@ -750,21 +748,13 @@ int guiLuaApi::l_copy_dir(lua_State *L) { | |
keep_source = lua_toboolean(L,3); | |
} | |
- //deny relative destination paths | |
- if (std::string(destination).find("..") != std::string::npos) { | |
- lua_pushboolean(L,false); | |
- return 1; | |
- } | |
- | |
- std::string absolute_source = fs::AbsolutePath(source); | |
- | |
- if ((guiLuaApi::isMinetestPath(absolute_source)) && | |
- (guiLuaApi::isMinetestPath(destination))) { | |
- bool retval = fs::CopyDir(absolute_source,destination); | |
+ if ((guiLuaApi::isMinetestPath(source, false)) && | |
+ (guiLuaApi::isMinetestPath(destination, false))) { | |
+ bool retval = fs::CopyDir(source,destination); | |
if (retval && (!keep_source)) { | |
- retval &= fs::RecursiveDelete(absolute_source); | |
+ retval &= fs::RecursiveDelete(source); | |
} | |
lua_pushboolean(L,retval); | |
return 1; | |
@@ -775,12 +765,10 @@ int guiLuaApi::l_copy_dir(lua_State *L) { | |
/******************************************************************************/ | |
int guiLuaApi::l_extract_zip(lua_State *L) { | |
- const char *zipfile = luaL_checkstring(L, 1); | |
- const char *destination = luaL_checkstring(L, 2); | |
+ std::string zipfile = luaL_checkstring(L, 1); | |
+ std::string destination = luaL_checkstring(L, 2); | |
- std::string absolute_destination = fs::AbsolutePath(destination); | |
- | |
- if (guiLuaApi::isMinetestPath(absolute_destination)) { | |
+ if (guiLuaApi::isMinetestPath(destination, false)) { | |
#ifdef HAVE_MINIZIP | |
#else | |
@@ -810,33 +798,56 @@ int guiLuaApi::l_get_scriptdir(lua_State *L) { | |
} | |
/******************************************************************************/ | |
-bool guiLuaApi::isMinetestPath(std::string path) { | |
+bool guiLuaApi::isMinetestPath(std::string &path, bool allow_missing_parent) { | |
+ | |
+ /* | |
+ Make path absolute. | |
+ | |
+ The point of this is to avoid directory traversal attacks. | |
+ */ | |
+ if (allow_missing_parent) { | |
+ /* | |
+ Note: used when creating directory trees (l_create_dir) | |
+ fs::AbsolutePath(path) doesn't work if parent does | |
+ not exist, so try removing path components at a time | |
+ */ | |
+ path = fs::RemoveRelativePathComponents(path); | |
+ for (int count = 0; ; count++) { | |
+ std::string removed; | |
+ std::string prefix = fs::RemoveLastPathComponent(path, &removed, count); | |
+ // the following abort condition ensures | |
+ // that isMinetestPath always terminates | |
+ if (prefix == "") | |
+ return false; | |
+ prefix = fs::AbsolutePath(prefix); | |
+ if (prefix != "") { | |
+ path = prefix + DIR_DELIM + removed; | |
+ } | |
+ } | |
+ } else { | |
+ path = fs::AbsolutePath(path); | |
+ if (path == "") | |
+ return false; | |
+ } | |
/* temp */ | |
-#ifdef _WIN32 // WINDOWS | |
- char* buf [MAX_PATH]; | |
- | |
- if (GetTempPath (MAX_PATH, buf) != 0) { | |
- if (fs::AbsolutePath(path).find(buf) == 0) | |
- return true; | |
- } | |
-#else | |
- if ((std::string(DIR_DELIM) == "/") && | |
- (fs::AbsolutePath(path).find("/tmp") == 0)) | |
+ std::string temp_path = fs::TempPath(); | |
+ if (temp_path != "" && fs::PathStartsWith(path, temp_path)) | |
return true; | |
-#endif | |
/* games */ | |
- if (path.find(fs::AbsolutePath(porting::path_share + DIR_DELIM + "games")) == 0) | |
+ if (fs::PathStartsWith(path, porting::path_share + DIR_DELIM + "games")) | |
return true; | |
/* mods */ | |
- if (path.find(fs::AbsolutePath(porting::path_share + DIR_DELIM + "mods")) == 0) | |
+ if (fs::PathStartsWith(path, porting::path_share + DIR_DELIM + "mods")) | |
return true; | |
/* worlds */ | |
- if (path.find(fs::AbsolutePath(porting::path_user + DIR_DELIM + "worlds")) == 0) | |
+ if (fs::PathStartsWith(path, porting::path_user + DIR_DELIM + "worlds")) | |
+ return true; | |
+ if (fs::PathStartsWith(path, porting::path_user + DIR_DELIM + "world")) | |
return true; | |
diff --git a/src/guiLuaApi.h b/src/guiLuaApi.h | |
index 6a5f3c4..8973c4e 100644 | |
--- a/src/guiLuaApi.h | |
+++ b/src/guiLuaApi.h | |
@@ -102,10 +102,11 @@ class guiLuaApi { | |
/** | |
* check if a path is within some of minetests folders | |
- * @param path path to check | |
+ * @param path path to check, replaced with absolute path | |
+ * @param allow_missing_parent accept if parent directory is missing | |
* @return true/false | |
*/ | |
- static bool isMinetestPath(std::string path); | |
+ static bool isMinetestPath(std::string &path, bool allow_missing_parent); | |
//api calls | |
diff --git a/src/map.cpp b/src/map.cpp | |
index d5ab8eb..434170a 100644 | |
--- a/src/map.cpp | |
+++ b/src/map.cpp | |
@@ -3228,17 +3228,18 @@ v2s16 ServerMap::getSectorPos(std::string dirname) | |
{ | |
unsigned int x, y; | |
int r; | |
- size_t spos = dirname.rfind(DIR_DELIM_C) + 1; | |
- assert(spos != std::string::npos); | |
- if(dirname.size() - spos == 8) | |
+ std::string component; | |
+ fs::RemoveLastPathComponent(dirname, &component, 1); | |
+ if(component.size() == 8) | |
{ | |
// Old layout | |
- r = sscanf(dirname.substr(spos).c_str(), "%4x%4x", &x, &y); | |
+ r = sscanf(component.c_str(), "%4x%4x", &x, &y); | |
} | |
- else if(dirname.size() - spos == 3) | |
+ else if(component.size() == 3) | |
{ | |
// New layout | |
- r = sscanf(dirname.substr(spos-4).c_str(), "%3x" DIR_DELIM "%3x", &x, &y); | |
+ fs::RemoveLastPathComponent(dirname, &component, 2); | |
+ r = sscanf(component.c_str(), "%3x" DIR_DELIM "%3x", &x, &y); | |
// Sign-extend the 12 bit values up to 16 bits... | |
if(x&0x800) x|=0xF000; | |
if(y&0x800) y|=0xF000; | |
diff --git a/src/test.cpp b/src/test.cpp | |
index 3a0316e..2d220e8 100644 | |
--- a/src/test.cpp | |
+++ b/src/test.cpp | |
@@ -36,6 +36,7 @@ | |
#include "settings.h" | |
#include "log.h" | |
#include "util/string.h" | |
+#include "filesys.h" | |
#include "voxelalgorithms.h" | |
#include "inventory.h" | |
#include "util/numeric.h" | |
@@ -171,6 +172,213 @@ struct TestUtilities: public TestBase | |
} | |
}; | |
+struct TestPath: public TestBase | |
+{ | |
+ // adjusts a POSIX path to system-specific conventions | |
+ // -> changes '/' to DIR_DELIM | |
+ // -> absolute paths start with "C:\\" on windows | |
+ std::string p(std::string path) | |
+ { | |
+ for(size_t i = 0; i < path.size(); ++i){ | |
+ if(path[i] == '/'){ | |
+ path.replace(i, 1, DIR_DELIM); | |
+ i += std::string(DIR_DELIM).size() - 1; // generally a no-op | |
+ } | |
+ } | |
+ | |
+ #ifdef _WIN32 | |
+ if(path[0] == '\\') | |
+ path = "C:" + path; | |
+ #endif | |
+ | |
+ return path; | |
+ } | |
+ | |
+ void Run() | |
+ { | |
+ std::string path, result, removed; | |
+ | |
+ /* | |
+ Test fs::IsDirDelimiter | |
+ */ | |
+ UASSERT(fs::IsDirDelimiter('/') == true); | |
+ UASSERT(fs::IsDirDelimiter('A') == false); | |
+ UASSERT(fs::IsDirDelimiter(0) == false); | |
+ #ifdef _WIN32 | |
+ UASSERT(fs::IsDirDelimiter('\\') == true); | |
+ #else | |
+ UASSERT(fs::IsDirDelimiter('\\') == false); | |
+ #endif | |
+ | |
+ /* | |
+ Test fs::PathStartsWith | |
+ */ | |
+ { | |
+ const int numpaths = 12; | |
+ std::string paths[numpaths] = { | |
+ "", | |
+ p("/"), | |
+ p("/home/user/minetest"), | |
+ p("/home/user/minetest/bin"), | |
+ p("/home/user/.minetest"), | |
+ p("/tmp/dir/file"), | |
+ p("/tmp/file/"), | |
+ p("/tmP/file"), | |
+ p("/tmp"), | |
+ p("/tmp/dir"), | |
+ p("/home/user2/minetest/worlds"), | |
+ p("/home/user2/minetest/world"), | |
+ }; | |
+ /* | |
+ expected fs::PathStartsWith results | |
+ 0 = returns false | |
+ 1 = returns true | |
+ 2 = returns false on windows, false elsewhere | |
+ 3 = returns true on windows, true elsewhere | |
+ 4 = returns true if and only if | |
+ FILESYS_CASE_INSENSITIVE is true | |
+ */ | |
+ int expected_results[numpaths][numpaths] = { | |
+ {1,2,0,0,0,0,0,0,0,0,0,0}, | |
+ {1,1,0,0,0,0,0,0,0,0,0,0}, | |
+ {1,1,1,0,0,0,0,0,0,0,0,0}, | |
+ {1,1,1,1,0,0,0,0,0,0,0,0}, | |
+ {1,1,0,0,1,0,0,0,0,0,0,0}, | |
+ {1,1,0,0,0,1,0,0,1,1,0,0}, | |
+ {1,1,0,0,0,0,1,4,1,0,0,0}, | |
+ {1,1,0,0,0,0,4,1,4,0,0,0}, | |
+ {1,1,0,0,0,0,0,0,1,0,0,0}, | |
+ {1,1,0,0,0,0,0,0,1,1,0,0}, | |
+ {1,1,0,0,0,0,0,0,0,0,1,0}, | |
+ {1,1,0,0,0,0,0,0,0,0,0,1}, | |
+ }; | |
+ | |
+ for (int i = 0; i < numpaths; i++) | |
+ for (int j = 0; j < numpaths; j++){ | |
+ verbosestream<<"testing fs::PathStartsWith(\"" | |
+ <<paths[i]<<"\", \"" | |
+ <<paths[j]<<"\")"<<std::endl; | |
+ bool starts = fs::PathStartsWith(paths[i], paths[j]); | |
+ int expected = expected_results[i][j]; | |
+ if(expected == 0){ | |
+ UASSERT(starts == false); | |
+ } | |
+ else if(expected == 1){ | |
+ UASSERT(starts == true); | |
+ } | |
+ #ifdef _WIN32 | |
+ else if(expected == 2){ | |
+ UASSERT(starts == false); | |
+ } | |
+ else if(expected == 3){ | |
+ UASSERT(starts == true); | |
+ } | |
+ #else | |
+ else if(expected == 2){ | |
+ UASSERT(starts == true); | |
+ } | |
+ else if(expected == 3){ | |
+ UASSERT(starts == false); | |
+ } | |
+ #endif | |
+ else if(expected == 4){ | |
+ UASSERT(starts == (bool)FILESYS_CASE_INSENSITIVE); | |
+ } | |
+ } | |
+ } | |
+ | |
+ /* | |
+ Test fs::RemoveLastPathComponent | |
+ */ | |
+ UASSERT(fs::RemoveLastPathComponent("") == ""); | |
+ path = p("/home/user/minetest/bin/..//worlds/world1"); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 0); | |
+ UASSERT(result == path); | |
+ UASSERT(removed == ""); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 1); | |
+ UASSERT(result == p("/home/user/minetest/bin/..//worlds")); | |
+ UASSERT(removed == p("world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 2); | |
+ UASSERT(result == p("/home/user/minetest/bin/..")); | |
+ UASSERT(removed == p("worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 3); | |
+ UASSERT(result == p("/home/user/minetest/bin")); | |
+ UASSERT(removed == p("../worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 4); | |
+ UASSERT(result == p("/home/user/minetest")); | |
+ UASSERT(removed == p("bin/../worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 5); | |
+ UASSERT(result == p("/home/user")); | |
+ UASSERT(removed == p("minetest/bin/../worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 6); | |
+ UASSERT(result == p("/home")); | |
+ UASSERT(removed == p("user/minetest/bin/../worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 7); | |
+ #ifdef _WIN32 | |
+ UASSERT(result == "C:"); | |
+ #else | |
+ UASSERT(result == ""); | |
+ #endif | |
+ UASSERT(removed == p("home/user/minetest/bin/../worlds/world1")); | |
+ | |
+ /* | |
+ Now repeat the test with a trailing delimiter | |
+ */ | |
+ path = p("/home/user/minetest/bin/..//worlds/world1/"); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 0); | |
+ UASSERT(result == path); | |
+ UASSERT(removed == ""); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 1); | |
+ UASSERT(result == p("/home/user/minetest/bin/..//worlds")); | |
+ UASSERT(removed == p("world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 2); | |
+ UASSERT(result == p("/home/user/minetest/bin/..")); | |
+ UASSERT(removed == p("worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 3); | |
+ UASSERT(result == p("/home/user/minetest/bin")); | |
+ UASSERT(removed == p("../worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 4); | |
+ UASSERT(result == p("/home/user/minetest")); | |
+ UASSERT(removed == p("bin/../worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 5); | |
+ UASSERT(result == p("/home/user")); | |
+ UASSERT(removed == p("minetest/bin/../worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 6); | |
+ UASSERT(result == p("/home")); | |
+ UASSERT(removed == p("user/minetest/bin/../worlds/world1")); | |
+ result = fs::RemoveLastPathComponent(path, &removed, 7); | |
+ #ifdef _WIN32 | |
+ UASSERT(result == "C:"); | |
+ #else | |
+ verbosestream<<"\""<<result<<"\""<<std::endl; | |
+ UASSERT(result == ""); | |
+ #endif | |
+ UASSERT(removed == p("home/user/minetest/bin/../worlds/world1")); | |
+ | |
+ /* | |
+ Test fs::RemoveRelativePathComponent | |
+ */ | |
+ path = p("/home/user/minetest/bin"); | |
+ result = fs::RemoveRelativePathComponents(path); | |
+ UASSERT(result == path); | |
+ path = p("/home/user/minetest/bin/../worlds/world1"); | |
+ result = fs::RemoveRelativePathComponents(path); | |
+ UASSERT(result == p("/home/user/minetest/worlds/world1")); | |
+ path = p("/home/user/minetest/bin/../worlds/world1/"); | |
+ result = fs::RemoveRelativePathComponents(path); | |
+ UASSERT(result == p("/home/user/minetest/worlds/world1")); | |
+ path = p("."); | |
+ result = fs::RemoveRelativePathComponents(path); | |
+ UASSERT(result == ""); | |
+ path = p("./subdir/../.."); | |
+ result = fs::RemoveRelativePathComponents(path); | |
+ UASSERT(result == ""); | |
+ path = p("/a/b/c/.././../d/../e/f/g/../h/i/j/../../../.."); | |
+ result = fs::RemoveRelativePathComponents(path); | |
+ UASSERT(result == p("/a/e")); | |
+ } | |
+}; | |
+ | |
struct TestSettings: public TestBase | |
{ | |
void Run() | |
@@ -1755,6 +1963,7 @@ void run_tests() | |
infostream<<"run_tests() started"<<std::endl; | |
TEST(TestUtilities); | |
+ TEST(TestPath); | |
TEST(TestSettings); | |
TEST(TestCompress); | |
TEST(TestSerialization); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment