fs: Add utility functions for the new file dialog
These include an alternate mode of normalize_path() that enforces the platform's preferred path delimiter (i.e. backslash on Windows) on the output, and a function to detect whether a path refers to a root directory. Unfortunately, the last bit requires introducing a new link-time dependency on Windows, against a system library. It's guaranteed to be always there but it seems kind of a waste. The alternative would be to hand-parse the string but that seems even more of a waste. And no, Boost.Filesystem can't do this in a straightforward fashion right now.
This commit is contained in:
parent
e38dd8ff16
commit
ea9d077b89
7 changed files with 102 additions and 4 deletions
|
@ -531,7 +531,7 @@ for env in [test_env, client_env, env]:
|
|||
env[d] = os.path.join(env["prefix"], env[d])
|
||||
|
||||
if env["PLATFORM"] == 'win32':
|
||||
env.Append(LIBS = ["wsock32", "iconv", "z"], CCFLAGS = ["-mthreads"], LINKFLAGS = ["-mthreads"], CPPDEFINES = ["_WIN32_WINNT=0x0501"])
|
||||
env.Append(LIBS = ["wsock32", "iconv", "z", "shlwapi"], CCFLAGS = ["-mthreads"], LINKFLAGS = ["-mthreads"], CPPDEFINES = ["_WIN32_WINNT=0x0501"])
|
||||
|
||||
if env["PLATFORM"] == 'darwin': # Mac OS X
|
||||
env.Append(FRAMEWORKS = "Carbon") # Carbon GUI
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
<Add library="SDL2_ttf" />
|
||||
<Add library="freetype" />
|
||||
<Add library="ws2_32" />
|
||||
<Add library="shlwapi" />
|
||||
<Add library="pango-1.0" />
|
||||
<Add library="pangocairo-1.0" />
|
||||
<Add library="cairo" />
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
<Add library="SDL2" />
|
||||
<Add library="ws2_32" />
|
||||
<Add library="wsock32" />
|
||||
<Add library="shlwapi" />
|
||||
<Add library="libboost_iostreams-mgw51-mt-1_61.a" />
|
||||
<Add library="libboost_bzip2-mgw51-mt-1_61.a" />
|
||||
<Add library="libboost_filesystem-mgw51-mt-1_61.a" />
|
||||
|
|
|
@ -62,6 +62,7 @@ if(MSVC)
|
|||
${sdl-lib}
|
||||
${sdlmain-lib}
|
||||
ws2_32.lib
|
||||
shlwapi.lib
|
||||
)
|
||||
else(MSVC)
|
||||
set(common-external-libs
|
||||
|
|
|
@ -184,8 +184,26 @@ std::string directory_name(const std::string& file);
|
|||
|
||||
/**
|
||||
* Returns the absolute path of a file.
|
||||
*
|
||||
* @param path Original path.
|
||||
* @param normalize_separators Whether to substitute path separators with the
|
||||
* platform's preferred format.
|
||||
*/
|
||||
std::string normalize_path(const std::string &path);
|
||||
std::string normalize_path(const std::string& path,
|
||||
bool normalize_separators = false);
|
||||
|
||||
/**
|
||||
* Returns whether the path is the root of the file hierarchy.
|
||||
*
|
||||
* @note This function is unreliable for paths that do not exist -- it will
|
||||
* always return @a false for those.
|
||||
*/
|
||||
bool is_root(const std::string& path);
|
||||
|
||||
/**
|
||||
* Returns whether the path seems to be relative.
|
||||
*/
|
||||
bool is_relative(const std::string& path);
|
||||
|
||||
/**
|
||||
* Returns whether @a c is a path separator.
|
||||
|
@ -195,6 +213,11 @@ std::string normalize_path(const std::string &path);
|
|||
*/
|
||||
bool is_path_sep(char c);
|
||||
|
||||
/**
|
||||
* Returns the standard path separator for the current platform.
|
||||
*/
|
||||
char path_separator();
|
||||
|
||||
/**
|
||||
* The paths manager is responsible for recording the various paths
|
||||
* that binary files may be located at.
|
||||
|
|
|
@ -39,6 +39,7 @@ using boost::uintmax_t;
|
|||
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
#endif /* !_WIN32 */
|
||||
|
||||
#include "config.hpp"
|
||||
|
@ -918,18 +919,67 @@ std::string directory_name(const std::string& file)
|
|||
{
|
||||
return path(file).parent_path().string();
|
||||
}
|
||||
|
||||
bool is_path_sep(char c)
|
||||
{
|
||||
static const path sep = path("/").make_preferred();
|
||||
const std::string s = std::string(1, c);
|
||||
return sep == path(s).make_preferred();
|
||||
}
|
||||
std::string normalize_path(const std::string &fpath)
|
||||
|
||||
char path_separator()
|
||||
{
|
||||
if (fpath.empty()) {
|
||||
return path::preferred_separator;
|
||||
}
|
||||
|
||||
bool is_root(const std::string& path)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
error_code ec;
|
||||
const bfs::path& p = bfs::canonical(path, ec);
|
||||
return ec ? false : !p.has_parent_path();
|
||||
#else
|
||||
//
|
||||
// Boost.Filesystem is completely unreliable when it comes to detecting
|
||||
// whether a path refers to a drive's root directory on Windows, so we are
|
||||
// forced to take an alternative approach here. Instead of hand-parsing
|
||||
// strings we'll just call a graphical shell service.
|
||||
//
|
||||
// There are several poorly-documented ways to refer to a drive in Windows by
|
||||
// escaping the filesystem namespace using \\.\, \\?\, and \??\. We're just
|
||||
// going to ignore those here, which may yield unexpected results in places
|
||||
// such as the file dialog. This function really shouldn't be used for
|
||||
// security validation anyway, and there are virtually infinite ways to name
|
||||
// a drive's root using the NT object namespace so it's pretty pointless to
|
||||
// try to catch those there.
|
||||
//
|
||||
// (And no, shlwapi.dll's PathIsRoot() doesn't recognize \\.\C:\, \\?\C:\, or
|
||||
// \??\C:\ as roots either.)
|
||||
//
|
||||
// More generally, do NOT use this code in security-sensitive applications.
|
||||
//
|
||||
// See also: <https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html>
|
||||
//
|
||||
const std::wstring& wpath = bfs::path{path}.make_preferred().wstring();
|
||||
return PathIsRootW(wpath.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_relative(const std::string& path)
|
||||
{
|
||||
return bfs::path{path}.is_relative();
|
||||
}
|
||||
|
||||
std::string normalize_path(const std::string& fpath, bool normalize_separators)
|
||||
{
|
||||
if(fpath.empty()) {
|
||||
return fpath;
|
||||
}
|
||||
|
||||
if(normalize_separators) {
|
||||
return bfs::absolute(fpath).make_preferred().string();
|
||||
}
|
||||
|
||||
return bfs::absolute(fpath).string();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,28 @@ BOOST_AUTO_TEST_CASE( test_fs_game_path_reverse_engineering )
|
|||
|
||||
BOOST_AUTO_TEST_CASE( test_fs_base )
|
||||
{
|
||||
BOOST_CHECK( is_root("/") );
|
||||
BOOST_CHECK( is_root("////") );
|
||||
BOOST_CHECK( is_root("/../") );
|
||||
BOOST_CHECK( is_root("/../.././") );
|
||||
BOOST_CHECK( is_root("/.././../.") );
|
||||
BOOST_CHECK( is_root("/.") );
|
||||
|
||||
BOOST_CHECK( is_root("/bin/..") );
|
||||
BOOST_CHECK( is_root("/bin/../bin/../.") );
|
||||
|
||||
BOOST_CHECK( !is_root("/bin") );
|
||||
BOOST_CHECK( !is_root("/bin/../bin/.") );
|
||||
|
||||
BOOST_CHECK( is_relative(".") );
|
||||
BOOST_CHECK( is_relative("..") );
|
||||
BOOST_CHECK( is_relative("../foo") );
|
||||
BOOST_CHECK( is_relative("foo") );
|
||||
BOOST_CHECK( !is_relative("/./../foo/..") );
|
||||
BOOST_CHECK( !is_relative("/foo/..") );
|
||||
BOOST_CHECK( !is_relative("/foo") );
|
||||
BOOST_CHECK( !is_relative("///foo") );
|
||||
|
||||
BOOST_CHECK( is_directory("/") );
|
||||
BOOST_CHECK( is_directory("/.") );
|
||||
BOOST_CHECK( is_directory("/./././.") );
|
||||
|
|
Loading…
Add table
Reference in a new issue