Move path related functions into PathUtil

This commit is contained in:
Birunthan Mohanathas 2013-06-12 21:14:55 +03:00
parent 68430b54db
commit d079d13da6
14 changed files with 236 additions and 137 deletions

150
Common/PathUtil.cpp Normal file
View File

@ -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 <Shlobj.h>
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

45
Common/PathUtil.h Normal file
View File

@ -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 <Windows.h>
#include <string>
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

View File

@ -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;

View File

@ -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)

View File

@ -84,6 +84,7 @@
<ClCompile Include="..\Common\Gfx\Util\WICBitmapLockDIB.cpp" />
<ClCompile Include="..\Common\Gfx\Util\WICBitmapLockGDIP.cpp" />
<ClCompile Include="..\Common\MenuTemplate.cpp" />
<ClCompile Include="..\Common\PathUtil.cpp" />
<ClCompile Include="..\Common\Platform.cpp" />
<ClCompile Include="..\Common\StringUtil.cpp" />
<ClCompile Include="CommandHandler.cpp">
@ -315,6 +316,7 @@
<ClInclude Include="..\Common\Gfx\Util\WICBitmapLockDIB.h" />
<ClInclude Include="..\Common\Gfx\Util\WICBitmapLockGDIP.h" />
<ClInclude Include="..\Common\MenuTemplate.h" />
<ClInclude Include="..\Common\PathUtil.h" />
<ClInclude Include="..\Common\Platform.h" />
<ClInclude Include="..\Common\RawString.h" />
<ClInclude Include="..\Common\StringUtil.h" />

View File

@ -396,6 +396,9 @@
<ClCompile Include="CommandHandler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\Common\PathUtil.cpp">
<Filter>Common</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ConfigParser.h">
@ -692,6 +695,9 @@
<ClInclude Include="..\Common\RawString.h">
<Filter>Common</Filter>
</ClInclude>
<ClInclude Include="..\Common\PathUtil.h">
<Filter>Common</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Library.rc">

View File

@ -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));

View File

@ -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());

View File

@ -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)
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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);