diff options
Diffstat (limited to 'shared/resolve_path.cpp')
-rw-r--r-- | shared/resolve_path.cpp | 262 |
1 files changed, 234 insertions, 28 deletions
diff --git a/shared/resolve_path.cpp b/shared/resolve_path.cpp index 2a9cb7e6..f22b4505 100644 --- a/shared/resolve_path.cpp +++ b/shared/resolve_path.cpp @@ -3,9 +3,11 @@ #include <wx/datetime.h> #include "string_conv.h" #include "loki/ScopeGuard.h" +#include <map> #ifdef FFS_WIN #include "dll_loader.h" +#include <Shlobj.h> #include <wx/msw/wrapwin.h> //includes "windows.h" #include "long_path_prefix.h" #ifdef _MSC_VER @@ -26,6 +28,17 @@ namespace #ifdef FFS_WIN Zstring resolveBrokenNetworkMap(const Zstring& dirname) //circumvent issue with disconnected network maps that could be activated by a simple explorer double click { + /* + ATTENTION: it is not safe to call ::WNetGetConnection() for every network share: + + network type |::WNetGetConnection rv | lpRemoteName | existing UNC path + -----------------------------|-------------------------|---------------------------------|---------------- + inactive local network share | ERROR_CONNECTION_UNAVAIL| \\192.168.1.27\new2 | YES + WebDrive | NO_ERROR | \\Webdrive-ZenJu\GNU | NO + Box.net (WebDav) | NO_ERROR | \\www.box.net\DavWWWRoot\dav | YES + NetDrive | ERROR_NOT_CONNECTED | <empty> | NO + */ + if (dirname.size() >= 2 && iswalpha(dirname[0]) && dirname[1] == L':') { Zstring driveLetter(dirname.c_str(), 2); //e.g.: "Q:" @@ -37,13 +50,12 @@ Zstring resolveBrokenNetworkMap(const Zstring& dirname) //circumvent issue with DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form "<driveletter>:" &remoteNameBuffer[0], //__out LPTSTR lpRemoteName, &bufferSize); //__inout LPDWORD lpnLength - if (rv == NO_ERROR || - rv == ERROR_CONNECTION_UNAVAIL) //remoteNameBuffer will be filled nevertheless! - { - Zstring networkShare = &remoteNameBuffer[0]; - if (!networkShare.empty()) - return networkShare + (dirname.c_str() + 2); //replace "Q:\subdir" by "\\server\share\subdir" - } + if (rv == ERROR_CONNECTION_UNAVAIL) //remoteNameBuffer will be filled nevertheless! + { + Zstring networkShare = &remoteNameBuffer[0]; + if (!networkShare.empty()) + return networkShare + (dirname.c_str() + 2); //replace "Q:\subdir" by "\\server\share\subdir" + } } } return dirname; @@ -80,6 +92,96 @@ Zstring resolveRelativePath(const Zstring& relativeName) //additional: resolves #endif +#ifdef FFS_WIN +class CsidlConstants +{ +public: + typedef std::map<Zstring, Zstring, LessFilename> CsidlToDirMap; //case-insensitive comparison + + static const CsidlToDirMap& get() + { + static CsidlConstants inst; + return inst.csidlToDir; + } + +private: + CsidlConstants() + { + auto addCsidl = [&](int csidl, const Zstring& paramName) + { + wchar_t buffer[MAX_PATH]; + if (SUCCEEDED(::SHGetFolderPath(NULL, //__in HWND hwndOwner, + csidl | CSIDL_FLAG_DONT_VERIFY, //__in int nFolder, + NULL, //__in HANDLE hToken, + 0 /* == SHGFP_TYPE_CURRENT*/, //__in DWORD dwFlags, + buffer))) //__out LPTSTR pszPath + { + Zstring dirname = buffer; + if (!dirname.empty()) + csidlToDir.insert(std::make_pair(paramName, dirname)); + } + }; + + addCsidl(CSIDL_DESKTOPDIRECTORY, L"csidl_Desktop"); // C:\Users\username\Desktop + addCsidl(CSIDL_COMMON_DESKTOPDIRECTORY, L"csidl_PublicDesktop"); // C:\Users\All Users\Desktop + + addCsidl(CSIDL_MYMUSIC, L"csidl_MyMusic"); // C:\Users\username\My Documents\My Music + addCsidl(CSIDL_COMMON_MUSIC, L"csidl_PublicMusic"); // C:\Users\All Users\Documents\My Music + + addCsidl(CSIDL_MYPICTURES, L"csidl_MyPictures"); // C:\Users\username\My Documents\My Pictures + addCsidl(CSIDL_COMMON_PICTURES, L"csidl_PublicPictures"); // C:\Users\All Users\Documents\My Pictures + + addCsidl(CSIDL_MYVIDEO, L"csidl_MyVideo"); // C:\Users\username\My Documents\My Videos + addCsidl(CSIDL_COMMON_VIDEO, L"csidl_PublicVideo"); // C:\Users\All Users\Documents\My Videos + + addCsidl(CSIDL_PERSONAL, L"csidl_MyDocuments"); // C:\Users\username\My Documents + addCsidl(CSIDL_COMMON_DOCUMENTS, L"csidl_PublicDocuments"); // C:\Users\All Users\Documents + + addCsidl(CSIDL_STARTMENU, L"csidl_StartMenu"); // C:\Users\username\Start Menu + addCsidl(CSIDL_COMMON_STARTMENU, L"csidl_PublicStartMenu"); // C:\Users\All Users\Start Menu + + addCsidl(CSIDL_FAVORITES, L"csidl_Favorites"); // C:\Users\username\Favorites + addCsidl(CSIDL_COMMON_FAVORITES, L"csidl_PublicFavorites"); // C:\Users\All Users\Favoriten + + addCsidl(CSIDL_TEMPLATES, L"csidl_Templates"); // C:\Users\username\Templates + addCsidl(CSIDL_COMMON_TEMPLATES, L"csidl_PublicTemplates"); // C:\Users\All Users\Templates + + addCsidl(CSIDL_RESOURCES, L"csidl_Resources"); // C:\Windows\Resources + + //CSIDL_APPDATA covered by %AppData% + //CSIDL_LOCAL_APPDATA covered by %LocalAppData% + //CSIDL_COMMON_APPDATA covered by %ProgramData% + + //CSIDL_PROFILE covered by %UserProfile% + } + + CsidlConstants(const CsidlConstants&); + CsidlConstants& operator=(const CsidlConstants&); + + CsidlToDirMap csidlToDir; +}; +#endif + + +wxString getEnvValue(const wxString& envName) //return empty on error +{ + //try to apply environment variables + wxString envValue; + if (wxGetEnv(envName, &envValue)) + { + //some postprocessing: + trim(envValue); //remove leading, trailing blanks + + //remove leading, trailing double-quotes + if (startsWith(envValue, L"\"") && + endsWith(envValue, L"\"") && + envValue.length() >= 2) + envValue = wxString(envValue.c_str() + 1, envValue.length() - 2); + } + return envValue; +} + + bool replaceMacro(wxString& macro) //macro without %-characters, return true if replaced successfully { if (macro.IsEmpty()) @@ -117,22 +219,27 @@ bool replaceMacro(wxString& macro) //macro without %-characters, return true if if (processPhrase(L"sec" , L"%S")) return true; //try to apply environment variables - wxString envValue; - if (wxGetEnv(macro, &envValue)) { - macro = envValue; - - //some postprocessing: - macro.Trim(true); //remove leading, trailing blanks - macro.Trim(false); // + wxString envValue = getEnvValue(macro); + if (!envValue.empty()) + { + macro = envValue; + return true; + } + } - //remove leading, trailing double-quotes - if (macro.StartsWith(wxT("\"")) && - macro.EndsWith(wxT("\"")) && - macro.length() >= 2) - macro = wxString(macro.c_str() + 1, macro.length() - 2); - return true; +#ifdef FFS_WIN + //try to resolve CSIDL values + { + auto csidlMap = CsidlConstants::get(); + auto iter = csidlMap.find(toZ(macro)); + if (iter != csidlMap.end()) + { + macro = toWx(iter->second); + return true; + } } +#endif return false; } @@ -190,7 +297,7 @@ private: #endif -Zstring getVolumePath(const Zstring& volumeName) //empty string on error +Zstring volumenNameToPath(const Zstring& volumeName) //return empty string on error { #ifdef FFS_WIN std::vector<wchar_t> volGuid(10000); @@ -198,8 +305,7 @@ Zstring getVolumePath(const Zstring& volumeName) //empty string on error HANDLE hVol = ::FindFirstVolume(&volGuid[0], static_cast<DWORD>(volGuid.size())); if (hVol != INVALID_HANDLE_VALUE) { - Loki::ScopeGuard dummy = Loki::MakeGuard(::FindVolumeClose, hVol); - (void)dummy; + LOKI_ON_BLOCK_EXIT2(::FindVolumeClose(hVol)); do { @@ -222,10 +328,8 @@ Zstring getVolumePath(const Zstring& volumeName) //empty string on error DWORD cchBufferLength, PDWORD lpcchReturnLength); - static const GetVolumePathNamesForVolumeNameWFunc getVolumePathNamesForVolumeName = - util::getDllFun<GetVolumePathNamesForVolumeNameWFunc>(L"kernel32.dll", "GetVolumePathNamesForVolumeNameW"); - - if (getVolumePathNamesForVolumeName != NULL) + const util::DllFun<GetVolumePathNamesForVolumeNameWFunc> getVolumePathNamesForVolumeName(L"kernel32.dll", "GetVolumePathNamesForVolumeNameW"); + if (getVolumePathNamesForVolumeName) { std::vector<wchar_t> volPath(10000); @@ -261,6 +365,28 @@ Zstring getVolumePath(const Zstring& volumeName) //empty string on error } +#ifdef FFS_WIN +Zstring volumePathToName(const Zstring& volumePath) //return empty string on error +{ + const DWORD bufferSize = MAX_PATH + 1; + std::vector<wchar_t> volName(bufferSize); + + if (::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName, + &volName[0], //__out LPTSTR lpVolumeNameBuffer, + bufferSize, //__in DWORD nVolumeNameSize, + NULL, //__out_opt LPDWORD lpVolumeSerialNumber, + NULL, //__out_opt LPDWORD lpMaximumComponentLength, + NULL, //__out_opt LPDWORD lpFileSystemFlags, + NULL, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize + { + return &volName[0]; + } + return Zstring(); +} +#endif + + void expandVolumeName(Zstring& text) // [volname]:\folder [volname]\folder [volname]folder -> C:\folder { Zstring before; @@ -287,7 +413,7 @@ void expandVolumeName(Zstring& text) // [volname]:\folder [volname]\folde if (volname.empty()) return; - Zstring volPath = getVolumePath(volname); //return empty string on error + Zstring volPath = volumenNameToPath(volname); //return empty string on error if (volPath.empty()) return; @@ -299,6 +425,86 @@ void expandVolumeName(Zstring& text) // [volname]:\folder [volname]\folde } +#ifdef FFS_WIN +std::vector<Zstring> zen::getDirectoryAliases(Zstring dirname) +{ + trim(dirname, true, false); + + std::vector<Zstring> output; + + if (dirname.empty()) + return output; + + //1. replace volume path by volume name: c:\dirname -> [SYSTEM]\dirname + if (dirname.size() >= 3 && + std::iswalpha(dirname[0]) && + dirname[1] == L':' && + dirname[2] == L'\\') + { + Zstring volname = volumePathToName(Zstring(dirname.c_str(), 3)); + if (!volname.empty()) + output.push_back(L"[" + volname + L"]" + Zstring(dirname.c_str() + 2)); + } + + //2. replace volume name by volume path: [SYSTEM]\dirname -> c:\dirname + { + Zstring testVolname = dirname; + expandVolumeName(testVolname); + if (testVolname != dirname) + output.push_back(testVolname); + } + + //3. environment variables: C:\Users\username -> %USERPROFILE% + { + std::map<Zstring, Zstring> envToDir; + + //get list of useful variables + auto addEnvVar = [&](const wxString& envName) + { + wxString envVal = getEnvValue(envName); //return empty on error + if (!envVal.empty()) + envToDir.insert(std::make_pair(toZ(envName), toZ(envVal))); + }; + addEnvVar(L"AllUsersProfile"); // C:\ProgramData + addEnvVar(L"AppData"); // C:\Users\username\AppData\Roaming + addEnvVar(L"LocalAppData"); // C:\Users\username\AppData\Local + addEnvVar(L"ProgramData"); // C:\ProgramData + addEnvVar(L"ProgramFiles"); // C:\Program Files + addEnvVar(L"ProgramFiles(x86)");// C:\Program Files (x86) + addEnvVar(L"Public"); // C:\Users\Public + addEnvVar(L"UserProfile"); // C:\Users\username + addEnvVar(L"WinDir"); // C:\Windows + addEnvVar(L"Temp"); // C:\Windows\Temp + + //add CSIDL values: http://msdn.microsoft.com/en-us/library/bb762494(v=vs.85).aspx + auto csidlMap = CsidlConstants::get(); + envToDir.insert(csidlMap.begin(), csidlMap.end()); + + Zstring tmp = dirname; + ::makeUpper(tmp); + std::for_each(envToDir.begin(), envToDir.end(), + [&](const std::pair<Zstring, Zstring>& entry) + { + Zstring tmp2 = entry.second; //case-insensitive "startsWith()" + ::makeUpper(tmp2); // + if (startsWith(tmp, tmp2)) + output.push_back(L"%" + entry.first + L"%" + (dirname.c_str() + tmp2.size())); + }); + } + + //5. replace (all) macros: //%USERPROFILE% -> C:\Users\username + { + wxString testMacros = toWx(dirname); + expandMacros(testMacros); + if (toZ(testMacros) != dirname) + output.push_back(toZ(testMacros)); + } + + return output; +} +#endif + + Zstring zen::getFormattedDirectoryName(const Zstring& dirname) { //Formatting is needed since functions expect the directory to end with '\' to be able to split the relative names. |