Skip to content

Instantly share code, notes, and snippets.

@kahrl
Created May 31, 2013 21:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kahrl/5688237 to your computer and use it in GitHub Desktop.
Save kahrl/5688237 to your computer and use it in GitHub Desktop.
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