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