From 0d19be219660d2aba87ebe01ae15191f56ba84d7 Mon Sep 17 00:00:00 2001 From: Thomas Geymayer Date: Tue, 28 Jan 2014 00:32:08 +0100 Subject: [PATCH] SGPath: generalize retrieving standard locations (and add more) --- simgear/misc/path_test.cxx | 14 +++ simgear/misc/sg_path.cxx | 190 +++++++++++++++++++++++-------------- simgear/misc/sg_path.hxx | 13 +++ 3 files changed, 144 insertions(+), 73 deletions(-) diff --git a/simgear/misc/path_test.cxx b/simgear/misc/path_test.cxx index a062a723..3eeccbeb 100644 --- a/simgear/misc/path_test.cxx +++ b/simgear/misc/path_test.cxx @@ -41,6 +41,20 @@ void test_dir() } cout << temp.path().modTime() << endl; + + std::cout << "Standard Locations:" + << "\n - Home: " << SGPath::standardLocation(SGPath::HOME) + << "\n - Desktop: " << SGPath::standardLocation(SGPath::DESKTOP) + << "\n - Downloads: " << SGPath::standardLocation(SGPath::DOWNLOADS) + << "\n - Documents: " << SGPath::standardLocation(SGPath::DOCUMENTS) + << "\n - Pictures: " << SGPath::standardLocation(SGPath::PICTURES) + << std::endl; + + VERIFY( !SGPath::standardLocation(SGPath::HOME ).isNull() ); + VERIFY( !SGPath::standardLocation(SGPath::DESKTOP ).isNull() ); + VERIFY( !SGPath::standardLocation(SGPath::DOWNLOADS).isNull() ); + VERIFY( !SGPath::standardLocation(SGPath::DOCUMENTS).isNull() ); + VERIFY( !SGPath::standardLocation(SGPath::PICTURES ).isNull() ); } SGPath::Permissions validateNone(const SGPath&) diff --git a/simgear/misc/sg_path.cxx b/simgear/misc/sg_path.cxx index adc4df5b..e38c67ee 100644 --- a/simgear/misc/sg_path.cxx +++ b/simgear/misc/sg_path.cxx @@ -56,10 +56,9 @@ static const char sgSearchPathSep = ':'; #endif #ifdef _WIN32 - #include // for CSIDL -static SGPath pathForCSIDL(int csidl, const SGPath::PermissonChecker& checker) +static SGPath pathForCSIDL(int csidl, const SGPath& def) { typedef BOOL (WINAPI*GetSpecialFolderPath)(HWND, LPSTR, int, BOOL); static GetSpecialFolderPath SHGetSpecialFolderPath = NULL; @@ -71,17 +70,75 @@ static SGPath pathForCSIDL(int csidl, const SGPath::PermissonChecker& checker) } if (!SHGetSpecialFolderPath){ - return SGPath(); + return def; } char path[MAX_PATH]; if (SHGetSpecialFolderPath(0, path, csidl, false)) { - return SGPath(path, checker); + return SGPath(path, def.getPermissonChecker()); } - return SGPath(); + return def; } +#elif __APPLE__ +#include + +//------------------------------------------------------------------------------ +static SGPath appleSpecialFolder(OSType type, const SGPath& def) +{ + FSRef ref; + OSErr err = FSFindFolder(kUserDomain, kDesktopFolderType, false, &ref); + if( err ) + return def; + + unsigned char path[1024]; + if( FSRefMakePath(&ref, path, 1024) != noErr ) + return def; + return SGPath((const char*) path, def.getPermissonChecker()); +} +#else +static SGPath getXDGDir( const std::string& name, + const SGPath& def, + const std::string& fallback ) +{ + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + + // $XDG_CONFIG_HOME defines the base directory relative to which user specific + // configuration files should be stored. If $XDG_CONFIG_HOME is either not set + // or empty, a default equal to $HOME/.config should be used. + const SGPath user_dirs = SGPath::fromEnv( "XDG_CONFIG_HOME", + SGPath::home() / ".config") + / "user-dirs.dirs"; + + // Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped + // homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an absolute + // path. No other format is supported. + const std::string XDG_ID = "XDG_" + name + "_DIR=\""; + + std::ifstream user_dirs_file( user_dirs.c_str() ); + std::string line; + while( std::getline(user_dirs_file, line).good() ) + { + if( !starts_with(line, XDG_ID) || *line.rbegin() != '"' ) + continue; + + // Extract dir from XDG__DIR="" + line = line.substr(XDG_ID.length(), line.length() - XDG_ID.length() - 1 ); + + const std::string HOME = "$HOME"; + if( starts_with(line, HOME) ) + return SGPath::home(def) + / simgear::strutils::unescape(line.substr(HOME.length())); + + return SGPath(line, def.getPermissonChecker()); + } + + if( def.isNull() ) + return SGPath::home(def) / fallback; + + return def; +} #endif // For windows, replace "\" by "/". @@ -334,6 +391,7 @@ string SGPath::complete_lower_extension() const } } +//------------------------------------------------------------------------------ void SGPath::validate() const { if (_cached && _cacheEnabled) { @@ -378,6 +436,7 @@ void SGPath::validate() const _cached = true; } +//------------------------------------------------------------------------------ void SGPath::checkAccess() const { if( _rwCached && _cacheEnabled ) @@ -647,6 +706,56 @@ bool SGPath::rename(const SGPath& newName) return true; } +//------------------------------------------------------------------------------ +SGPath SGPath::standardLocation(StandardLocation type, const SGPath& def) +{ + switch(type) + { + case HOME: + return home(def); +#ifdef _WIN32 + case DESKTOP: + return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def); + case DOWNLOADS: + // TODO use KnownFolders + // http://msdn.microsoft.com/en-us/library/bb776911%28v=vs.85%29.aspx + if( !def.isNull() ) + return def; + + return pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def); + case DOCUMENTS: + return pathForCSIDL(CSIDL_MYDOCUMENTS, def); + case PICTURES: + return pathForCSIDL(CSIDL_MYPICTURES, def); +#elif __APPLE__ + case DOWNLOADS: + if( !def.isNull() ) + return def; + // There is no special downloads folder -> just use the desktop + case DESKTOP: + return appleSpecialFolder(kDesktopFolderType, def); + case DOCUMENTS: + return appleSpecialFolder(kDocumentsFolderType, def); + case PICTURES: + return appleSpecialFolder(kPictureDocumentsFolderType, def); +#else + case DESKTOP: + return getXDGDir("DESKTOP", def, "Desktop"); + case DOWNLOADS: + return getXDGDir("DOWNLOADS", def, "Downloads"); + case DOCUMENTS: + return getXDGDir("DOCUMENTS", def, "Documents"); + case PICTURES: + return getXDGDir("PICTURES", def, "Pictures"); +#endif + default: + SG_LOG( SG_GENERAL, + SG_WARN, + "SGPath::standardLocation() unhandled type: " << type ); + return def; + } +} + //------------------------------------------------------------------------------ SGPath SGPath::fromEnv(const char* name, const SGPath& def) { @@ -671,84 +780,19 @@ SGPath SGPath::home(const SGPath& def) } #endif -#ifdef _WIN32 //------------------------------------------------------------------------------ SGPath SGPath::desktop(const SGPath& def) { - SGPath r = pathForCSIDL(CSIDL_DESKTOPDIRECTORY, def._permission_checker); - if (!r.isNull()) { - return r; - } - - SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::desktop() failed, bad" ); - return def; + return standardLocation(DESKTOP, def); } +//------------------------------------------------------------------------------ SGPath SGPath::documents(const SGPath& def) { - SGPath r = pathForCSIDL(CSIDL_MYDOCUMENTS, def._permission_checker); - if (!r.isNull()) { - return r; - } - - SG_LOG(SG_GENERAL, SG_ALERT, "SGPath::documents() failed, bad" ); - return def; + return standardLocation(DOCUMENTS, def); } -#elif __APPLE__ -#include //------------------------------------------------------------------------------ -SGPath SGPath::desktop(const SGPath& def) -{ - FSRef ref; - OSErr err = FSFindFolder(kUserDomain, kDesktopFolderType, false, &ref); - if (err) - return def; - - unsigned char path[1024]; - if (FSRefMakePath(&ref, path, 1024) != noErr) - return def; - - return SGPath((const char*) path, def._permission_checker); -} -#else -//------------------------------------------------------------------------------ -SGPath SGPath::desktop(const SGPath& def) -{ - // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - - // $XDG_CONFIG_HOME defines the base directory relative to which user specific - // configuration files should be stored. If $XDG_CONFIG_HOME is either not set - // or empty, a default equal to $HOME/.config should be used. - const SGPath user_dirs = fromEnv("XDG_CONFIG_HOME", home() / ".config") - / "user-dirs.dirs"; - - // Format is XDG_xxx_DIR="$HOME/yyy", where yyy is a shell-escaped - // homedir-relative path, or XDG_xxx_DIR="/yyy", where /yyy is an absolute - // path. No other format is supported. - const std::string DESKTOP = "XDG_DESKTOP_DIR=\""; - - std::ifstream user_dirs_file( user_dirs.c_str() ); - std::string line; - while( std::getline(user_dirs_file, line).good() ) - { - if( !starts_with(line, DESKTOP) || *line.rbegin() != '"' ) - continue; - - // Extract dir from XDG_DESKTOP_DIR="" - line = line.substr(DESKTOP.length(), line.length() - DESKTOP.length() - 1 ); - - const std::string HOME = "$HOME"; - if( starts_with(line, HOME) ) - return home(def) / simgear::strutils::unescape(line.substr(HOME.length())); - - return SGPath(line, def._permission_checker); - } - - return home(def) / "Desktop"; -} -#endif - std::string SGPath::realpath() const { #if (defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED <= 1050) diff --git a/simgear/misc/sg_path.hxx b/simgear/misc/sg_path.hxx index 2cade320..377a0b01 100644 --- a/simgear/misc/sg_path.hxx +++ b/simgear/misc/sg_path.hxx @@ -257,6 +257,18 @@ public: */ bool rename(const SGPath& newName); + enum StandardLocation + { + HOME, + DESKTOP, + DOWNLOADS, + DOCUMENTS, + PICTURES + }; + + static SGPath standardLocation( StandardLocation type, + const SGPath& def = SGPath() ); + /** * Get a path stored in the environment variable with the given \a name. * @@ -280,6 +292,7 @@ public: * Get path to the user's documents directory */ static SGPath documents(const SGPath& def = SGPath()); + private: void fix(); -- 2.39.5