diff --git a/Common/PathUtil.cpp b/Common/PathUtil.cpp new file mode 100644 index 00000000..139b0da1 --- /dev/null +++ b/Common/PathUtil.cpp @@ -0,0 +1,150 @@ +/* + Copyright (C) 2013 Rainmeter Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "PathUtil.h" +#include + +namespace PathUtil { + +bool IsSeparator(WCHAR ch) +{ + return ch == L'\\' || ch == L'/'; +} + +bool IsDotOrDotDot(const WCHAR* path) +{ + return path[0] == L'.' && (path[1] == L'\0' || (path[1] == L'.' && path[2] == L'\0')); +} + +bool IsUNC(const std::wstring& path) +{ + return path.length() >= 2 && IsSeparator(path[0]) && IsSeparator(path[1]); +} + +bool IsAbsolute(const std::wstring& path) +{ + return (path.find(L":\\") != std::wstring::npos || + path.find(L":/") != std::wstring::npos || + IsUNC(path)); +} + +void AppendBacklashIfMissing(std::wstring& path) +{ + if (!path.empty() && !IsSeparator(path[path.length() - 1])) + { + path += L'\\'; + } +} + +std::wstring GetFolderFromFilePath(const std::wstring& filePath) +{ + std::wstring::size_type pos = filePath.find_last_of(L"\\/"); + if (pos != std::wstring::npos) + { + return filePath.substr(0, pos + 1); + } + return L".\\"; +} + +/* +** Extracts volume path from program path. +** E.g.: +** "C:\path\" to "C:" +** "\\server\share\" to "\\server\share" +** "\\server\C:\path\" to "\\server\C:" +*/ +std::wstring GetVolume(const std::wstring& path) +{ + std::wstring::size_type pos; + if ((pos = path.find_first_of(L':')) != std::wstring::npos) + { + return path.substr(0, pos + 1); + } + else if (IsUNC(path)) + { + if ((pos = path.find_first_of(L"\\/", 2)) != std::wstring::npos) + { + std::wstring::size_type pos2; + if ((pos2 = path.find_first_of(L"\\/", pos + 1)) != std::wstring::npos || + pos != (path.length() - 1)) + { + pos = pos2; + } + } + + return path.substr(0, pos); + } + + return std::wstring(); +} + +void ExpandEnvironmentVariables(std::wstring& path) +{ + std::wstring::size_type pos; + if ((pos = path.find(L'%')) != std::wstring::npos && + path.find(L'%', pos + 2) != std::wstring::npos) + { + DWORD bufSize = 4096; + WCHAR* buffer = new WCHAR[bufSize]; + + // %APPDATA% is a special casem + pos = path.find(L"%APPDATA%", pos); + if (pos != std::wstring::npos) + { + HRESULT hr = SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, buffer); + if (SUCCEEDED(hr)) + { + size_t len = wcslen(buffer); + do + { + path.replace(pos, 9, buffer, len); + } + while ((pos = path.find(L"%APPDATA%", pos + len)) != std::wstring::npos); + } + } + + if ((pos = path.find(L'%')) != std::wstring::npos && + path.find(L'%', pos + 2) != std::wstring::npos) + { + // Expand the environment variables. + do + { + DWORD ret = ExpandEnvironmentStrings(path.c_str(), buffer, bufSize); + if (ret == 0) // Error + { + break; + } + if (ret <= bufSize) // Fits in the buffer + { + path.assign(buffer, ret - 1); + break; + } + + delete [] buffer; + bufSize = ret; + buffer = new WCHAR[bufSize]; + } + while (true); + } + + delete [] buffer; + } +} + + +} // namespace PathUtil diff --git a/Common/PathUtil.h b/Common/PathUtil.h new file mode 100644 index 00000000..77166889 --- /dev/null +++ b/Common/PathUtil.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2013 Rainmeter Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef RM_COMMON_PATHUTIL_H_ +#define RM_COMMON_PATHUTIL_H_ + +#include +#include + +namespace PathUtil { + +bool IsSeparator(WCHAR ch); + +bool IsDotOrDotDot(const WCHAR* path); + +bool IsUNC(const std::wstring& path); + +bool IsAbsolute(const std::wstring& path); + +void AppendBacklashIfMissing(std::wstring& path); + +std::wstring GetFolderFromFilePath(const std::wstring& filePath); + +std::wstring GetVolume(const std::wstring& path); + +void ExpandEnvironmentVariables(std::wstring& strPath); + +} // namespace PathUtil + +#endif diff --git a/Library/CommandHandler.cpp b/Library/CommandHandler.cpp index fd4e058f..26119f11 100644 --- a/Library/CommandHandler.cpp +++ b/Library/CommandHandler.cpp @@ -17,6 +17,7 @@ */ #include "StdAfx.h" +#include "../Common/PathUtil.h" #include "CommandHandler.h" #include "ConfigParser.h" #include "DialogAbout.h" @@ -464,7 +465,7 @@ void CommandHandler::RunFile(const WCHAR* file, const WCHAR* args) } else { - std::wstring dir = Rainmeter::ExtractPath(file); + std::wstring dir = PathUtil::GetFolderFromFilePath(file); si.lpDirectory = dir.c_str(); si.lpParameters = args; si.fMask = SEE_MASK_DOENVSUBST | SEE_MASK_FLAG_NO_UI; diff --git a/Library/ConfigParser.cpp b/Library/ConfigParser.cpp index 40c26b88..c7cfb3a4 100644 --- a/Library/ConfigParser.cpp +++ b/Library/ConfigParser.cpp @@ -17,6 +17,7 @@ */ #include "StdAfx.h" +#include "../Common/PathUtil.h" #include "ConfigParser.h" #include "MathParser.h" #include "Litestep.h" @@ -98,7 +99,7 @@ void ConfigParser::SetBuiltInVariables(const std::wstring& filename, const std:: insertVariable(L"SETTINGSPATH", g_Rainmeter->GetSettingsPath()); insertVariable(L"SKINSPATH", g_Rainmeter->GetSkinPath()); insertVariable(L"PLUGINSPATH", g_Rainmeter->GetPluginPath()); - insertVariable(L"CURRENTPATH", Rainmeter::ExtractPath(filename)); + insertVariable(L"CURRENTPATH", PathUtil::GetFolderFromFilePath(filename)); insertVariable(L"ADDONSPATH", g_Rainmeter->GetAddonPath()); if (meterWindow) @@ -570,7 +571,7 @@ bool ConfigParser::ReplaceVariables(std::wstring& result) { bool replaced = false; - Rainmeter::ExpandEnvironmentVariables(result); + PathUtil::ExpandEnvironmentVariables(result); if (c_MonitorVariables.empty()) { @@ -764,7 +765,7 @@ const std::wstring& ConfigParser::ReadString(LPCTSTR section, LPCTSTR key, LPCTS } else { - Rainmeter::ExpandEnvironmentVariables(result); + PathUtil::ExpandEnvironmentVariables(result); } if (bReplaceMeasures && ReplaceMeasures(result)) @@ -1446,10 +1447,10 @@ void ConfigParser::ReadIniFile(const std::wstring& iniFile, LPCTSTR skinSection, value.assign(sep, clen); ReadVariables(); ReplaceVariables(value); - if (!System::IsAbsolutePath(value)) + if (!PathUtil::IsAbsolute(value)) { // Relative to the ini folder - value.insert(0, Rainmeter::ExtractPath(iniFile)); + value.insert(0, PathUtil::GetFolderFromFilePath(iniFile)); } if (resetInsertPos) diff --git a/Library/Library.vcxproj b/Library/Library.vcxproj index b3f0d74a..49731687 100644 --- a/Library/Library.vcxproj +++ b/Library/Library.vcxproj @@ -84,6 +84,7 @@ + @@ -315,6 +316,7 @@ + diff --git a/Library/Library.vcxproj.filters b/Library/Library.vcxproj.filters index ecf885c9..13bfbf56 100644 --- a/Library/Library.vcxproj.filters +++ b/Library/Library.vcxproj.filters @@ -396,6 +396,9 @@ Source Files + + Common + @@ -692,6 +695,9 @@ Common + + Common + diff --git a/Library/MeasureDiskSpace.cpp b/Library/MeasureDiskSpace.cpp index f6c48c12..f0fa00b9 100644 --- a/Library/MeasureDiskSpace.cpp +++ b/Library/MeasureDiskSpace.cpp @@ -20,6 +20,7 @@ #include "MeasureDiskSpace.h" #include "Rainmeter.h" #include "System.h" +#include "../Common/PathUtil.h" enum DRIVETYPE { @@ -188,9 +189,10 @@ void MeasureDiskSpace::ReadOptions(ConfigParser& parser, const WCHAR* section) m_OldTotalBytes = 0; m_StringValue.clear(); } - else if (!System::IsPathSeparator(m_Drive[m_Drive.length() - 1])) // E.g. "C:" + else { - m_Drive += L'\\'; // A trailing backslash is required. + // A trailing backslash is required for GetDiskFreeSpaceEx(). + PathUtil::AppendBacklashIfMissing(m_Drive); } m_Type = (1 == parser.ReadInt(section, L"Type", 0)); diff --git a/Library/MeterImage.cpp b/Library/MeterImage.cpp index 90846326..bad455f8 100644 --- a/Library/MeterImage.cpp +++ b/Library/MeterImage.cpp @@ -22,6 +22,7 @@ #include "Error.h" #include "Rainmeter.h" #include "System.h" +#include "../Common/PathUtil.h" #include "../Common/Gfx/Canvas.h" extern Rainmeter* g_Rainmeter; @@ -138,13 +139,7 @@ void MeterImage::ReadOptions(ConfigParser& parser, const WCHAR* section) // Deprecated! std::wstring path = parser.ReadString(section, L"Path", L""); - if (!path.empty()) - { - if (!System::IsPathSeparator(path[path.length() - 1])) - { - path += L'\\'; - } - } + PathUtil::AppendBacklashIfMissing(path); // Read tinting options m_Image.ReadOptions(parser, section, path.c_str()); diff --git a/Library/MeterWindow.cpp b/Library/MeterWindow.cpp index b841ac42..7286cd88 100644 --- a/Library/MeterWindow.cpp +++ b/Library/MeterWindow.cpp @@ -36,6 +36,7 @@ #include "TintedImage.h" #include "MeasureScript.h" #include "../Version.h" +#include "../Common/PathUtil.h" #include "../Common/Gfx/CanvasD2D.h" #include "../Common/Gfx/CanvasGDIP.h" @@ -4711,7 +4712,7 @@ void MeterWindow::SetWindowSizeVariables(int w, int h) */ void MeterWindow::MakePathAbsolute(std::wstring& path) { - if (path.empty() || System::IsAbsolutePath(path)) + if (path.empty() || PathUtil::IsAbsolute(path)) { return; // It's already absolute path (or it's empty) } diff --git a/Library/Rainmeter.cpp b/Library/Rainmeter.cpp index a332db09..4b438702 100644 --- a/Library/Rainmeter.cpp +++ b/Library/Rainmeter.cpp @@ -18,6 +18,7 @@ #include "StdAfx.h" #include "../Common/MenuTemplate.h" +#include "../Common/PathUtil.h" #include "Rainmeter.h" #include "TrayWindow.h" #include "System.h" @@ -192,15 +193,16 @@ int Rainmeter::Initialize(LPCWSTR iniPath, LPCWSTR layout) // Remove the module's name from the path WCHAR* pos = wcsrchr(buffer, L'\\'); m_Path.assign(buffer, pos ? pos - buffer + 1 : 0); + m_Drive = PathUtil::GetVolume(m_Path); bool bDefaultIniLocation = false; if (iniPath) { // The command line defines the location of Rainmeter.ini (or whatever it calls it). std::wstring iniFile = iniPath; - ExpandEnvironmentVariables(iniFile); + PathUtil::ExpandEnvironmentVariables(iniFile); - if (iniFile.empty() || System::IsPathSeparator(iniFile[iniFile.length() - 1])) + if (iniFile.empty() || PathUtil::IsSeparator(iniFile[iniFile.length() - 1])) { iniFile += L"Rainmeter.ini"; } @@ -209,7 +211,7 @@ int Rainmeter::Initialize(LPCWSTR iniPath, LPCWSTR layout) iniFile += L"\\Rainmeter.ini"; } - if (!System::IsPathSeparator(iniFile[0]) && iniFile.find_first_of(L':') == std::wstring::npos) + if (!PathUtil::IsSeparator(iniFile[0]) && iniFile.find_first_of(L':') == std::wstring::npos) { // Make absolute path iniFile.insert(0, m_Path); @@ -227,7 +229,7 @@ int Rainmeter::Initialize(LPCWSTR iniPath, LPCWSTR layout) if (_waccess(m_IniFile.c_str(), 0) == -1) { m_IniFile = L"%APPDATA%\\Rainmeter\\Rainmeter.ini"; - ExpandEnvironmentVariables(m_IniFile); + PathUtil::ExpandEnvironmentVariables(m_IniFile); bDefaultIniLocation = true; } } @@ -265,7 +267,7 @@ int Rainmeter::Initialize(LPCWSTR iniPath, LPCWSTR layout) // Set file locations { - m_SettingsPath = ExtractPath(m_IniFile); + m_SettingsPath = PathUtil::GetFolderFromFilePath(m_IniFile); size_t len = m_IniFile.length(); if (len > 4 && _wcsicmp(iniFile + (len - 4), L".ini") == 0) @@ -355,12 +357,8 @@ int Rainmeter::Initialize(LPCWSTR iniPath, LPCWSTR layout) { // Try Rainmeter.ini first m_SkinPath.assign(buffer, len); - ExpandEnvironmentVariables(m_SkinPath); - - if (!m_SkinPath.empty() && !System::IsPathSeparator(m_SkinPath[m_SkinPath.length() - 1])) - { - m_SkinPath += L'\\'; - } + PathUtil::ExpandEnvironmentVariables(m_SkinPath); + PathUtil::AppendBacklashIfMissing(m_SkinPath); } else if (bDefaultIniLocation && SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_MYDOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, buffer))) @@ -388,29 +386,6 @@ int Rainmeter::Initialize(LPCWSTR iniPath, LPCWSTR layout) LogNoticeF(L"IniFile: %s", iniFile); LogNoticeF(L"SkinPath: %s", m_SkinPath.c_str()); - // Extract volume path from program path - // E.g.: - // "C:\path\" to "C:" - // "\\server\share\" to "\\server\share" - // "\\server\C:\path\" to "\\server\C:" - std::wstring::size_type loc; - if ((loc = m_Path.find_first_of(L':')) != std::wstring::npos) - { - m_Drive.assign(m_Path, 0, loc + 1); - } - else if (System::IsUNCPath(m_Path)) - { - if ((loc = m_Path.find_first_of(L"\\/", 2)) != std::wstring::npos) - { - std::wstring::size_type loc2; - if ((loc2 = m_Path.find_first_of(L"\\/", loc + 1)) != std::wstring::npos || loc != (m_Path.length() - 1)) - { - loc = loc2; - } - } - m_Drive.assign(m_Path, 0, loc); - } - // Test that the Rainmeter.ini file is writable TestSettingsFile(bDefaultIniLocation); @@ -684,8 +659,7 @@ void Rainmeter::CreateComponentFolders(bool defaultIniLocation) do { if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && - wcscmp(L".", fd.cFileName) != 0 && - wcscmp(L"..", fd.cFileName) != 0) + PathUtil::IsDotOrDotDot(fd.cFileName)) { std::wstring layoutFolder = path + fd.cFileName; layoutFolder += L'\\'; @@ -1289,8 +1263,7 @@ int Rainmeter::ScanForSkinsRecursive(const std::wstring& path, std::wstring base if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - if (wcscmp(L".", fileData.cFileName) != 0 && - wcscmp(L"..", fileData.cFileName) != 0 && + if (!PathUtil::IsDotOrDotDot(fileData.cFileName) && !(level == 0 && wcscmp(L"@Backup", fileData.cFileName) == 0) && !(level == 0 && wcscmp(L"Backup", fileData.cFileName) == 0) && !(level == 1 && wcscmp(L"@Resources", fileData.cFileName) == 0)) @@ -1387,8 +1360,7 @@ void Rainmeter::ScanForLayouts() do { if (fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && - wcscmp(L".", fileData.cFileName) != 0 && - wcscmp(L"..", fileData.cFileName) != 0) + !PathUtil::IsDotOrDotDot(fileData.cFileName)) { m_Layouts.push_back(fileData.cFileName); } @@ -2537,7 +2509,7 @@ void Rainmeter::TestSettingsFile(bool bDefaultIniLocation) if (!bDefaultIniLocation) { std::wstring strTarget = L"%APPDATA%\\Rainmeter\\"; - ExpandEnvironmentVariables(strTarget); + PathUtil::ExpandEnvironmentVariables(strTarget); error += GetFormattedString(ID_STR_SETTINGSMOVEFILE, iniFile, strTarget.c_str()); } @@ -2549,68 +2521,3 @@ void Rainmeter::TestSettingsFile(bool bDefaultIniLocation) ShowMessage(nullptr, error.c_str(), MB_OK | MB_ICONERROR); } } - -std::wstring Rainmeter::ExtractPath(const std::wstring& strFilePath) -{ - std::wstring::size_type pos = strFilePath.find_last_of(L"\\/"); - if (pos != std::wstring::npos) - { - return strFilePath.substr(0, pos + 1); - } - return L".\\"; -} - -void Rainmeter::ExpandEnvironmentVariables(std::wstring& strPath) -{ - std::wstring::size_type pos; - - if ((pos = strPath.find(L'%')) != std::wstring::npos && - strPath.find(L'%', pos + 2) != std::wstring::npos) - { - DWORD bufSize = 4096; - WCHAR* buffer = new WCHAR[bufSize]; // lets hope the buffer is large enough... - - // %APPDATA% is a special case - pos = strPath.find(L"%APPDATA%", pos); - if (pos != std::wstring::npos) - { - HRESULT hr = SHGetFolderPath(nullptr, CSIDL_APPDATA, nullptr, SHGFP_TYPE_CURRENT, buffer); - if (SUCCEEDED(hr)) - { - size_t len = wcslen(buffer); - do - { - strPath.replace(pos, 9, buffer, len); - } - while ((pos = strPath.find(L"%APPDATA%", pos + len)) != std::wstring::npos); - } - } - - if ((pos = strPath.find(L'%')) != std::wstring::npos && - strPath.find(L'%', pos + 2) != std::wstring::npos) - { - // Expand the environment variables - do - { - DWORD ret = ExpandEnvironmentStrings(strPath.c_str(), buffer, bufSize); - if (ret == 0) // Error - { - LogWarningF(L"Unable to expand environment strings in: %s", strPath.c_str()); - break; - } - if (ret <= bufSize) // Fits in the buffer - { - strPath.assign(buffer, ret - 1); - break; - } - - delete [] buffer; - bufSize = ret; - buffer = new WCHAR[bufSize]; - } - while (true); - } - - delete [] buffer; - } -} diff --git a/Library/Rainmeter.h b/Library/Rainmeter.h index 43e5e869..a0cf45d3 100644 --- a/Library/Rainmeter.h +++ b/Library/Rainmeter.h @@ -212,9 +212,6 @@ public: bool LoadLayout(const std::wstring& name); void PreserveSetting(const std::wstring& from, LPCTSTR key, bool replace = true); - static std::wstring ExtractPath(const std::wstring& strFilePath); - static void ExpandEnvironmentVariables(std::wstring& strPath); - friend class CommandHandler; friend class DialogManage; diff --git a/Library/System.cpp b/Library/System.cpp index 837cc1c6..9b56cbcf 100644 --- a/Library/System.cpp +++ b/Library/System.cpp @@ -23,6 +23,7 @@ #include "MeterWindow.h" #include "MeasureNet.h" #include "Error.h" +#include "../Common/PathUtil.h" using namespace Gdiplus; @@ -1218,7 +1219,7 @@ bool System::CopyFiles(std::wstring from, std::wstring to, bool bMove) { // If given "from" path ends with path separator, remove it (Workaround for XP: error code 1026) size_t len; - while (len = from.size(), len > 0 && IsPathSeparator(from[len - 1])) + while (len = from.size(), len > 0 && PathUtil::IsSeparator(from[len - 1])) { from.resize(len - 1); } diff --git a/Library/System.h b/Library/System.h index d1f3f4d3..292f73d9 100644 --- a/Library/System.h +++ b/Library/System.h @@ -63,10 +63,6 @@ public: static ULONGLONG GetTickCount64(); static POINT GetCursorPosition(); - static bool IsPathSeparator(WCHAR ch) { return (ch == L'\\' || ch == L'/'); } - static bool IsUNCPath(const std::wstring& path) { return (path.length() >= 2 && IsPathSeparator(path[0]) && IsPathSeparator(path[1])); } - static bool IsAbsolutePath(const std::wstring& path) { return (path.find(L":\\") != std::wstring::npos || path.find(L":/") != std::wstring::npos || IsUNCPath(path)); } - static bool IsFileWritable(LPCWSTR file); static HMODULE RmLoadLibrary(LPCWSTR lpLibFileName, DWORD* dwError = nullptr); diff --git a/Library/TintedImage.cpp b/Library/TintedImage.cpp index a0ab820d..9fe57cf6 100644 --- a/Library/TintedImage.cpp +++ b/Library/TintedImage.cpp @@ -17,6 +17,7 @@ */ #include "StdAfx.h" +#include "../Common/PathUtil.h" #include "TintedImage.h" #include "ConfigParser.h" #include "System.h" @@ -631,13 +632,7 @@ void TintedImage::ReadOptions(ConfigParser& parser, const WCHAR* section, const std::wstring oldPath = m_Path; m_Path = parser.ReadString(section, m_OptionArray[OptionIndexImagePath], imagePath); - if (!m_Path.empty()) - { - if (!System::IsPathSeparator(m_Path[m_Path.length() - 1])) - { - m_Path += L'\\'; - } - } + PathUtil::AppendBacklashIfMissing(m_Path); m_HasPathChanged = (oldPath != m_Path);