summaryrefslogtreecommitdiff
path: root/shared
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:04:59 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:04:59 +0200
commitf570e2f2685aa43aa518c2f8578391c1847cddbe (patch)
treeb9376b3a7e807c5e0c4cf3d5615c14034d9675d6 /shared
parent3.2 (diff)
downloadFreeFileSync-f570e2f2685aa43aa518c2f8578391c1847cddbe.tar.gz
FreeFileSync-f570e2f2685aa43aa518c2f8578391c1847cddbe.tar.bz2
FreeFileSync-f570e2f2685aa43aa518c2f8578391c1847cddbe.zip
3.3
Diffstat (limited to 'shared')
-rw-r--r--shared/buildInfo.h12
-rw-r--r--shared/customButton.cpp6
-rw-r--r--shared/customComboBox.cpp4
-rw-r--r--shared/dllLoader.cpp45
-rw-r--r--shared/dllLoader.h13
-rw-r--r--shared/fileHandling.cpp239
-rw-r--r--shared/fileHandling.h6
-rw-r--r--shared/fileID.cpp82
-rw-r--r--shared/fileID.h194
-rw-r--r--shared/fileTraverser.cpp22
-rw-r--r--shared/localization.cpp13
-rw-r--r--shared/longPathPrefix.cpp95
-rw-r--r--shared/longPathPrefix.h27
-rw-r--r--shared/recycler.cpp128
-rw-r--r--shared/recycler.h21
-rw-r--r--shared/shadow.cpp122
-rw-r--r--shared/shadow.h3
-rw-r--r--shared/zstring.cpp123
-rw-r--r--shared/zstring.h24
19 files changed, 924 insertions, 255 deletions
diff --git a/shared/buildInfo.h b/shared/buildInfo.h
new file mode 100644
index 00000000..1c14caa5
--- /dev/null
+++ b/shared/buildInfo.h
@@ -0,0 +1,12 @@
+#ifndef BUILDINFO_H_INCLUDED
+#define BUILDINFO_H_INCLUDED
+
+namespace Utility
+{
+//determine build info
+//seems to be safer than checking for _WIN64 (defined on windows for 64-bit compilations only) while _WIN32 is always defined
+static const bool is32BitBuild = sizeof(void*) == 4;
+static const bool is64BitBuild = sizeof(void*) == 8;
+}
+
+#endif // BUILDINFO_H_INCLUDED
diff --git a/shared/customButton.cpp b/shared/customButton.cpp
index 40c0397f..73b9d1ee 100644
--- a/shared/customButton.cpp
+++ b/shared/customButton.cpp
@@ -277,17 +277,17 @@ void wxButtonWithImage::refreshButtonLabel()
//wxDC::DrawLabel() unfortunately isn't working for transparent images on Linux, so we need to use custom image-concatenation
if (bitmapFront.IsOk())
- writeToImage(wxImage(bitmapFront.ConvertToImage()),
+ writeToImage(bitmapFront.ConvertToImage(),
wxPoint(0, (transparentImage.GetHeight() - bitmapFront.GetHeight()) / 2),
transparentImage);
if (bitmapText.IsOk())
- writeToImage(wxImage(bitmapText.ConvertToImage()),
+ writeToImage(bitmapText.ConvertToImage(),
wxPoint(bitmapFront.GetWidth() + m_spaceAfter, (transparentImage.GetHeight() - bitmapText.GetHeight()) / 2),
transparentImage);
if (bitmapBack.IsOk())
- writeToImage(wxImage(bitmapBack.ConvertToImage()),
+ writeToImage(bitmapBack.ConvertToImage(),
wxPoint(bitmapFront.GetWidth() + m_spaceAfter + bitmapText.GetWidth() + m_spaceBefore, (transparentImage.GetHeight() - bitmapBack.GetHeight()) / 2),
transparentImage);
diff --git a/shared/customComboBox.cpp b/shared/customComboBox.cpp
index f2eec7a9..e21e915c 100644
--- a/shared/customComboBox.cpp
+++ b/shared/customComboBox.cpp
@@ -50,6 +50,10 @@ void CustomComboBox::OnKeyEvent(wxKeyEvent& event)
void CustomComboBox::addPairToFolderHistory(const wxString& newFolder, unsigned int maxHistSize)
{
+ //don't add empty directories
+ if (newFolder.empty())
+ return;
+
const wxString oldVal = this->GetValue();
//insert new folder or put it to the front if already existing
diff --git a/shared/dllLoader.cpp b/shared/dllLoader.cpp
index e37ded54..fbfa5b11 100644
--- a/shared/dllLoader.cpp
+++ b/shared/dllLoader.cpp
@@ -1,44 +1,55 @@
#include "dllLoader.h"
#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include <map>
+#include <assert.h>
namespace
{
-class KernelDllHandler //dynamically load "kernel32.dll"
+class DllHandler //dynamically load "kernel32.dll"
{
public:
- static const KernelDllHandler& getInstance()
+ static DllHandler& getInstance()
{
- static KernelDllHandler instance;
+ static DllHandler instance;
return instance;
}
- HINSTANCE getHandle() const
+ HINSTANCE getHandle(const std::wstring& libraryName)
{
- return hKernel;
+ HandleMap::const_iterator foundEntry = handles.find(libraryName);
+ if (foundEntry == handles.end())
+ {
+ HINSTANCE newHandle = ::LoadLibrary(libraryName.c_str());
+ handles.insert(std::make_pair(libraryName, newHandle));
+
+ assert(handles.find(libraryName) != handles.end());
+ return newHandle;
+ }
+ else
+ return foundEntry->second;
}
private:
- KernelDllHandler() :
- hKernel(NULL)
- {
- //get a handle to the DLL module containing required functionality
- hKernel = ::LoadLibrary(L"kernel32.dll");
- }
+ DllHandler() {}
- ~KernelDllHandler()
+ ~DllHandler()
{
- if (hKernel) ::FreeLibrary(hKernel);
+ for (HandleMap::const_iterator i = handles.begin(); i != handles.end(); ++i)
+ if (i->second != NULL) ::FreeLibrary(i->second);
}
- HINSTANCE hKernel;
+ typedef std::map<std::wstring, HINSTANCE> HandleMap;
+ HandleMap handles;
};
}
-void* Utility::loadSymbolKernel(const std::string& functionName)
+void* Utility::loadSymbol(const std::wstring& libraryName, const std::string& functionName)
{
- if (KernelDllHandler::getInstance().getHandle() != NULL)
- return reinterpret_cast<void*>(::GetProcAddress(KernelDllHandler::getInstance().getHandle(), functionName.c_str()));
+ const HINSTANCE libHandle = DllHandler::getInstance().getHandle(libraryName);
+
+ if (libHandle != NULL)
+ return reinterpret_cast<void*>(::GetProcAddress(libHandle, functionName.c_str()));
else
return NULL;
}
diff --git a/shared/dllLoader.h b/shared/dllLoader.h
index bf62b542..5b561c5a 100644
--- a/shared/dllLoader.h
+++ b/shared/dllLoader.h
@@ -5,10 +5,11 @@
namespace Utility
{
- //load kernel dll functions
-template <typename FunctionType>
-FunctionType loadDllFunKernel(const std::string& functionName);
+//load function from a DLL library, e.g. from kernel32.dll
+//NOTE: you're allowed to take a static reference to the return value to optimize performance! :)
+template <typename FunctionType>
+FunctionType loadDllFunction(const std::wstring& libraryName, const std::string& functionName);
@@ -22,13 +23,13 @@ FunctionType loadDllFunKernel(const std::string& functionName);
//---------------Inline Implementation---------------------------------------------------
-void* loadSymbolKernel(const std::string& functionName);
+void* loadSymbol(const std::wstring& libraryName, const std::string& functionName);
template <typename FunctionType>
inline
-FunctionType loadDllFunKernel(const std::string& functionName)
+FunctionType loadDllFunction(const std::wstring& libraryName, const std::string& functionName)
{
- return reinterpret_cast<FunctionType>(loadSymbolKernel(functionName));
+ return reinterpret_cast<FunctionType>(loadSymbol(libraryName, functionName));
}
#ifndef FFS_WIN
diff --git a/shared/fileHandling.cpp b/shared/fileHandling.cpp
index 4b9901e0..ef1d3e6c 100644
--- a/shared/fileHandling.cpp
+++ b/shared/fileHandling.cpp
@@ -14,9 +14,11 @@
#include <wx/utils.h>
#ifdef FFS_WIN
+#include "recycler.h"
#include "dllLoader.h"
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include "shadow.h"
+#include "longPathPrefix.h"
#elif defined FFS_LINUX
#include <sys/stat.h>
@@ -104,83 +106,48 @@ Zstring FreeFileSync::getFormattedDirectoryName(const Zstring& dirname)
if (dirnameTmp.empty()) //an empty string is interpreted as "\"; this is not desired
return Zstring();
- if (!dirnameTmp.EndsWith(zToWx(globalFunctions::FILE_NAME_SEPARATOR)))
- dirnameTmp += zToWx(globalFunctions::FILE_NAME_SEPARATOR);
-
//replace macros
expandMacros(dirnameTmp);
+#ifdef FFS_WIN
+ /*
+ resolve relative names; required by:
+ - \\?\-prefix which needs absolute names
+ - Volume Shadow Copy: volume name needs to be part of each filename
+ - file icon buffer (at least for extensions that are acutally read from disk, e.g. "exe")
+ - detection of dependent directories, e.g. "\" and "C:\test"
+ */
+ dirnameTmp = resolveRelativePath(dirnameTmp.c_str()).c_str();
+#endif
+
+ if (!dirnameTmp.EndsWith(zToWx(globalFunctions::FILE_NAME_SEPARATOR)))
+ dirnameTmp += zToWx(globalFunctions::FILE_NAME_SEPARATOR);
+
return wxToZ(dirnameTmp);
}
-class RecycleBin
+bool FreeFileSync::recycleBinExists()
{
-public:
- static const RecycleBin& getInstance()
- {
- static RecycleBin instance; //lazy creation of RecycleBin
- return instance;
- }
-
- bool recycleBinExists() const
- {
- return recycleBinAvailable;
- }
-
- bool moveToRecycleBin(const Zstring& filename) const; //throw (std::logic_error)
-
-private:
- RecycleBin() :
- recycleBinAvailable(false)
- {
#ifdef FFS_WIN
- recycleBinAvailable = true;
+ return true;
+#else
+ return false;
#endif // FFS_WIN
- }
-
- ~RecycleBin() {}
-
-private:
- bool recycleBinAvailable;
-};
+}
-bool RecycleBin::moveToRecycleBin(const Zstring& filename) const //throw (std::logic_error)
+inline
+void moveToRecycleBin(const Zstring& filename) //throw (std::logic_error), throw (FileError)
{
- if (!recycleBinAvailable) //this method should ONLY be called if recycle bin is available
+ if (!FreeFileSync::recycleBinExists()) //this method should ONLY be called if recycle bin is available
throw std::logic_error("Initialization of Recycle Bin failed!");
#ifdef FFS_WIN
- Zstring filenameDoubleNull = filename + wxChar(0);
-
- SHFILEOPSTRUCT fileOp;
- fileOp.hwnd = NULL;
- fileOp.wFunc = FO_DELETE;
- fileOp.pFrom = filenameDoubleNull.c_str();
- fileOp.pTo = NULL;
- fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
- fileOp.fAnyOperationsAborted = false;
- fileOp.hNameMappings = NULL;
- fileOp.lpszProgressTitle = NULL;
-
- if (SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) return false;
+ FreeFileSync::moveToWindowsRecycler(filename); //throw (FileError)
+#else
+ throw std::logic_error("No Recycler for Linux available at the moment!");
#endif
-
- return true;
-}
-
-
-bool FreeFileSync::recycleBinExists()
-{
- return RecycleBin::getInstance().recycleBinExists();
-}
-
-
-inline
-bool moveToRecycleBin(const Zstring& filename) //throw (std::logic_error)
-{
- return RecycleBin::getInstance().moveToRecycleBin(filename);
}
@@ -190,7 +157,7 @@ bool FreeFileSync::fileExists(const DefaultChar* filename)
#ifdef FFS_WIN
// we must use GetFileAttributes() instead of the ANSI C functions because
// it can cope with network (UNC) paths unlike them
- const DWORD ret = ::GetFileAttributes(filename);
+ const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
return (ret != INVALID_FILE_ATTRIBUTES) && !(ret & FILE_ATTRIBUTE_DIRECTORY); //returns true for (file-)symlinks also
@@ -208,7 +175,7 @@ bool FreeFileSync::dirExists(const DefaultChar* dirname)
#ifdef FFS_WIN
// we must use GetFileAttributes() instead of the ANSI C functions because
// it can cope with network (UNC) paths unlike them
- const DWORD ret = ::GetFileAttributes(dirname);
+ const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str());
return (ret != INVALID_FILE_ATTRIBUTES) && (ret & FILE_ATTRIBUTE_DIRECTORY); //returns true for (dir-)symlinks also
@@ -223,7 +190,7 @@ bool FreeFileSync::dirExists(const DefaultChar* dirname)
bool FreeFileSync::symlinkExists(const DefaultChar* objname)
{
#ifdef FFS_WIN
- const DWORD ret = ::GetFileAttributes(objname);
+ const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(objname).c_str());
return (ret != INVALID_FILE_ATTRIBUTES) && (ret & FILE_ATTRIBUTE_REPARSE_POINT);
#elif defined FFS_LINUX
@@ -263,8 +230,8 @@ bool FreeFileSync::isMovable(const Zstring& pathFrom, const Zstring& pathTo)
const bool result =
//try to move the file
#ifdef FFS_WIN
- ::MoveFileEx(dummyFileSource.c_str(), //__in LPCTSTR lpExistingFileName,
- dummyFileTarget.c_str(), //__in_opt LPCTSTR lpNewFileName,
+ ::MoveFileEx(applyLongPathPrefix(dummyFileSource).c_str(), //__in LPCTSTR lpExistingFileName,
+ applyLongPathPrefix(dummyFileTarget).c_str(), //__in_opt LPCTSTR lpNewFileName,
0) != 0; //__in DWORD dwFlags
#elif defined FFS_LINUX
::rename(dummyFileSource.c_str(), dummyFileTarget.c_str()) == 0;
@@ -285,7 +252,9 @@ void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin)
{
//no error situation if file is not existing! manual deletion relies on it!
#ifdef FFS_WIN
- if (::GetFileAttributes(filename.c_str()) == INVALID_FILE_ATTRIBUTES)
+
+ const Zstring filenameFmt = applyLongPathPrefix(filename);
+ if (::GetFileAttributes(filenameFmt.c_str()) == INVALID_FILE_ATTRIBUTES)
return; //neither file nor any other object with that name existing
#elif defined FFS_LINUX
@@ -296,15 +265,14 @@ void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin)
if (useRecycleBin)
{
- if (!moveToRecycleBin(filename))
- throw FileError(wxString(_("Error moving to Recycle Bin:")) + wxT("\n\"") + zToWx(filename) + wxT("\""));
+ ::moveToRecycleBin(filename);
return;
}
#ifdef FFS_WIN
//initialize file attributes
if (!::SetFileAttributes(
- filename.c_str(), //address of filename
+ filenameFmt.c_str(), //address of filename
FILE_ATTRIBUTE_NORMAL)) //attributes to set
{
wxString errorMessage = wxString(_("Error deleting file:")) + wxT("\n\"") + zToWx(filename) + wxT("\"");
@@ -312,7 +280,7 @@ void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin)
}
//remove file, support for \\?\-prefix
- if (!::DeleteFile(filename.c_str()))
+ if (!::DeleteFile(filenameFmt.c_str()))
{
wxString errorMessage = wxString(_("Error deleting file:")) + wxT("\n\"") + zToWx(filename) + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
@@ -331,9 +299,9 @@ void FreeFileSync::removeFile(const Zstring& filename, const bool useRecycleBin)
void FreeFileSync::renameFile(const Zstring& oldName, const Zstring& newName) //throw (FileError);
{
#ifdef FFS_WIN
- if (!::MoveFileEx(oldName.c_str(), //__in LPCTSTR lpExistingFileName,
- newName.c_str(), //__in_opt LPCTSTR lpNewFileName,
- 0)) //__in DWORD dwFlags
+ if (!::MoveFileEx(applyLongPathPrefix(oldName).c_str(), //__in LPCTSTR lpExistingFileName,
+ applyLongPathPrefix(newName).c_str(), //__in_opt LPCTSTR lpNewFileName,
+ 0)) //__in DWORD dwFlags
{
const wxString errorMessage = wxString(_("Error moving file:")) + wxT("\n\"") + zToWx(oldName) + wxT("\" ->\n\"") + zToWx(newName) + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
@@ -388,8 +356,8 @@ void FreeFileSync::moveFile(const Zstring& sourceFile, const Zstring& targetFile
#ifdef FFS_WIN
//first try to move the file directly without copying
- if (::MoveFileEx(sourceFile.c_str(), //__in LPCTSTR lpExistingFileName,
- targetFile.c_str(), //__in_opt LPCTSTR lpNewFileName,
+ if (::MoveFileEx(applyLongPathPrefix(sourceFile).c_str(), //__in LPCTSTR lpExistingFileName,
+ applyLongPathPrefix(targetFile).c_str(), //__in_opt LPCTSTR lpNewFileName,
0)) //__in DWORD dwFlags
return;
@@ -494,8 +462,8 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool
{
//first try to move the directory directly without copying
#ifdef FFS_WIN
- if (::MoveFileEx(sourceDir.c_str(), //__in LPCTSTR lpExistingFileName,
- targetDir.c_str(), //__in_opt LPCTSTR lpNewFileName,
+ if (::MoveFileEx(applyLongPathPrefix(sourceDir).c_str(), //__in LPCTSTR lpExistingFileName,
+ applyLongPathPrefix(targetDir).c_str(), //__in_opt LPCTSTR lpNewFileName,
0)) //__in DWORD dwFlags
return;
@@ -615,7 +583,9 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
{
//no error situation if directory is not existing! manual deletion relies on it!
#ifdef FFS_WIN
- const DWORD dirAttr = GetFileAttributes(directory.c_str()); //name of a file or directory
+ const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix
+
+ const DWORD dirAttr = ::GetFileAttributes(directoryFmt.c_str()); //name of a file or directory
if (dirAttr == INVALID_FILE_ATTRIBUTES)
return; //neither directory nor any other object with that name existing
@@ -627,8 +597,7 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
if (useRecycleBin)
{
- if (!moveToRecycleBin(directory))
- throw FileError(wxString(_("Error moving to Recycle Bin:")) + wxT("\n\"") + zToWx(directory) + wxT("\""));
+ ::moveToRecycleBin(directory);
return;
}
@@ -636,7 +605,7 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
#ifdef FFS_WIN
//initialize file attributes
if (!::SetFileAttributes( // initialize file attributes: actually NEEDED for symbolic links also!
- directory.c_str(), // address of directory name
+ directoryFmt.c_str(), // address of directory name
FILE_ATTRIBUTE_NORMAL)) // attributes to set
{
wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
@@ -645,9 +614,9 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
//attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!!
- if (dirAttr & FILE_ATTRIBUTE_REPARSE_POINT) //remove symlink directly, support for \\?\-prefix
+ if (dirAttr & FILE_ATTRIBUTE_REPARSE_POINT) //remove symlink directly
{
- if (!::RemoveDirectory(directory.c_str()))
+ if (!::RemoveDirectory(directoryFmt.c_str()))
{
wxString errorMessage = wxString(_("Error deleting directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
@@ -683,7 +652,7 @@ void FreeFileSync::removeDirectory(const Zstring& directory, const bool useRecyc
//parent directory is deleted last
#ifdef FFS_WIN
//remove directory, support for \\?\-prefix
- if (!::RemoveDirectory(directory.c_str()))
+ if (!::RemoveDirectory(directoryFmt.c_str()))
#else
if (::rmdir(directory.c_str()) != 0)
#endif
@@ -718,7 +687,7 @@ void FreeFileSync::copyFileTimes(const Zstring& sourceDir, const Zstring& target
return;
#ifdef FFS_WIN
- HANDLE hDirRead = ::CreateFile(sourceDir.c_str(),
+ HANDLE hDirRead = ::CreateFile(applyLongPathPrefix(sourceDir).c_str(),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
@@ -737,7 +706,7 @@ void FreeFileSync::copyFileTimes(const Zstring& sourceDir, const Zstring& target
&accessTime,
&lastWriteTime))
{
- HANDLE hDirWrite = ::CreateFile(targetDir.c_str(),
+ HANDLE hDirWrite = ::CreateFile(applyLongPathPrefix(targetDir).c_str(),
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
@@ -776,13 +745,13 @@ void FreeFileSync::copyFileTimes(const Zstring& sourceDir, const Zstring& target
Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target path of symbolic link to a directory
{
//open handle to target of symbolic link
- HANDLE hDir = ::CreateFile(dirLinkName.c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
+ const HANDLE hDir = ::CreateFile(FreeFileSync::applyLongPathPrefix(dirLinkName).c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
if (hDir == INVALID_HANDLE_VALUE)
return Zstring();
@@ -799,7 +768,7 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa
DWORD cchFilePath,
DWORD dwFlags);
static const GetFinalPathNameByHandleWFunc getFinalPathNameByHandle =
- Utility::loadDllFunKernel<GetFinalPathNameByHandleWFunc>("GetFinalPathNameByHandleW");
+ Utility::loadDllFunction<GetFinalPathNameByHandleWFunc>(L"kernel32.dll", "GetFinalPathNameByHandleW");
if (getFinalPathNameByHandle == NULL)
throw FileError(wxString(_("Error loading library function:")) + wxT("\n\"") + wxT("GetFinalPathNameByHandleW") + wxT("\""));
@@ -885,10 +854,10 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
//now creation should be possible
#ifdef FFS_WIN
- const DWORD templateAttr = ::GetFileAttributes(templateDir.c_str()); //replaces wxDirExists(): also returns successful for broken symlinks
+ const DWORD templateAttr = ::GetFileAttributes(applyLongPathPrefix(templateDir).c_str()); //replaces wxDirExists(): also returns successful for broken symlinks
if (templateAttr == INVALID_FILE_ATTRIBUTES) //fallback
{
- if (!::CreateDirectory(directory.c_str(), // pointer to a directory path string
+ if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), // pointer to a directory path string
NULL) && level == 0)
{
const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
@@ -910,8 +879,8 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
else
{
if (!::CreateDirectoryEx( // this function automatically copies symbolic links if encountered
- linkPath.c_str(), // pointer to path string of template directory
- directory.c_str(), // pointer to a directory path string
+ applyLongPathPrefix(linkPath).c_str(), // pointer to path string of template directory
+ applyLongPathPrefixCreateDir(directory).c_str(), // pointer to a directory path string
NULL) && level == 0)
{
const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
@@ -924,12 +893,16 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
}
else //in all other cases
{
- if (!::CreateDirectoryEx( // this function automatically copies symbolic links if encountered
- templateDir.c_str(), // pointer to path string of template directory
- directory.c_str(), // pointer to a directory path string
+ if (!::CreateDirectoryEx( // this function automatically copies symbolic links if encountered
+ applyLongPathPrefix(templateDir).c_str(), // pointer to path string of template directory
+ applyLongPathPrefixCreateDir(directory).c_str(), // pointer to a directory path string
NULL) && level == 0)
{
- const wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\"");
+ const wxString errorMessage = templateAttr & FILE_ATTRIBUTE_REPARSE_POINT ?
+ //give a more meaningful errormessage if copying a symbolic link failed, e.g. "C:\Users\ZenJu\Application Data"
+ (wxString(_("Error copying symbolic link:")) + wxT("\n\"") + templateDir.c_str() + wxT("\" ->\n\"") + directory.c_str() + wxT("\"")) :
+
+ (wxString(_("Error creating directory:")) + wxT("\n\"") + directory.c_str() + wxT("\""));
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
}
@@ -960,7 +933,7 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat
if (symlink(buffer, directory.c_str()) != 0)
{
- wxString errorMessage = wxString(_("Error creating directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"");
+ const wxString errorMessage = wxString(_("Error copying symbolic link:")) + wxT("\n\"") + zToWx(templateDir) + wxT("\" ->\n\"") + zToWx(directory) + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
}
return; //symlink created successfully
@@ -1022,7 +995,7 @@ Zstring createTempName(const Zstring& filename)
{
//if it's not unique, add a postfix number
int postfix = 1;
- while (FreeFileSync::fileExists(output + DefaultChar('_') + numberToZstring(postfix)))
+ while (FreeFileSync::fileExists(output + DefaultStr("_") + numberToZstring(postfix)))
++postfix;
output += Zstring(DefaultStr("_")) + numberToZstring(postfix);
@@ -1107,9 +1080,10 @@ bool supportForNonEncryptedDestination()
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- //symbolic links are supported starting with Vista
+ //encrypted destination is not supported with Windows 2000
if (GetVersionEx(&osvi))
- return osvi.dwMajorVersion >= 5 && osvi.dwMinorVersion >= 1; //XP has majorVersion == 5, minorVersion == 1, Vista majorVersion == 6
+ return osvi.dwMajorVersion > 5 ||
+ (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion > 0); //2000 has majorVersion == 5, minorVersion == 0
//overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
return false;
}
@@ -1121,6 +1095,10 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
FreeFileSync::ShadowCopy* shadowCopyHandler,
FreeFileSync::CopyFileCallback* callback)
{
+ //FreeFileSync::fileExists(targetFile.c_str()) -> avoid this call, performance;
+ //if target exists (very unlikely, because sync-algorithm deletes it) renaming below will fail!
+
+
DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS;
//copy symbolic links instead of the files pointed at
@@ -1133,11 +1111,11 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
if (nonEncSupported)
copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION;
-
const Zstring temporary = createTempName(targetFile); //use temporary file until a correct date has been set
+
if (!::CopyFileEx( //same performance as CopyFile()
- sourceFile.c_str(),
- temporary.c_str(),
+ applyLongPathPrefix(sourceFile).c_str(),
+ applyLongPathPrefix(temporary).c_str(),
copyCallbackInternal,
callback,
NULL,
@@ -1152,6 +1130,7 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
(lastError == ERROR_SHARING_VIOLATION ||
lastError == ERROR_LOCK_VIOLATION))
{
+ //shadowFilename already contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat"
const Zstring shadowFilename(shadowCopyHandler->makeShadowCopy(sourceFile));
copyFile(shadowFilename, //transferred bytes is automatically reset when new file is copied
targetFile,
@@ -1161,12 +1140,33 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
return;
}
- const wxString errorMessage = wxString(_("Error copying file:")) + wxT("\n\"") + sourceFile.c_str() + wxT("\" ->\n\"") + targetFile.c_str() + wxT("\"");
- throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError));
+ //assemble error message...
+ const wxString errorMessage = wxString(_("Error copying file:")) + wxT("\n\"") + sourceFile.c_str() + wxT("\" ->\n\"") + targetFile.c_str() + wxT("\"") +
+ wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError);
+
+ throw FileError(errorMessage);
}
- //rename temporary file
- FreeFileSync::renameFile(temporary, targetFile);
+ try
+ {
+ //rename temporary file
+ FreeFileSync::renameFile(temporary, targetFile);
+ }
+ catch (...) //if renaming temporary failed: cleanup
+ {
+ try
+ {
+ removeFile(temporary, false); //throw (FileError, std::logic_error);
+ }
+ catch(...) {}
+
+ //this can only happen in very obscure situations: while scanning, target didn't exist, but while sync'ing it suddenly does (e.g. network drop?)
+ if (FreeFileSync::fileExists(targetFile.c_str()))
+ throw FileError(wxString(_("Error copying file:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + zToWx(targetFile) + wxT("\"\n\n")
+ + _("Target file already existing!"));
+
+ throw;
+ }
//copy creation date (last modification date is redundantly written, too)
copyFileTimes(sourceFile, targetFile); //throw()
@@ -1230,7 +1230,7 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
if (symlink(buffer, targetFile.c_str()) != 0)
{
- const wxString errorMessage = wxString(_("Error writing file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\"");
+ const wxString errorMessage = wxString(_("Error copying symbolic link:")) + wxT("\n\"") + zToWx(sourceFile) + wxT("\" ->\n\"") + zToWx(targetFile) + wxT("\"");
throw FileError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
}
@@ -1254,12 +1254,12 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
//create targetFile and open it for writing
const Zstring temporary = createTempName(targetFile); //use temporary file until a correct date has been set
- std::ofstream fileOut(temporary.c_str(), std::ios_base::binary);
- if (fileOut.fail())
- throw FileError(wxString(_("Error opening file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\""));
-
try
{
+ std::ofstream fileOut(temporary.c_str(), std::ios_base::binary);
+ if (fileOut.fail())
+ throw FileError(wxString(_("Error opening file:")) + wxT("\n\"") + zToWx(targetFile) + wxT("\""));
+
//copy contents of sourceFile to targetFile
wxULongLong totalBytesTransferred;
static MemoryAllocator memory;
@@ -1326,8 +1326,11 @@ void FreeFileSync::copyFile(const Zstring& sourceFile,
catch (...)
{
//try to delete target file if error occured, or exception was thrown in callback function
+ //no data-loss, because of "fileExists(targetFile))" check at the beginning!
if (FreeFileSync::fileExists(targetFile))
::unlink(targetFile); //don't handle error situations!
+
+ //clean-up temporary
if (FreeFileSync::fileExists(temporary))
::unlink(temporary); //don't handle error situations!
diff --git a/shared/fileHandling.h b/shared/fileHandling.h
index b12f6f03..95d8fddd 100644
--- a/shared/fileHandling.h
+++ b/shared/fileHandling.h
@@ -1,5 +1,5 @@
-#ifndef RECYCLER_H_INCLUDED
-#define RECYCLER_H_INCLUDED
+#ifndef RECYCLER2_H_INCLUDED
+#define RECYCLER2_H_INCLUDED
#include "zstring.h"
#include "fileError.h"
@@ -83,4 +83,4 @@ void copyFile(const Zstring& sourceFile,
}
-#endif // RECYCLER_H_INCLUDED
+#endif // RECYCLER2_H_INCLUDED
diff --git a/shared/fileID.cpp b/shared/fileID.cpp
new file mode 100644
index 00000000..005707dc
--- /dev/null
+++ b/shared/fileID.cpp
@@ -0,0 +1,82 @@
+#include "fileID.h"
+
+#ifdef FFS_WIN
+#include "staticAssert.h"
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "longPathPrefix.h"
+
+#elif defined FFS_LINUX
+
+#endif
+
+
+
+#ifdef FFS_WIN
+class CloseHandleOnExit
+{
+public:
+ CloseHandleOnExit(HANDLE fileHandle) : fileHandle_(fileHandle) {}
+
+ ~CloseHandleOnExit()
+ {
+ ::CloseHandle(fileHandle_);
+ }
+
+private:
+ HANDLE fileHandle_;
+};
+
+
+Utility::FileID Utility::retrieveFileID(const Zstring& filename)
+{
+ //ensure our DWORD_FFS really is the same as DWORD
+ assert_static(sizeof(Utility::FileID::DWORD_FFS) == sizeof(DWORD));
+
+//WARNING: CreateFile() is SLOW, while GetFileInformationByHandle() is quite cheap!
+//http://msdn.microsoft.com/en-us/library/aa363788(VS.85).aspx
+
+ const HANDLE hFile = ::CreateFile(FreeFileSync::applyLongPathPrefix(filename).c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, //FILE_FLAG_BACKUP_SEMANTICS needed to open directories
+ NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ CloseHandleOnExit dummy(hFile);
+
+ BY_HANDLE_FILE_INFORMATION info;
+ if (::GetFileInformationByHandle(hFile, &info))
+ {
+ return Utility::FileID(info.dwVolumeSerialNumber,
+ info.nFileIndexHigh,
+ info.nFileIndexLow);
+ }
+ }
+ return Utility::FileID(); //empty ID
+}
+
+
+#elif defined FFS_LINUX
+Utility::FileID Utility::retrieveFileID(const Zstring& filename)
+{
+ struct stat fileInfo;
+ if (::lstat(filename.c_str(), &fileInfo) == 0) //lstat() does not resolve symlinks
+ return Utility::FileID(fileInfo.st_dev, fileInfo.st_ino);
+
+ return Utility::FileID(); //empty ID
+}
+#endif
+
+
+bool Utility::sameFileSpecified(const Zstring& file1, const Zstring& file2)
+{
+ const Utility::FileID id1 = retrieveFileID(file1);
+ const Utility::FileID id2 = retrieveFileID(file2);
+
+ if (id1 != FileID() && id2 != FileID())
+ return id1 == id2;
+
+ return false;
+}
diff --git a/shared/fileID.h b/shared/fileID.h
new file mode 100644
index 00000000..7944b368
--- /dev/null
+++ b/shared/fileID.h
@@ -0,0 +1,194 @@
+#ifndef FILEID_H_INCLUDED
+#define FILEID_H_INCLUDED
+
+#include <wx/stream.h>
+#include "zstring.h"
+
+#ifdef FFS_WIN
+#elif defined FFS_LINUX
+#include <sys/stat.h>
+#endif
+
+
+//unique file identifier
+namespace Utility
+{
+class FileID
+{
+public:
+ //standard copy constructor and assignment operator are okay!
+
+ FileID(wxInputStream& stream); //read
+ void toStream(wxOutputStream& stream) const; //write
+
+ bool isNull() const;
+ bool operator==(const FileID& rhs) const;
+ bool operator!=(const FileID& rhs) const;
+ bool operator<(const FileID& rhs) const;
+
+ FileID();
+#ifdef FFS_WIN
+ typedef unsigned long DWORD_FFS; //we don't want do include "windows.h" or "<wx/msw/wrapwin.h>" here, do we?
+
+ FileID(DWORD_FFS dwVolumeSN,
+ DWORD_FFS fileIndexHi,
+ DWORD_FFS fileIndexLo);
+#elif defined FFS_LINUX
+ FileID(dev_t devId,
+ ino_t inId);
+#endif
+private:
+#ifdef FFS_WIN
+ DWORD_FFS dwVolumeSerialNumber;
+ DWORD_FFS nFileIndexHigh;
+ DWORD_FFS nFileIndexLow;
+#elif defined FFS_LINUX
+ dev_t deviceId;
+ ino_t inodeId;
+#endif
+};
+
+//get unique file id (symbolic link handling: opens the link!!!)
+//error condition: returns FileID ()
+FileID retrieveFileID(const Zstring& filename);
+
+//test whether two distinct paths point to the same file or directory:
+// true: paths point to same files/dirs
+// false: error occured OR point to different files/dirs
+bool sameFileSpecified(const Zstring& file1, const Zstring& file2);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//---------------Inline Implementation---------------------------------------------------
+#ifdef FFS_WIN
+inline
+Utility::FileID::FileID() :
+ dwVolumeSerialNumber(0),
+ nFileIndexHigh(0),
+ nFileIndexLow(0) {}
+
+inline
+Utility::FileID::FileID(DWORD_FFS dwVolumeSN,
+ DWORD_FFS fileIndexHi,
+ DWORD_FFS fileIndexLo) :
+ dwVolumeSerialNumber(dwVolumeSN),
+ nFileIndexHigh(fileIndexHi),
+ nFileIndexLow(fileIndexLo) {}
+
+inline
+bool Utility::FileID::isNull() const
+{
+ return dwVolumeSerialNumber == 0 &&
+ nFileIndexHigh == 0 &&
+ nFileIndexLow == 0;
+}
+
+inline
+bool Utility::FileID::operator==(const FileID& rhs) const
+{
+ return dwVolumeSerialNumber == rhs.dwVolumeSerialNumber &&
+ nFileIndexHigh == rhs.nFileIndexHigh &&
+ nFileIndexLow == rhs.nFileIndexLow;
+}
+
+inline
+bool Utility::FileID::operator<(const FileID& rhs) const
+{
+ if (dwVolumeSerialNumber != rhs.dwVolumeSerialNumber)
+ return dwVolumeSerialNumber < rhs.dwVolumeSerialNumber;
+
+ if (nFileIndexHigh != rhs.nFileIndexHigh)
+ return nFileIndexHigh < rhs.nFileIndexHigh;
+
+ return nFileIndexLow < rhs.nFileIndexLow;
+}
+
+inline
+Utility::FileID::FileID(wxInputStream& stream) //read
+{
+ stream.Read(&dwVolumeSerialNumber, sizeof(dwVolumeSerialNumber));
+ stream.Read(&nFileIndexHigh, sizeof(nFileIndexHigh));
+ stream.Read(&nFileIndexLow, sizeof(nFileIndexLow));
+}
+
+inline
+void Utility::FileID::toStream(wxOutputStream& stream) const //write
+{
+ stream.Write(&dwVolumeSerialNumber, sizeof(dwVolumeSerialNumber));
+ stream.Write(&nFileIndexHigh, sizeof(nFileIndexHigh));
+ stream.Write(&nFileIndexLow, sizeof(nFileIndexLow));
+}
+
+#elif defined FFS_LINUX
+inline
+Utility::FileID::FileID() :
+ deviceId(0),
+ inodeId(0) {}
+
+inline
+Utility::FileID::FileID(dev_t devId,
+ ino_t inId) :
+ deviceId(devId),
+ inodeId(inId) {}
+
+inline
+bool Utility::FileID::isNull() const
+{
+ return deviceId == 0 &&
+ inodeId == 0;
+}
+
+inline
+bool Utility::FileID::operator==(const FileID& rhs) const
+{
+ return deviceId == rhs.deviceId &&
+ inodeId == rhs.inodeId;
+}
+
+inline
+bool Utility::FileID::operator<(const FileID& rhs) const
+{
+ if (deviceId != rhs.deviceId)
+ return deviceId < rhs.deviceId;
+
+ return inodeId < rhs.inodeId;
+}
+
+inline
+Utility::FileID::FileID(wxInputStream& stream) //read
+{
+ stream.Read(&deviceId, sizeof(deviceId));
+ stream.Read(&inodeId, sizeof(inodeId));
+}
+
+inline
+void Utility::FileID::toStream(wxOutputStream& stream) const //write
+{
+ stream.Write(&deviceId, sizeof(deviceId));
+ stream.Write(&inodeId, sizeof(inodeId));
+}
+#endif
+inline
+bool Utility::FileID::operator!=(const FileID& rhs) const
+{
+ return !(*this == rhs);
+}
+
+#endif // FILEID_H_INCLUDED
diff --git a/shared/fileTraverser.cpp b/shared/fileTraverser.cpp
index c323f1a3..7d2615bf 100644
--- a/shared/fileTraverser.cpp
+++ b/shared/fileTraverser.cpp
@@ -6,6 +6,7 @@
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "longPathPrefix.h"
#elif defined FFS_LINUX
#include <sys/stat.h>
@@ -22,7 +23,7 @@ public:
~CloseHandleOnExit()
{
- CloseHandle(fileHandle_);
+ ::CloseHandle(fileHandle_);
}
private:
@@ -65,7 +66,7 @@ inline
bool setWin32FileInformationFromSymlink(const Zstring linkName, FreeFileSync::TraverseCallback::FileInfo& output)
{
//open handle to target of symbolic link
- HANDLE hFile = ::CreateFile(linkName.c_str(),
+ HANDLE hFile = ::CreateFile(FreeFileSync::applyLongPathPrefix(linkName).c_str(),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
@@ -129,8 +130,8 @@ bool traverseDirectory(const Zstring& directory, FreeFileSync::TraverseCallback*
directory + globalFunctions::FILE_NAME_SEPARATOR;
WIN32_FIND_DATA fileMetaData;
- HANDLE searchHandle = FindFirstFile((directoryFormatted + DefaultChar('*')).c_str(), //__in LPCTSTR lpFileName
- &fileMetaData); //__out LPWIN32_FIND_DATA lpFindFileData
+ HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryFormatted + DefaultChar('*')).c_str(), //__in LPCTSTR lpFileName
+ &fileMetaData); //__out LPWIN32_FIND_DATA lpFindFileData
//no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
if (searchHandle == INVALID_HANDLE_VALUE)
@@ -140,8 +141,10 @@ bool traverseDirectory(const Zstring& directory, FreeFileSync::TraverseCallback*
return true;
//else: we have a problem... report it:
- const wxString errorMessage = wxString(_("Error traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"") ;
- switch (sink->onError(errorMessage + wxT("\n\n") + FreeFileSync::getLastErrorFormatted(lastError)))
+ const wxString errorMessage = wxString(_("Error traversing directory:")) + wxT("\n\"") + zToWx(directory) + wxT("\"") + wxT("\n\n") +
+ FreeFileSync::getLastErrorFormatted(lastError);
+
+ switch (sink->onError(errorMessage))
{
case TraverseCallback::TRAVERSING_STOP:
return false;
@@ -205,8 +208,8 @@ bool traverseDirectory(const Zstring& directory, FreeFileSync::TraverseCallback*
}
}
}
- while (FindNextFile(searchHandle, // handle to search
- &fileMetaData)); // pointer to structure for data on found file
+ while (::FindNextFile(searchHandle, // handle to search
+ &fileMetaData)); // pointer to structure for data on found file
const DWORD lastError = ::GetLastError();
if (lastError == ERROR_NO_MORE_FILES)
@@ -362,3 +365,6 @@ void FreeFileSync::traverseFolder(const Zstring& directory,
else
traverseDirectory<false>(directoryFormatted, sink, 0);
}
+
+
+
diff --git a/shared/localization.cpp b/shared/localization.cpp
index ad3cbb99..5f017989 100644
--- a/shared/localization.cpp
+++ b/shared/localization.cpp
@@ -53,7 +53,7 @@ LocalizationInfo::LocalizationInfo()
newEntry.languageID = wxLANGUAGE_SPANISH;
newEntry.languageName = wxT("Español");
newEntry.languageFile = wxT("spanish.lng");
- newEntry.translatorName = wxT("David Rodríguez");
+ newEntry.translatorName = wxT("Alexis Martínez");
newEntry.languageFlag = wxT("spain.png");
locMapping.push_back(newEntry);
@@ -134,6 +134,13 @@ LocalizationInfo::LocalizationInfo()
newEntry.languageFlag = wxT("finland.png");
locMapping.push_back(newEntry);
+ newEntry.languageID = wxLANGUAGE_SWEDISH;
+ newEntry.languageName = wxT("Svenska");
+ newEntry.languageFile = wxT("swedish.lng");
+ newEntry.translatorName = wxT("Ã…ke Engelbrektson");
+ newEntry.languageFlag = wxT("sweden.png");
+ locMapping.push_back(newEntry);
+
newEntry.languageID = wxLANGUAGE_TURKISH;
newEntry.languageName = wxT("Türkçe");
newEntry.languageFile = wxT("turkish.lng");
@@ -230,6 +237,10 @@ int mapLanguageDialect(const int language)
case wxLANGUAGE_SPANISH_VENEZUELA:
return wxLANGUAGE_SPANISH;
+ //variants of wxLANGUAGE_SWEDISH
+ case wxLANGUAGE_SWEDISH_FINLAND:
+ return wxLANGUAGE_SWEDISH;
+
//case wxLANGUAGE_CZECH:
//case wxLANGUAGE_FINNISH:
//case wxLANGUAGE_JAPANESE:
diff --git a/shared/longPathPrefix.cpp b/shared/longPathPrefix.cpp
new file mode 100644
index 00000000..9ce74c8b
--- /dev/null
+++ b/shared/longPathPrefix.cpp
@@ -0,0 +1,95 @@
+#include "longPathPrefix.h"
+#include <boost/scoped_array.hpp>
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "fileError.h"
+#include "systemFunctions.h"
+#include "stringConv.h"
+#include <wx/intl.h>
+
+namespace
+{
+Zstring getFullPathName(const Zstring& relativeName, size_t proposedBufferSize = 1000)
+{
+ using namespace FreeFileSync;
+
+ boost::scoped_array<DefaultChar> fullPath(new DefaultChar[proposedBufferSize]);
+ const DWORD rv = ::GetFullPathName(
+ relativeName.c_str(), //__in LPCTSTR lpFileName,
+ proposedBufferSize, //__in DWORD nBufferLength,
+ fullPath.get(), //__out LPTSTR lpBuffer,
+ NULL); //__out LPTSTR *lpFilePart
+ if (rv == 0 || rv == proposedBufferSize)
+ throw FileError(wxString(_("Error resolving full path name:")) + wxT("\n\"") + zToWx(relativeName) + wxT("\"") +
+ wxT("\n\n") + FreeFileSync::getLastErrorFormatted());
+ if (rv > proposedBufferSize)
+ return getFullPathName(relativeName, rv);
+
+ return fullPath.get();
+}
+}
+
+
+Zstring FreeFileSync::resolveRelativePath(const Zstring& path) //throw()
+{
+ try
+ {
+ return getFullPathName(path);
+ }
+ catch (...)
+ {
+ return path;
+ }
+}
+
+
+//there are two flavors of long path prefix: one for UNC paths, one for regular paths
+const Zstring LONG_PATH_PREFIX = DefaultStr("\\\\?\\");
+const Zstring LONG_PATH_PREFIX_UNC = DefaultStr("\\\\?\\UNC");
+
+template <size_t max_path>
+inline
+Zstring applyLongPathPrefixImpl(const Zstring& path)
+{
+ if ( path.length() >= max_path && //maximum allowed path length without prefix is (MAX_PATH - 1)
+ !path.StartsWith(LONG_PATH_PREFIX))
+ {
+ if (path.StartsWith(DefaultStr("\\\\"))) //UNC-name, e.g. \\zenju-pc\Users
+ return LONG_PATH_PREFIX_UNC + path.AfterFirst(DefaultChar('\\')); //convert to \\?\UNC\zenju-pc\Users
+ else
+ return LONG_PATH_PREFIX + path; //prepend \\?\ prefix
+ }
+
+ //fallback
+ return path;
+}
+
+
+Zstring FreeFileSync::applyLongPathPrefix(const Zstring& path)
+{
+ return applyLongPathPrefixImpl<MAX_PATH>(path);
+}
+
+
+Zstring FreeFileSync::applyLongPathPrefixCreateDir(const Zstring& path) //throw()
+{
+ //special rule for ::CreateDirectoryEx(): MAX_PATH - 12(=^ 8.3 filename) is threshold
+ return applyLongPathPrefixImpl<MAX_PATH - 12>(path);
+}
+
+
+Zstring FreeFileSync::removeLongPathPrefix(const Zstring& path) //throw()
+{
+ if (path.StartsWith(LONG_PATH_PREFIX))
+ {
+ Zstring finalPath = path;
+ if (path.StartsWith(LONG_PATH_PREFIX_UNC)) //UNC-name
+ finalPath.Replace(LONG_PATH_PREFIX_UNC, DefaultStr("\\"), false);
+ else
+ finalPath.Replace(LONG_PATH_PREFIX, DefaultStr(""), false);
+ return finalPath;
+ }
+
+ //fallback
+ return path;
+}
+
diff --git a/shared/longPathPrefix.h b/shared/longPathPrefix.h
new file mode 100644
index 00000000..e4834184
--- /dev/null
+++ b/shared/longPathPrefix.h
@@ -0,0 +1,27 @@
+#ifndef LONGPATHPREFIX_H_INCLUDED
+#define LONGPATHPREFIX_H_INCLUDED
+
+#ifndef FFS_WIN
+use in windows build only!
+#endif
+
+#include "zstring.h"
+
+namespace FreeFileSync
+{
+
+Zstring resolveRelativePath(const Zstring& path); //throw()
+
+//handle filenames longer-equal 260 (== MAX_PATH) characters by applying \\?\-prefix (Reference: http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath)
+/*
+1. path must be absolute
+2. if path is smaller than MAX_PATH nothing is changed!
+3. path may already contain \\?\-prefix
+*/
+Zstring applyLongPathPrefix(const Zstring& path); //throw()
+Zstring applyLongPathPrefixCreateDir(const Zstring& path); //throw() -> special rule for ::CreateDirectoryEx(): MAX_PATH - 12(=^ 8.3 filename) is threshold
+
+Zstring removeLongPathPrefix(const Zstring& path); //throw()
+}
+
+#endif // LONGPATHPREFIX_H_INCLUDED
diff --git a/shared/recycler.cpp b/shared/recycler.cpp
new file mode 100644
index 00000000..b3bb87dd
--- /dev/null
+++ b/shared/recycler.cpp
@@ -0,0 +1,128 @@
+#include "recycler.h"
+#include "dllLoader.h"
+#include <wx/intl.h>
+#include <wx/msw/wrapwin.h> //includes "windows.h"
+#include "buildInfo.h"
+#include "staticAssert.h"
+#include <algorithm>
+#include <functional>
+//#include "../shared/longPathPrefix.h"
+
+const std::wstring& getRecyclerDllName()
+{
+ static const std::wstring filename(
+ Utility::is64BitBuild ?
+ L"Recycler_x64.dll":
+ L"Recycler_Win32.dll");
+
+ assert_static(Utility::is32BitBuild || Utility::is64BitBuild);
+
+ return filename;
+}
+
+
+bool vistaOrLater()
+{
+ OSVERSIONINFO osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ //IFileOperation is supported with Vista and later
+ if (GetVersionEx(&osvi))
+ return osvi.dwMajorVersion > 5;
+ //XP has majorVersion == 5, minorVersion == 1
+ //Vista has majorVersion == 6, minorVersion == 0
+ //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
+ return false;
+}
+
+/*
+Performance test: delete 1000 files
+------------------------------------
+SHFileOperation - single file 33s
+SHFileOperation - multiple files 2,1s
+IFileOperation - single file 33s
+IFileOperation - multiple files 2,1s
+
+=> SHFileOperation and IFileOperation have nearly IDENTICAL performance characteristics!
+
+Nevertheless, let's use IFileOperation for better error reporting!
+*/
+
+void FreeFileSync::moveToWindowsRecycler(const Zstring& fileToDelete) //throw (FileError)
+{
+ std::vector<Zstring> fileNames;
+ fileNames.push_back(fileToDelete);
+ moveToWindowsRecycler(fileNames); //throw (FileError)
+}
+
+
+void FreeFileSync::moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw (FileError)
+{
+ if (filesToDelete.empty())
+ return;
+
+ static const bool useIFileOperation = vistaOrLater();
+
+ if (useIFileOperation) //new recycle bin usage: available since Vista
+ {
+ typedef bool (*MoveToRecycleBinFunc)(
+ const wchar_t* fileNames[],
+ size_t fileNo, //size of fileNames array
+ wchar_t* errorMessage,
+ size_t errorBufferLen);
+
+ static const MoveToRecycleBinFunc moveToRecycler =
+ Utility::loadDllFunction<MoveToRecycleBinFunc>(getRecyclerDllName().c_str(), "moveToRecycleBin");
+
+ if (moveToRecycler == NULL)
+ throw FileError(wxString(_("Could not load a required DLL:")) + wxT(" \"") + getRecyclerDllName().c_str() + wxT("\""));
+
+ //#warning moving long file paths to recycler does not work! clarify!
+// std::vector<Zstring> temp;
+// std::transform(filesToDelete.begin(), filesToDelete.end(),
+// std::back_inserter(temp), std::ptr_fun(FreeFileSync::removeLongPathPrefix)); //::IFileOperation() can't handle \\?\-prefix!
+
+ std::vector<const wchar_t*> fileNames;
+ std::transform(filesToDelete.begin(), filesToDelete.end(),
+ std::back_inserter(fileNames), std::mem_fun_ref(&Zstring::c_str));
+
+ wchar_t errorMessage[2000];
+ if (!(*moveToRecycler)(&fileNames[0], //array must not be empty
+ fileNames.size(),
+ errorMessage,
+ 2000))
+ {
+ throw FileError(wxString(_("Error moving to Recycle Bin:")) + wxT("\n\"") + fileNames[0] + wxT("\"") + //report first file only... better than nothing
+ + wxT("\n\n") +
+ wxT("(") + errorMessage + wxT(")"));
+ }
+ }
+ else //regular recycle bin usage: available since XP
+ {
+ Zstring filenameDoubleNull;
+ for (std::vector<Zstring>::const_iterator i = filesToDelete.begin(); i != filesToDelete.end(); ++i)
+ {
+ //#warning moving long file paths to recycler does not work! clarify!
+ //filenameDoubleNull += removeLongPathPrefix(*i); //::SHFileOperation() can't handle \\?\-prefix!
+ filenameDoubleNull += *i; //::SHFileOperation() can't handle \\?\-prefix!
+ filenameDoubleNull += DefaultChar(0);
+ }
+
+ SHFILEOPSTRUCT fileOp;
+ fileOp.hwnd = NULL;
+ fileOp.wFunc = FO_DELETE;
+ fileOp.pFrom = filenameDoubleNull.c_str();
+ fileOp.pTo = NULL;
+ fileOp.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT | FOF_NOERRORUI;
+ fileOp.fAnyOperationsAborted = false;
+ fileOp.hNameMappings = NULL;
+ fileOp.lpszProgressTitle = NULL;
+
+ if (SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted)
+ {
+ throw FileError(wxString(_("Error moving to Recycle Bin:")) + wxT("\n\"") + filenameDoubleNull.c_str() + wxT("\"")); //report first file only... better than nothing
+ }
+ }
+}
+
diff --git a/shared/recycler.h b/shared/recycler.h
new file mode 100644
index 00000000..14aff4c0
--- /dev/null
+++ b/shared/recycler.h
@@ -0,0 +1,21 @@
+#ifndef RECYCLER_H_INCLUDED
+#define RECYCLER_H_INCLUDED
+
+#include "fileError.h"
+#include "zstring.h"
+#include <vector>
+
+#ifndef FFS_WIN
+use in windows build only!
+#endif
+
+
+namespace FreeFileSync
+{
+//single-file processing
+void moveToWindowsRecycler(const Zstring& fileToDelete); //throw (FileError)
+//multi-file processing: about a factor of 15 faster than single-file
+void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete); //throw (FileError) -> on error reports about first file only!
+}
+
+#endif // RECYCLER_H_INCLUDED
diff --git a/shared/shadow.cpp b/shared/shadow.cpp
index 29eec53b..7e3b34c0 100644
--- a/shared/shadow.cpp
+++ b/shared/shadow.cpp
@@ -2,6 +2,10 @@
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include <wx/intl.h>
#include "systemConstants.h"
+#include "dllLoader.h"
+#include <stdexcept>
+#include "staticAssert.h"
+#include "buildInfo.h"
using FreeFileSync::ShadowCopy;
@@ -12,7 +16,6 @@ bool newerThanXP()
ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- //symbolic links are supported starting with Vista
if (GetVersionEx(&osvi))
return osvi.dwMajorVersion > 5 ||
(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion > 1) ;
@@ -42,93 +45,78 @@ bool runningWOW64() //test if process is running under WOW64 (reference http://m
}
-class ShadowCopy::ShadowlDllHandler //dynamically load windows API functions
+const wxString& getShadowDllName()
{
- typedef bool (*CreateShadowCopyFct)( //volumeName must end with "\", while shadowVolName does not end with "\"
- const wchar_t* volumeName,
- wchar_t* shadowVolName,
- unsigned int shadowBufferLen,
- void** backupHandle,
- wchar_t* errorMessage,
- unsigned int errorBufferLen);
+ /*
+ distinguish a bunch of VSS builds: we use XP and Server 2003 implementations...
+ VSS version and compatibility overview: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx
+ */
- typedef void (*ReleaseShadowCopyFct)(void* backupHandle);
+ static const wxString filename(
+ Utility::is64BitBuild ?
+ (newerThanXP() ?
+ wxT("Shadow_Server2003_x64.dll") :
+ wxT("Shadow_XP_x64.dll")) :
-public:
- static const wxString& getShadowDllName()
- {
- /*
- distinguish a bunch of VSS builds: we use XP and Server 2003 implementations...
- VSS version and compatibility overview: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx
- */
-#if defined _WIN64 //note: _WIN32 is defined for 64-bit compilations, too, while _WIN64 only for 64-bit
- static const wxString filename(newerThanXP() ?
- wxT("Shadow_Server2003_x64.dll") :
- wxT("Shadow_XP_x64.dll"));
-#elif defined(_WIN32)
- static const wxString filename(newerThanXP() ?
- wxT("Shadow_Server2003_win32.dll") :
- wxT("Shadow_XP_win32.dll"));
-#else
- Are we at 128 bit already?
-#endif
- return filename;
- }
+ (newerThanXP() ?
+ wxT("Shadow_Server2003_Win32.dll") :
+ wxT("Shadow_XP_Win32.dll")));
- ShadowlDllHandler() :
- createShadowCopy(NULL),
- releaseShadowCopy(NULL),
- hShadow(NULL)
- {
- //get a handle to the DLL module containing the required functionality
- hShadow = ::LoadLibrary(getShadowDllName().c_str());
- if (hShadow)
- {
- createShadowCopy = reinterpret_cast<CreateShadowCopyFct>(::GetProcAddress(hShadow, "createShadowCopy"));
- releaseShadowCopy = reinterpret_cast<ReleaseShadowCopyFct>(::GetProcAddress(hShadow, "releaseShadowCopy"));
- }
- }
+ assert_static(Utility::is32BitBuild || Utility::is64BitBuild);
- ~ShadowlDllHandler()
- {
- if (hShadow) ::FreeLibrary(hShadow);
- }
-
- CreateShadowCopyFct createShadowCopy;
- ReleaseShadowCopyFct releaseShadowCopy;
+ return filename;
+}
-private:
- HINSTANCE hShadow;
-};
//#############################################################################################################
ShadowCopy::ShadowCopy() :
- backupHandle(NULL)
-{
- shadowDll = new ShadowlDllHandler;
-}
+ backupHandle(NULL) {}
ShadowCopy::~ShadowCopy()
{
if (backupHandle != NULL)
- shadowDll->releaseShadowCopy(backupHandle);
+ {
+ typedef void (*ReleaseShadowCopyFct)(void* backupHandle);
+ static const ReleaseShadowCopyFct releaseShadowCopy =
+ Utility::loadDllFunction<ReleaseShadowCopyFct>(getShadowDllName().c_str(), "releaseShadowCopy");
+
+ if (releaseShadowCopy == NULL)
+ throw std::logic_error("Could not load \"releaseShadowCopy\"!"); //shouldn't arrive here!
- delete shadowDll;
+ releaseShadowCopy(backupHandle);
+ }
}
Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
{
+ typedef bool (*CreateShadowCopyFct)( //volumeName must end with "\", while shadowVolName does not end with "\"
+ const wchar_t* volumeName,
+ wchar_t* shadowVolName,
+ unsigned int shadowBufferLen,
+ void** backupHandle,
+ wchar_t* errorMessage,
+ unsigned int errorBufferLen);
+ static const CreateShadowCopyFct createShadowCopy =
+ Utility::loadDllFunction<CreateShadowCopyFct>(getShadowDllName().c_str(), "createShadowCopy");
+
+
+ typedef void (*ReleaseShadowCopyFct)(void* backupHandle);
+ static const ReleaseShadowCopyFct releaseShadowCopy =
+ Utility::loadDllFunction<ReleaseShadowCopyFct>(getShadowDllName().c_str(), "releaseShadowCopy");
+
+
+
//check if shadow copy dll was loaded correctly
- if ( shadowDll->createShadowCopy == NULL ||
- shadowDll->releaseShadowCopy == NULL)
+ if ( createShadowCopy == NULL ||
+ releaseShadowCopy == NULL)
{
wxString errorMsg = _("Error copying locked file %x!");
errorMsg.Replace(wxT("%x"), wxString(wxT("\"")) + inputFile.c_str() + wxT("\""));
throw FileError(errorMsg + wxT("\n\n") + _("Error starting Volume Shadow Copy Service!") + wxT("\n") +
- _("Could not load a required DLL:") + wxT(" \"") + ShadowlDllHandler::getShadowDllName() + wxT("\""));
+ _("Could not load a required DLL:") + wxT(" \"") + getShadowDllName() + wxT("\""));
}
//VSS does not support running under WOW64 except for Windows XP and Windows Server 2003
@@ -146,7 +134,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
//---------------------------------------------------------------------------------------------------------
wchar_t volumeNameRaw[1000];
- if (!GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName,
+ if (!::GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName,
volumeNameRaw, //__out LPTSTR lpszVolumePathName,
1000)) //__in DWORD cchBufferLength
{
@@ -164,7 +152,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
//release old shadow copy
if (backupHandle != NULL)
{
- shadowDll->releaseShadowCopy(backupHandle);
+ releaseShadowCopy(backupHandle);
backupHandle = NULL;
}
realVolumeLast.clear(); //...if next call fails...
@@ -175,7 +163,7 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
void* backupHandleTmp = NULL;
wchar_t errorMessage[1000];
- if (!shadowDll->createShadowCopy(
+ if (!createShadowCopy(
volumeNameFormatted.c_str(),
shadowVolName,
1000,
@@ -194,7 +182,8 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
backupHandle = backupHandleTmp;
}
- const size_t pos = inputFile.find(volumeNameFormatted);
+ //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found.
+ const size_t pos = inputFile.find(volumeNameFormatted); //inputFile needs NOT to begin with volumeNameFormatted: consider for example \\?\ prefix!
if (pos == Zstring::npos)
{
wxString errorMsg = _("Error copying locked file %x!");
@@ -209,4 +198,3 @@ Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile)
return shadowVolumeLast + Zstring(inputFile.c_str() + pos + volumeNameFormatted.length());
}
-
diff --git a/shared/shadow.h b/shared/shadow.h
index 79e0e59c..78100f78 100644
--- a/shared/shadow.h
+++ b/shared/shadow.h
@@ -23,9 +23,6 @@ private:
ShadowCopy(const ShadowCopy&);
ShadowCopy& operator=(const ShadowCopy&);
- class ShadowlDllHandler;
- const ShadowlDllHandler* shadowDll;
-
Zstring realVolumeLast; //buffer last volume name
Zstring shadowVolumeLast; //buffer last created shadow volume
void* backupHandle;
diff --git a/shared/zstring.cpp b/shared/zstring.cpp
index cb288ea2..c3d5ba8e 100644
--- a/shared/zstring.cpp
+++ b/shared/zstring.cpp
@@ -4,6 +4,7 @@
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include "dllLoader.h"
+#include <boost/scoped_array.hpp>
#endif //FFS_WIN
#ifdef __WXDEBUG__
@@ -45,60 +46,122 @@ AllocationCount& AllocationCount::getInstance()
}
#endif
-
#ifdef FFS_WIN
+bool hasInvariantLocale()
+{
+ OSVERSIONINFO osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ //invariant locale has been introduced with XP
+ if (GetVersionEx(&osvi))
+ return osvi.dwMajorVersion > 5 ||
+ (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion >= 1); //XP has majorVersion == 5, minorVersion == 1
+ //overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
+ return false;
+}
#ifndef LOCALE_INVARIANT
#define LOCALE_INVARIANT 0x007f
#endif
+//warning: LOCALE_INVARIANT is NOT available with Windows 2000, so we have to make yet another distinction...
+namespace
+{
+const LCID invariantLocale = hasInvariantLocale() ?
+ LOCALE_INVARIANT :
+ MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); //see: http://msdn.microsoft.com/en-us/goglobal/bb688122.aspx
+}
+
inline
-int compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount = -1, const int bCount = -1)
+int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size_t sizeB)
{
- //try to call "CompareStringOrdinal" first for low-level string comparison: unfortunately available not before Windows Vista!
+ //try to call "CompareStringOrdinal" for low-level string comparison: unfortunately available not before Windows Vista!
+ //by a factor ~3 faster than old string comparison using "LCMapString"
typedef int (WINAPI *CompareStringOrdinalFunc)(
LPCWSTR lpString1,
int cchCount1,
LPCWSTR lpString2,
int cchCount2,
BOOL bIgnoreCase);
- static const CompareStringOrdinalFunc ordinalCompare = Utility::loadDllFunKernel<CompareStringOrdinalFunc>("CompareStringOrdinal");
+ static const CompareStringOrdinalFunc ordinalCompare = Utility::loadDllFunction<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal");
-
- //we're lucky here! This additional test for "CompareStringOrdinal" has no noticeable performance impact!!
- if (ordinalCompare != NULL)
+ if (ordinalCompare != NULL) //this additional test has no noticeable performance impact
{
const int rv = (*ordinalCompare)(
a, //pointer to first string
- aCount, //size, in bytes or characters, of first string
+ sizeA, //size, in bytes or characters, of first string
b, //pointer to second string
- bCount, //size, in bytes or characters, of second string
+ sizeB, //size, in bytes or characters, of second string
true); //ignore case
-
if (rv == 0)
throw std::runtime_error("Error comparing strings (ordinal)!");
else
return rv - 2; //convert to C-style string compare result
}
- else //fallback to "CompareString". Attention: this function is NOT accurate: for example "weiß" == "weiss"!!!
+ else //fallback
{
- //DON'T use lstrcmpi() here! It uses word sort and is locale dependent!
- //Use CompareString() with "SORT_STRINGSORT" instead!!!
+//do NOT use "CompareString"; this function is NOT accurate (even with LOCALE_INVARIANT and SORT_STRINGSORT): for example "weiß" == "weiss"!!!
+//the only reliable way to compare filenames (with XP) is to call "CharUpper" or "LCMapString":
- const int rv = CompareString(
- LOCALE_INVARIANT, //locale independent
- NORM_IGNORECASE | SORT_STRINGSORT, //comparison-style options
- a, //pointer to first string
- aCount, //size, in bytes or characters, of first string
- b, //pointer to second string
- bCount); //size, in bytes or characters, of second string
+ const size_t minSize = std::min(sizeA, sizeB);
- if (rv == 0)
- throw std::runtime_error("Error comparing strings!");
- else
- return rv - 2; //convert to C-style string compare result
+ if (minSize == 0) //LCMapString does not allow input sizes of 0!
+ return sizeA - sizeB;
+
+ int rv = 0; //always initialize...
+ if (minSize <= 5000) //performance optimization: stack
+ {
+ wchar_t bufferA[5000];
+ wchar_t bufferB[5000];
+
+ if (::LCMapString( //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString()
+ invariantLocale, //__in LCID Locale,
+ LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
+ a, //__in LPCTSTR lpSrcStr,
+ minSize, //__in int cchSrc,
+ bufferA, //__out LPTSTR lpDestStr,
+ 5000 //__in int cchDest
+ ) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString)");
+
+ if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, b, minSize, bufferB, 5000) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString)");
+
+ rv = ::wmemcmp(bufferA, bufferB, minSize);
+ }
+ else //use freestore
+ {
+ boost::scoped_array<wchar_t> bufferA(new wchar_t[minSize]);
+ boost::scoped_array<wchar_t> bufferB(new wchar_t[minSize]);
+
+ if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, a, minSize, bufferA.get(), minSize) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
+
+ if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, b, minSize, bufferB.get(), minSize) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
+
+ rv = ::wmemcmp(bufferA.get(), bufferB.get(), minSize);
+ }
+
+ return rv == 0 ?
+ sizeA - sizeB :
+ rv;
}
+
+// const int rv = CompareString(
+// invariantLocale, //locale independent
+// NORM_IGNORECASE | SORT_STRINGSORT, //comparison-style options
+// a, //pointer to first string
+// aCount, //size, in bytes or characters, of first string
+// b, //pointer to second string
+// bCount); //size, in bytes or characters, of second string
+//
+// if (rv == 0)
+// throw std::runtime_error("Error comparing strings!");
+// else
+// return rv - 2; //convert to C-style string compare result
}
#endif
@@ -106,13 +169,13 @@ int compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount = -
#ifdef FFS_WIN
int Zstring::CmpNoCase(const DefaultChar* other) const
{
- return ::compareStringsWin32(c_str(), other); //way faster than wxString::CmpNoCase()!!
+ return ::compareFilenamesWin32(c_str(), other, length(), ::wcslen(other)); //way faster than wxString::CmpNoCase()
}
int Zstring::CmpNoCase(const Zstring& other) const
{
- return ::compareStringsWin32(c_str(), other.c_str(), length(), other.length()); //way faster than wxString::CmpNoCase()!!
+ return ::compareFilenamesWin32(c_str(), other.c_str(), length(), other.length()); //way faster than wxString::CmpNoCase()
}
#endif
@@ -277,14 +340,17 @@ std::vector<Zstring> Zstring::Tokenize(const DefaultChar delimiter) const
#ifdef FFS_WIN
-Zstring& Zstring::MakeLower()
+Zstring& Zstring::MakeUpper()
{
const size_t thisLen = length();
if (thisLen == 0)
return *this;
reserve(thisLen); //make unshared
- ::CharLower(data()); //use Windows' lower case conversion
+
+ //use Windows' upper case conversion: faster than ::CharUpper()
+ if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, data(), thisLen, data(), thisLen) == 0)
+ throw std::runtime_error("Error converting to upper case! (LCMapString)");
return *this;
}
@@ -484,3 +550,4 @@ void Zstring::reserve(size_t capacityNeeded) //make unshared and check capacity
descr->capacity = newCapacity;
}
}
+
diff --git a/shared/zstring.h b/shared/zstring.h
index d0be30bf..cb047e15 100644
--- a/shared/zstring.h
+++ b/shared/zstring.h
@@ -52,7 +52,7 @@ public:
#ifdef FFS_WIN
int CmpNoCase(const DefaultChar* other) const;
int CmpNoCase(const Zstring& other) const;
- Zstring& MakeLower();
+ Zstring& MakeUpper();
#endif
int Cmp(const DefaultChar* other) const;
int Cmp(const Zstring& other) const;
@@ -134,6 +134,28 @@ template <class T>
Zstring numberToZstring(const T& number); //convert number to Zstring
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
//#######################################################################################
//begin of implementation
bgstack15