summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:17:51 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:17:51 +0200
commit237aedc590b58c0e69d7dfcac92b5f767b7c004a (patch)
tree83f361a82ba483f2daf83b677e8685cd953812d9 /zen
parent4.5 (diff)
downloadFreeFileSync-237aedc590b58c0e69d7dfcac92b5f767b7c004a.tar.gz
FreeFileSync-237aedc590b58c0e69d7dfcac92b5f767b7c004a.tar.bz2
FreeFileSync-237aedc590b58c0e69d7dfcac92b5f767b7c004a.zip
4.6
Diffstat (limited to 'zen')
-rw-r--r--zen/FindFilePlus/find_file_plus.cpp127
-rw-r--r--zen/FindFilePlus/find_file_plus.h15
-rw-r--r--zen/basic_math.h2
-rw-r--r--zen/debug_log.h2
-rw-r--r--zen/deprecate.h1
-rw-r--r--zen/dst_hack.cpp2
-rw-r--r--zen/file_handling.cpp50
-rw-r--r--zen/file_handling.h16
-rw-r--r--zen/file_id.cpp2
-rw-r--r--zen/file_traverser.cpp143
-rw-r--r--zen/file_traverser.h4
-rw-r--r--zen/fixed_list.h4
-rw-r--r--zen/i18n.h20
-rw-r--r--zen/int64.h4
-rw-r--r--zen/privilege.cpp2
-rw-r--r--zen/string_base.h11
-rw-r--r--zen/symlink_target.h2
-rw-r--r--zen/zstring.cpp68
18 files changed, 276 insertions, 199 deletions
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp
index e613177b..876d0c0f 100644
--- a/zen/FindFilePlus/find_file_plus.cpp
+++ b/zen/FindFilePlus/find_file_plus.cpp
@@ -110,16 +110,12 @@ public:
FileSearcher(const wchar_t* dirname); //throw FileError
~FileSearcher();
- void readDir(FileInformation& output) { (this->*readDirFun)(output); } //throw FileError
-
- bool tryFallbackToDefaultQuery(); //call if readDir() throws STATUS_NOT_SUPPORTED or similar
+ void readDir(FileInformation& output); //throw FileError
private:
template <class QueryPolicy> void readDirImpl(FileInformation& output); //throw FileError
- //handle fallback, if retrieving file id is not supported by file system layer
- void (FileSearcher::*readDirFun)(FileInformation& output);
- UNICODE_STRING ntPathName; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()!
+ UNICODE_STRING dirnameNt; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()!
HANDLE hDir;
ULONG nextEntryOffset; //!= 0 if entry is waiting in buffer
@@ -132,47 +128,13 @@ private:
};
-namespace
-{
-/*
-Common C-style policy handling directory traversal:
-
-struct QueryPolicy
-{
- typedef ... RawFileInfo;
- static const FILE_INFORMATION_CLASS fileInformationClass = ...;
- static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo);
-};
-*/
-
-struct DirQueryDefault
-{
- typedef FILE_BOTH_DIR_INFORMATION RawFileInfo;
- static const FILE_INFORMATION_CLASS fileInformationClass = FileBothDirectoryInformation;
- static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo) { fileInfo.fileId.QuadPart = 0; }
-};
-
-struct DirQueryFileId
-{
- typedef FILE_ID_BOTH_DIR_INFORMATION RawFileInfo;
- static const FILE_INFORMATION_CLASS fileInformationClass = FileIdBothDirectoryInformation;
- static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo)
- {
- fileInfo.fileId.QuadPart = rawInfo.FileId.QuadPart; //may be 0 even in this context, e.g. for mapped FTP drive!
- static_assert(sizeof(fileInfo.fileId) == sizeof(rawInfo.FileId), "dang!");
- }
-};
-}
-
-
FileSearcher::FileSearcher(const wchar_t* dirname) :
hDir(NULL),
- nextEntryOffset(0),
- readDirFun(&FileSearcher::readDirImpl<DirQueryFileId>) //start optimistically
+ nextEntryOffset(0)
{
- ntPathName.Buffer = NULL;
- ntPathName.Length = 0;
- ntPathName.MaximumLength = 0;
+ dirnameNt.Buffer = NULL;
+ dirnameNt.Length = 0;
+ dirnameNt.MaximumLength = 0;
zen::ScopeGuard guardConstructor = zen::makeGuard([&]() { this->~FileSearcher(); });
//--------------------------------------------------------------------------------------------------------------
@@ -184,19 +146,19 @@ FileSearcher::FileSearcher(const wchar_t* dirname) :
//NOTE: RtlDosPathNameToNtPathName_U may be used on all XP/Win7/Win8 for compatibility
// RtlDosPathNameToNtPathName_U: used by Windows XP available with OS version 3.51 (Windows NT) and higher
// RtlDosPathNameToRelativeNtPathName_U: used by Win7/Win8 available with OS version 5.2 (Windows Server 2003) and higher
- if (!rtlDosPathNameToNtPathName_U(dirname, //__in dosFileName,
- &ntPathName, //__out ntFileName,
- NULL, //__out_optFilePart,
- NULL)) //__out_opt relativeName - empty if dosFileName is absolute
+ if (!rtlDosPathNameToNtPathName_U(dirname, //__in dosFileName,
+ &dirnameNt, //__out ntFileName,
+ NULL, //__out_optFilePart,
+ NULL)) //__out_opt relativeName - empty if dosFileName is absolute
throw NtFileError(STATUS_OBJECT_PATH_NOT_FOUND); //translates to ERROR_PATH_NOT_FOUND, same behavior like ::FindFirstFileEx()
+
OBJECT_ATTRIBUTES objAttr = {};
InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes,
- &ntPathName, //[in] PUNICODE_STRING objectName,
+ &dirnameNt, //[in] PUNICODE_STRING objectName,
OBJ_CASE_INSENSITIVE, //[in] ULONG attributes,
NULL, //[in] HANDLE rootDirectory,
NULL); //[in, optional] PSECURITY_DESCRIPTOR securityDescriptor
-
{
IO_STATUS_BLOCK status = {};
NTSTATUS rv = ntOpenFile(&hDir, //__out PHANDLE FileHandle,
@@ -220,28 +182,49 @@ FileSearcher::~FileSearcher()
if (hDir)
ntClose(hDir);
- if (ntPathName.Buffer)
- rtlFreeUnicodeString(&ntPathName); //cleanup identical to ::FindFirstFile()
+ if (dirnameNt.Buffer)
+ rtlFreeUnicodeString(&dirnameNt); //cleanup identical to ::FindFirstFile()
//note that most of this function seems inlined by the linker, so that its assembly looks equivalent to "RtlFreeHeap(GetProcessHeap(), 0, ntPathName.Buffer)"
}
-inline
-bool FileSearcher::tryFallbackToDefaultQuery()
+namespace
{
- if (readDirFun == &FileSearcher::readDirImpl<DirQueryDefault>)
- return false; //already default
+/*
+Common C-style policy handling directory traversal:
- //Note: NtQueryDirectoryFile() may not respect "restartScan" for some weird Win2000 file system drivers, so we won't bother
- //Samba before v3.0.22 (Mar 30, 2006) seems to have a bug which sucessfully returns 128 elements via NtQueryDirectoryFile()
- //and FileIdBothDirectoryInformation, then fails with STATUS_INVALID_LEVEL.
- //Although traversal is NOT finished yet, it will further return STATUS_NO_MORE_FILES, even if falling back to FileBothDirectoryInformation!!!
+struct QueryPolicy
+{
+ typedef ... RawFileInfo;
+ static const FILE_INFORMATION_CLASS fileInformationClass = ...;
+ static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo);
+};
+*/
- readDirFun = &FileSearcher::readDirImpl<DirQueryDefault>;
- return true;
+struct DirQueryDefault //as implemented in Win32 FindFirstFile()/FindNextFile()
+{
+ typedef FILE_BOTH_DIR_INFORMATION RawFileInfo;
+ static const FILE_INFORMATION_CLASS fileInformationClass = FileBothDirectoryInformation;
+ static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo) { fileInfo.fileId.QuadPart = 0; }
+};
+
+struct DirQueryFileId
+{
+ typedef FILE_ID_BOTH_DIR_INFORMATION RawFileInfo;
+ static const FILE_INFORMATION_CLASS fileInformationClass = FileIdBothDirectoryInformation;
+ static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo)
+ {
+ fileInfo.fileId.QuadPart = rawInfo.FileId.QuadPart; //may be 0 even in this context, e.g. for mapped FTP drive!
+ static_assert(sizeof(fileInfo.fileId) == sizeof(rawInfo.FileId), "dang!");
+ }
+};
}
+inline
+void FileSearcher::readDir(FileInformation& output) { readDirImpl<DirQueryFileId>(output); } //throw FileError
+
+
template <class QueryPolicy>
void FileSearcher::readDirImpl(FileInformation& output) //throw FileError
{
@@ -283,9 +266,9 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw FileError
if (!NT_SUCCESS(rv))
{
if (rv == STATUS_NO_SUCH_FILE) //harmonize ntQueryDirectoryFile() error handling! failure to find a file on first call returns STATUS_NO_SUCH_FILE,
- rv = STATUS_NO_MORE_FILES; //while on subsequent accesses return STATUS_NO_MORE_FILES
+ rv = STATUS_NO_MORE_FILES; //while on subsequent accesses returns STATUS_NO_MORE_FILES
//note: not all directories contain "., .." entries! E.g. a drive's root directory or NetDrive + ftp.gnu.org\CRYPTO.README"
- //-> addon: this is NOT a directory, it looks like on in NetDrive, but it's a file in Opera
+ //-> addon: this is NOT a directory, it looks like one in NetDrive, but it's a file in Opera
throw NtFileError(rv); //throws STATUS_NO_MORE_FILES when finished
}
@@ -353,22 +336,6 @@ bool findplus::readDir(FindHandle hnd, FileInformation& output)
}
catch (const NtFileError& e)
{
- /*
- fallback to default directory query method, if FileIdBothDirectoryInformation is not properly implemented
- this is required for NetDrive mounted Webdav, e.g. www.box.net and NT4, 2000 remote drives, et al.
- */
- if (e.ntError != STATUS_NO_MORE_FILES)
- if (e.ntError == STATUS_INVALID_LEVEL ||
- e.ntError == STATUS_NOT_SUPPORTED ||
- e.ntError == STATUS_INVALID_PARAMETER ||
- e.ntError == STATUS_INVALID_NETWORK_RESPONSE ||
- e.ntError == STATUS_INVALID_INFO_CLASS ||
- e.ntError == STATUS_ACCESS_VIOLATION) //FileIdBothDirectoryInformation on XP accessing UDF
- {
- if (hnd->tryFallbackToDefaultQuery())
- return readDir(hnd, output); //implementation of tryFallbackToDefaultQuery() promises, that we don't land in an endless recursion here!
- }
-
setWin32Error(rtlNtStatusToDosError(e.ntError));
return false;
}
diff --git a/zen/FindFilePlus/find_file_plus.h b/zen/FindFilePlus/find_file_plus.h
index 33e9a178..2ef5affe 100644
--- a/zen/FindFilePlus/find_file_plus.h
+++ b/zen/FindFilePlus/find_file_plus.h
@@ -38,7 +38,7 @@ struct FileInformation
DWORD fileAttributes;
DWORD shortNameLength;
WCHAR shortName[MAX_PATH + 1]; //shortName is 0-terminated
-}; //no need for #pragma pack -> all members already starting at 4 byte boundary!
+}; //no need for #pragma pack -> all members are perfectly 4, 8 byte aligned!
class FileSearcher;
typedef FileSearcher* FindHandle;
@@ -49,6 +49,19 @@ FindHandle openDir(const wchar_t* dirname); //returns NULL on error, call ::GetL
DLL_FUNCTION_DECLARATION
bool readDir(FindHandle hnd, FileInformation& output); //returns false on error or if there are no more files; ::GetLastError() returns ERROR_NO_MORE_FILES in this case
+/*
+warning:; this may also return file system implementation dependent error codes like ERROR_NOT_SUPPORTED, ERROR_INVALID_LEVEL,
+ect. if "FileIdBothDirectoryInformation" is not supported! We need a fallback:
+
+ sometimes it's *not* sufficient to use fallback for NtQueryDirectoryFile() alone, we need to reset "hDir", since it may be f$ck$d $p by some poor file system layer implementation:
+ - Samba before v3.0.22 (Mar 30, 2006) seems to have a bug which sucessfully returns 128 elements via NtQueryDirectoryFile() and FileIdBothDirectoryInformation,
+ then fails with STATUS_INVALID_LEVEL. Fallback to FileBothDirectoryInformation will return STATUS_NO_MORE_FILES, even if there *are* more files
+ - NtQueryDirectoryFile() may *not* respect "restartScan" for some weird Win2000 file system drivers, so we cannot rely on this as a replacement for a "hDir" reset
+ - Windows 7 Remote Desktop sharing does not work unless "hDir" is reset!
+ => let's assume worst case in general and do a reset!
+ perf note: implementing this reset at a folder level is possible, but a huge perf-killer (additional open/close handle), therefore fallback must apply to a complete folder (sub-)tree!
+ => caller needs to handle this and implement FindFirstFile()/FindNextFile() fallback!
+*/
DLL_FUNCTION_DECLARATION
void closeDir(FindHandle hnd);
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 606d90ad..f4f46ce0 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -304,7 +304,7 @@ template <class InputIterator> inline
double stdDeviation(InputIterator first, InputIterator last, double* arithMean)
{
//implementation minimizing rounding errors, see: http://en.wikipedia.org/wiki/Standard_deviation
- //combined with techinque avoiding overflow, see: http://www.netlib.org/blas/dnrm2.f -> only 10% performance degregation
+ //combined with techinque avoiding overflow, see: http://www.netlib.org/blas/dnrm2.f -> only 10% performance degradation
size_t n = 0;
double mean = 0;
diff --git a/zen/debug_log.h b/zen/debug_log.h
index bd9af25f..e7116156c 100644
--- a/zen/debug_log.h
+++ b/zen/debug_log.h
@@ -55,7 +55,7 @@ public:
int sourceRow,
const std::string& message)
{
- const std::string logEntry = "[" + formatTime<std::string>(FORMAT_TIME()) + "] " + afterLast(sourceFile, ZEN_FILE_NAME_SEPARATOR) +
+ const std::string logEntry = "[" + formatTime<std::string>(FORMAT_TIME) + "] " + afterLast(sourceFile, ZEN_FILE_NAME_SEPARATOR) +
", line " + toString<std::string>(sourceRow) + ": " + message + "\n";
const size_t bytesWritten = ::fwrite(logEntry.c_str(), 1, logEntry.size(), handle);
diff --git a/zen/deprecate.h b/zen/deprecate.h
index 3481a062..b045d3c0 100644
--- a/zen/deprecate.h
+++ b/zen/deprecate.h
@@ -7,6 +7,7 @@
#ifndef DEPRECATE_HEADER_2348970348
#define DEPRECATE_HEADER_2348970348
+//compiler macros: http://predef.sourceforge.net/precomp.html
#ifdef __GNUC__
#define ZEN_DEPRECATE __attribute__ ((deprecated))
diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp
index e4f48c2f..ac5774d0 100644
--- a/zen/dst_hack.cpp
+++ b/zen/dst_hack.cpp
@@ -94,7 +94,7 @@ bool dst::isFatDrive(HANDLE hFile) //throw()
LPWSTR lpFileSystemNameBuffer,
DWORD nFileSystemNameSize);
- const DllFun<GetVolumeInformationByHandleWFunc> getVolumeInformationByHandle(L"kernel32.dll", "GetVolumeInformationByHandleW");
+ const SysDllFun<GetVolumeInformationByHandleWFunc> getVolumeInformationByHandle(L"kernel32.dll", "GetVolumeInformationByHandleW");
if (!getVolumeInformationByHandle)
{
assert(false);
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index d6804e34..334128d9 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -505,13 +505,19 @@ void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw Fil
class CopyCallbackImpl : public zen::CallbackCopyFile //callback functionality
{
public:
- CopyCallbackImpl(const Zstring& sourceFile, CallbackMoveFile& callback) : sourceFile_(sourceFile), moveCallback(callback) {}
+ CopyCallbackImpl(const Zstring& sourceFile,
+ const Zstring& targetFile,
+ CallbackMoveFile& callback) : sourceFile_(sourceFile),
+ targetFile_(targetFile),
+ moveCallback(callback) {}
virtual void deleteTargetFile(const Zstring& targetFile) { assert(!fileExists(targetFile)); }
virtual void updateCopyStatus(UInt64 totalBytesTransferred)
{
- moveCallback.requestUiRefresh(sourceFile_);
+ const Int64 delta = to<Int64>(totalBytesTransferred) - bytesReported;
+ moveCallback.updateStatus(delta);
+ bytesReported += delta;
}
private:
@@ -519,15 +525,15 @@ private:
CopyCallbackImpl& operator=(const CopyCallbackImpl&);
const Zstring sourceFile_;
+ const Zstring targetFile_;
CallbackMoveFile& moveCallback;
+ Int64 bytesReported;
};
void zen::moveFile(const Zstring& sourceFile, const Zstring& targetFile, bool ignoreExisting, CallbackMoveFile* callback) //throw FileError;
{
- //call back once per file (moveFile() is called by moveDirectory())
- if (callback)
- callback->requestUiRefresh(sourceFile);
+ if (callback) callback->onBeforeFileMove(sourceFile, targetFile); //call back once *after* work was done
const bool targetExisting = fileExists(targetFile);
@@ -541,24 +547,28 @@ void zen::moveFile(const Zstring& sourceFile, const Zstring& targetFile, bool ig
try
{
renameFile(sourceFile, targetFile); //throw FileError, ErrorDifferentVolume
- return; //great, we get away cheaply!
+ //great, we get away cheaply!
+ if (callback) callback->objectProcessed();
+ return;
}
//if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the file)
catch (const ErrorDifferentVolume&) {}
//file is on a different volume: let's copy it
- std::unique_ptr<CopyCallbackImpl> copyCallback(callback != NULL ? new CopyCallbackImpl(sourceFile, *callback) : NULL);
-
if (symlinkExists(sourceFile))
copySymlink(sourceFile, targetFile, false); //throw FileError; don't copy filesystem permissions
else
+ {
+ std::unique_ptr<CopyCallbackImpl> copyCallback(callback != NULL ? new CopyCallbackImpl(sourceFile, targetFile, *callback) : NULL);
copyFile(sourceFile, targetFile, false, true, copyCallback.get()); //throw FileError;
+ }
//attention: if copy-operation was cancelled an exception is thrown => sourcefile is not deleted, as we wish!
}
removeFile(sourceFile); //throw FileError
//note: copying file is NOT undone in case of exception: currently this function is called in context of user-defined deletion dir, where this behavior is fine
+ if (callback) callback->objectProcessed();
}
namespace
@@ -605,19 +615,15 @@ private:
struct RemoveCallbackImpl : public CallbackRemoveDir
{
- RemoveCallbackImpl(const Zstring& sourceDir,
- CallbackMoveFile& moveCallback) :
- sourceDir_(sourceDir),
- moveCallback_(moveCallback) {}
+ RemoveCallbackImpl(CallbackMoveFile& moveCallback) : moveCallback_(moveCallback) {}
- virtual void notifyFileDeletion(const Zstring& filename) { moveCallback_.requestUiRefresh(sourceDir_); }
- virtual void notifyDirDeletion (const Zstring& dirname ) { moveCallback_.requestUiRefresh(sourceDir_); }
+ virtual void notifyFileDeletion(const Zstring& filename) { moveCallback_.updateStatus(0); }
+ virtual void notifyDirDeletion (const Zstring& dirname ) { moveCallback_.updateStatus(0); }
private:
RemoveCallbackImpl(const RemoveCallbackImpl&);
RemoveCallbackImpl& operator=(const RemoveCallbackImpl&);
- const Zstring sourceDir_;
CallbackMoveFile& moveCallback_;
};
}
@@ -625,9 +631,7 @@ private:
void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool ignoreExisting, CallbackMoveFile* callback) //throw FileError
{
- //call back once per folder
- if (callback)
- callback->requestUiRefresh(sourceDir);
+ if (callback) callback->onBeforeDirMove(sourceDir, targetDir); //call back once *after* work was done
const bool targetExisting = dirExists(targetDir);
@@ -643,7 +647,9 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool
try
{
renameFile(sourceDir, targetDir); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
- return; //great, we get away cheaply!
+ //great, we get away cheaply!
+ if (callback) callback->objectProcessed();
+ return;
}
//if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the directory)
catch (const ErrorDifferentVolume&) {}
@@ -681,8 +687,10 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool
}
//delete source
- std::unique_ptr<RemoveCallbackImpl> removeCallback(callback != NULL ? new RemoveCallbackImpl(sourceDir, *callback) : NULL);
+ std::unique_ptr<RemoveCallbackImpl> removeCallback(callback != NULL ? new RemoveCallbackImpl(*callback) : NULL);
removeDirectory(sourceDir, removeCallback.get()); //throw FileError;
+
+ if (callback) callback->objectProcessed();
}
@@ -1458,7 +1466,7 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
if source is a symlink and COPY_FILE_COPY_SYMLINK is NOT specified, this callback is called and hSourceFile is a handle to the *target* of the link!
file time handling:
- ::CopyFileEx() will copy file modification time (only) over from source file AFTER the last inocation of this callback
+ ::CopyFileEx() will copy file modification time (only) over from source file AFTER the last invokation of this callback
=> it is possible to adapt file creation time of target in here, but NOT file modification time!
alternate data stream handling:
diff --git a/zen/file_handling.h b/zen/file_handling.h
index 8ffe38d0..e6819322 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -47,7 +47,7 @@ void setFileTime(const Zstring& filename, const Int64& modificationTime, ProcSym
UInt64 getFilesize(const Zstring& filename); //throw FileError
//file handling
-bool removeFile(const Zstring& filename); //return "true" if file was actually deleted; throw (FileError)
+bool removeFile(const Zstring& filename); //return "true" if file was actually deleted; throw FileError
void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = NULL); //throw FileError
@@ -93,8 +93,8 @@ void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copy
struct CallbackRemoveDir
{
virtual ~CallbackRemoveDir() {}
- virtual void notifyFileDeletion(const Zstring& filename) = 0;
- virtual void notifyDirDeletion(const Zstring& dirname) = 0;
+ virtual void notifyFileDeletion(const Zstring& filename) = 0; //one call for each (existing) object!
+ virtual void notifyDirDeletion (const Zstring& dirname ) = 0; //
};
@@ -115,8 +115,14 @@ struct CallbackCopyFile //callback functionality
struct CallbackMoveFile //callback functionality
{
- virtual ~CallbackMoveFile() {}
- virtual void requestUiRefresh(const Zstring& currentObject) = 0; //see CallbackCopyFile!
+ virtual ~CallbackMoveFile() {} //see CallbackCopyFile for limitations when trowing exceptions!
+
+ virtual void onBeforeFileMove(const Zstring& fileFrom, const Zstring& fileTo) = 0; //one call before each (planned) move
+ virtual void onBeforeDirMove (const Zstring& dirFrom, const Zstring& dirTo ) = 0; //
+ virtual void objectProcessed() = 0; //one call after each completed move (count objects total)
+
+ //called frequently if move has to revert to copy + delete:
+ virtual void updateStatus(Int64 bytesDelta) = 0;
};
}
diff --git a/zen/file_id.cpp b/zen/file_id.cpp
index 7527062b..fa05a142 100644
--- a/zen/file_id.cpp
+++ b/zen/file_id.cpp
@@ -27,7 +27,7 @@ zen::FileId zen::getFileID(const Zstring& filename)
const HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(filename).c_str(),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- 0,
+ NULL,
OPEN_EXISTING,
FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, //FILE_FLAG_BACKUP_SEMANTICS needed to open a directory
NULL);
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 236581a3..0fb8a332 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -16,6 +16,7 @@
#include "file_update_handle.h"
#include "dll.h"
#include "FindFilePlus/find_file_plus.h"
+#include <zen/win_ver.h>
#elif defined FFS_LINUX
#include <sys/stat.h>
@@ -36,7 +37,7 @@ bool tryReportingError(Command cmd, zen::TraverseCallback& callback) //return "t
for (;;)
try
{
- cmd();
+ cmd(); //throw FileError
return true;
}
catch (const FileError& e)
@@ -47,9 +48,9 @@ bool tryReportingError(Command cmd, zen::TraverseCallback& callback) //return "t
break;
case TraverseCallback::TRAV_ERROR_IGNORE:
return false;
- default:
- assert(false);
- break;
+ //default:
+ // assert(false);
+ //break;
}
}
}
@@ -78,14 +79,54 @@ bool extractFileInfoFromSymlink(const Zstring& linkName, zen::TraverseCallback::
//write output
output.fileSize = zen::UInt64(fileInfoByHandle.nFileSizeLow, fileInfoByHandle.nFileSizeHigh);
output.lastWriteTimeRaw = toTimeT(fileInfoByHandle.ftLastWriteTime);
- //output.id = extractFileID(fileInfoByHandle); -> id from dereferenced symlink is problematic, since renaming will consider the link, not the target!
+ output.id = FileId(); //= extractFileID(fileInfoByHandle); -> id from dereferenced symlink is problematic, since renaming will consider the link, not the target!
return true;
}
-DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
+DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if serial is not supported!
+{
+ //this works for:
+ //- root paths "C:\", "D:\"
+ //- network shares: \\share\dirname
+ //- indirection: subst S: %USERPROFILE%
+ // -> GetVolumePathName() on the other hand resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try...
+
+ //dynamically load windows API function (existing since Windows XP)
+ typedef BOOL (WINAPI *GetFileInformationByHandleFunc)(HANDLE hFile,
+ LPBY_HANDLE_FILE_INFORMATION lpFileInformation);
+
+ const SysDllFun<GetFileInformationByHandleFunc> getFileInformationByHandle(L"kernel32.dll", "GetFileInformationByHandle");
+ if (!getFileInformationByHandle)
+ {
+ assert(false);
+ return 0;
+ }
+
+ const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(pathName).c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS /*needed to open a directory*/ /*| FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!*/ ,
+ NULL);
+ if (hDir == INVALID_HANDLE_VALUE)
+ return 0;
+ ZEN_ON_BLOCK_EXIT(::CloseHandle(hDir));
+
+ BY_HANDLE_FILE_INFORMATION fileInfo = {};
+ if (!getFileInformationByHandle(hDir, //__in HANDLE hFile,
+ &fileInfo)) //__out LPBY_HANDLE_FILE_INFORMATION lpFileInformation
+ return 0;
+
+ return fileInfo.dwVolumeSerialNumber;
+}
+
+
+/*
+DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error!
{
- //note: this even works for network shares: \\share\dirname
+ //note: this works for network shares: \\share\dirname, but not "subst"!
const DWORD BUFFER_SIZE = 10000;
std::vector<wchar_t> buffer(BUFFER_SIZE);
@@ -113,11 +154,14 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
return volumeSerial;
}
+*/
+
+const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found"
-const DllFun<findplus::OpenDirFunc> openDir (findplus::getDllName(), findplus::openDirFuncName ); //
-const DllFun<findplus::ReadDirFunc> readDir (findplus::getDllName(), findplus::readDirFuncName ); //load at startup: avoid pre C++11 static initialization MT issues
-const DllFun<findplus::CloseDirFunc> closeDir(findplus::getDllName(), findplus::closeDirFuncName); //
+const DllFun<findplus::OpenDirFunc> openDir = isXpOrLater ? DllFun<findplus::OpenDirFunc >(findplus::getDllName(), findplus::openDirFuncName ) : DllFun<findplus::OpenDirFunc >(); //
+const DllFun<findplus::ReadDirFunc> readDir = isXpOrLater ? DllFun<findplus::ReadDirFunc >(findplus::getDllName(), findplus::readDirFuncName ) : DllFun<findplus::ReadDirFunc >(); //load at startup: avoid pre C++11 static initialization MT issues
+const DllFun<findplus::CloseDirFunc> closeDir= isXpOrLater ? DllFun<findplus::CloseDirFunc>(findplus::getDllName(), findplus::closeDirFuncName) : DllFun<findplus::CloseDirFunc>(); //
/*
Common C-style interface for Win32 FindFirstFile(), FindNextFile() and FileFilePlus openDir(), closeDir():
@@ -125,12 +169,15 @@ struct TraverserPolicy //see "policy based design"
{
typedef ... DirHandle;
typedef ... FindData;
+
static void create(const Zstring& directory, DirHandle& hnd); //throw FileError - *no* concession to FindFirstFile(): open handle only, *no* return of data!
static void destroy(DirHandle hnd); //throw()
-static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+
+template <class FallbackFun>
+static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo, FallbackFun fb) //throw FileError -> fb: fallback to FindFirstFile()/FindNextFile()
//FindData "member" functions
-static void extractFileInfo (const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output);
+static void extractFileInfo (const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output); //volumeSerial may be 0 if not available!
static Int64 getModTime (const FindData& fileInfo);
static const FILETIME& getModTimeRaw (const FindData& fileInfo); //yet another concession to DST hack
static const FILETIME& getCreateTimeRaw(const FindData& fileInfo); //
@@ -177,7 +224,8 @@ struct Win32Traverser
static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw()
- static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+ template <class FallbackFun>
+ static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo, FallbackFun) //throw FileError
{
if (hnd.firstRead)
{
@@ -197,10 +245,11 @@ struct Win32Traverser
}
template <class FindData>
- static void extractFileInfo(const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output)
+ static void extractFileInfo(const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output)
{
- output.lastWriteTimeRaw = getModTime(fileInfo);
output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
+ output.lastWriteTimeRaw = getModTime(fileInfo);
+ output.id = FileId();
}
template <class FindData>
@@ -243,12 +292,40 @@ struct FilePlusTraverser
static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw()
- static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+ template <class FallbackFun>
+ static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo, FallbackFun fb) //throw FileError
{
if (!::readDir(hnd.searchHandle, fileInfo))
{
- if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
+ const DWORD lastError = ::GetLastError();
+ if (lastError == ERROR_NO_MORE_FILES) //not an error situation
return false;
+
+ /*
+ fallback to default directory query method, if FileIdBothDirectoryInformation is not properly implemented
+ this is required for NetDrive mounted Webdav, e.g. www.box.net and NT4, 2000 remote drives, et al.
+
+ NT status code | Win32 error code
+ -----------------------------------------------------------
+ STATUS_INVALID_LEVEL | ERROR_INVALID_LEVEL
+ STATUS_NOT_SUPPORTED | ERROR_NOT_SUPPORTED
+ STATUS_INVALID_PARAMETER | ERROR_INVALID_PARAMETER
+ STATUS_INVALID_NETWORK_RESPONSE | ERROR_BAD_NET_RESP
+ STATUS_INVALID_INFO_CLASS | ERROR_INVALID_PARAMETER
+ STATUS_UNSUCCESSFUL | ERROR_GEN_FAILURE
+ STATUS_ACCESS_VIOLATION | ERROR_NOACCESS ->FileIdBothDirectoryInformation on XP accessing UDF
+ */
+ if (lastError == ERROR_INVALID_LEVEL ||
+ lastError == ERROR_NOT_SUPPORTED ||
+ lastError == ERROR_INVALID_PARAMETER ||
+ lastError == ERROR_BAD_NET_RESP ||
+ lastError == ERROR_GEN_FAILURE ||
+ lastError == ERROR_NOACCESS)
+ {
+ fb(); //fallback should apply to whole directory sub-tree!
+ return false;
+ }
+
//else we have a problem... report it:
throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
}
@@ -256,12 +333,11 @@ struct FilePlusTraverser
}
template <class FindData>
- static void extractFileInfo(const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output)
+ static void extractFileInfo(const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output)
{
output.fileSize = UInt64(fileInfo.fileSize.QuadPart);
output.lastWriteTimeRaw = getModTime(fileInfo);
- if (volumeSerial)
- output.id = extractFileID(*volumeSerial, fileInfo.fileId);
+ output.id = extractFileID(volumeSerial, fileInfo.fileId);
}
template <class FindData>
@@ -296,7 +372,7 @@ public:
{
activatePrivilege(SE_BACKUP_NAME); //throw FileError
}
- catch (...) {} //don't cause issues in user mode
+ catch (FileError&) {} //don't cause issues in user mode
if (::openDir && ::readDir && ::closeDir)
traverse<FilePlusTraverser>(baseDirectory, sink, 0);
@@ -335,24 +411,26 @@ private:
typename Trav::FindData fileInfo = {};
+ auto fallback = [&] { this->traverse<Win32Traverser>(directory, sink, level); }; //help VS2010 a little by avoiding too deeply nested lambdas
+
while ([&]() -> bool
{
- bool moreData = false;
+ bool gotEntry = false;
typedef Trav Trav1; //f u VS!
tryReportingError([&]
{
typedef Trav1 Trav; //f u VS!
- moreData = Trav::getEntry(searchHandle, directory, fileInfo); //throw FileError
+ gotEntry = Trav::getEntry(searchHandle, directory, fileInfo, fallback); //throw FileError
}, sink);
- return moreData;
+ return gotEntry;
}())
{
//skip "." and ".."
const Zchar* const shortName = Trav::getShortName(fileInfo);
if (shortName[0] == L'.' &&
- (shortName[1] == L'\0' || (shortName[1] == L'.' && shortName[2] == L'\0')))
+ (shortName[1] == 0 || (shortName[1] == L'.' && shortName[2] == 0)))
continue;
const Zstring& fullName = endsWith(directory, FILE_NAME_SEPARATOR) ?
@@ -393,7 +471,7 @@ private:
}
else
{
- Trav::extractFileInfo(fileInfo, volumeSerial != 0 ? &volumeSerial : nullptr, details); //make optional character of volumeSerial explicit in the interface
+ Trav::extractFileInfo(fileInfo, volumeSerial, details); //make optional character of volumeSerial explicit in the interface
//####################################### DST hack ###########################################
if (isFatFileSystem)
@@ -553,7 +631,7 @@ private:
//don't return "." and ".."
const char* const shortName = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"!
if (shortName[0] == '.' &&
- (shortName[1] == '\0' || (shortName[1] == '.' && shortName[2] == '\0')))
+ (shortName[1] == 0 || (shortName[1] == '.' && shortName[2] == 0)))
continue;
const Zstring& fullName = endsWith(directory, FILE_NAME_SEPARATOR) ? //e.g. "/"
@@ -637,14 +715,3 @@ void zen::traverseFolder(const Zstring& directory, bool followSymlinks, Traverse
{
DirTraverser(directory, followSymlinks, sink, dstCallback);
}
-
-
-bool zen::supportForFileId() //Linux: always; Windows: if FindFilePlus_Win32.dll was loaded correctly
-{
-#ifdef FFS_WIN
- return ::openDir && ::readDir && ::closeDir;
-
-#elif defined FFS_LINUX
- return true;
-#endif
-}
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index 075c32e5..b277b6ab 100644
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -70,10 +70,6 @@ void traverseFolder(const Zstring& directory, //throw();
//followSymlinks:
//"true": Symlinks dereferenced and reported via onFile() and onDir() => onSymlink not used!
//"false": Symlinks directly reported via onSymlink(), directory symlinks are not followed
-
-
-//determine whether FileId can be expected to be retrieved
-bool supportForFileId(); //Linux: always; Windows: if FindFilePlus_Win32.dll was loaded correctly
}
#endif // FILETRAVERSER_H_INCLUDED
diff --git a/zen/fixed_list.h b/zen/fixed_list.h
index e80adb99..f08a4815 100644
--- a/zen/fixed_list.h
+++ b/zen/fixed_list.h
@@ -82,7 +82,9 @@ public:
void remove_if(Predicate pred)
{
Node* prev = NULL;
- for (auto ptr = first; ptr;)
+ Node* ptr = first;
+
+ while (ptr)
if (pred(ptr->val))
{
Node* tmp = ptr->next;
diff --git a/zen/i18n.h b/zen/i18n.h
index de615cdb..08ebd05c 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -9,6 +9,8 @@
#include <string>
#include <memory>
+#include <clocale> //thousands separator
+#include "utf8.h" //
//thin layer to enable localization - without platform/library dependencies!
#ifndef WXINTL_NO_GETTEXT_MACRO
@@ -27,7 +29,6 @@ struct TranslationHandler
{
virtual ~TranslationHandler() {}
- virtual std::wstring thousandsSeparator() = 0;
virtual std::wstring translate(const std::wstring& text) = 0; //simple translation
virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, int n) = 0;
};
@@ -35,8 +36,9 @@ struct TranslationHandler
void setTranslator(TranslationHandler* newHandler = NULL); //takes ownership
TranslationHandler* getTranslator();
-inline
-std::wstring getThousandsSeparator() { return getTranslator() ? getTranslator()->thousandsSeparator() : L","; };
+std::wstring getThousandsSeparator();
+
+
@@ -91,6 +93,18 @@ void setTranslator(TranslationHandler* newHandler) { implementation::globalHandl
inline
TranslationHandler* getTranslator() { return implementation::globalHandler().get(); }
+
+
+inline
+std::wstring getThousandsSeparator() //consistency with sprintf(): just use the same values the C-runtime uses!!!
+{
+ //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale
+ const lconv* localInfo = ::localeconv(); //always bound according to doc
+ return utf8CvrtTo<std::wstring>(localInfo->thousands_sep);
+ // why not working?
+ // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).thousands_sep();
+ // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t> >(std::locale("")).decimal_point();
+}
}
#endif //I18_N_HEADER_3843489325045
diff --git a/zen/int64.h b/zen/int64.h
index 91e24437..31c278ca 100644
--- a/zen/int64.h
+++ b/zen/int64.h
@@ -33,7 +33,8 @@ zen::Int64/zen::UInt64: wrapper classes around std::int64_t/std::uint64_t
namespace zen
{
-template <class T, class U> inline void checkRange(U value)
+template <class T, class U> inline
+void checkRange(U value)
{
//caveat: std::numeric_limits<T>::min returns minimum positive(!) number for T = double, while behaving correctly for integer types... sigh
assert(double(std::numeric_limits<T>::lowest()) <= double(value) && //new with C++11!
@@ -93,6 +94,7 @@ public:
Int64& operator|=(const Int64& rhs) { value |= rhs.value; return *this;}
Int64& operator<<=(int rhs) { assert(rhs < 0 || (value << rhs) >> rhs == value); value <<= rhs; return *this; }
Int64& operator>>=(int rhs) { assert(rhs > 0 || (value >> rhs) << rhs == value); value >>= rhs; return *this; }
+ Int64 operator-() const { return -value; }
inline friend bool operator==(const Int64& lhs, const Int64& rhs) { return lhs.value == rhs.value; }
inline friend bool operator!=(const Int64& lhs, const Int64& rhs) { return lhs.value != rhs.value; }
diff --git a/zen/privilege.cpp b/zen/privilege.cpp
index 6dd0b2d7..3b7e9cc5 100644
--- a/zen/privilege.cpp
+++ b/zen/privilege.cpp
@@ -107,7 +107,7 @@ private:
if (iter->second)
try
{
- setPrivilege(iter->first.c_str(), false);
+ setPrivilege(iter->first.c_str(), false); //throw FileError
}
catch (...) {}
}
diff --git a/zen/string_base.h b/zen/string_base.h
index ffc2f839..88da13bf 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -9,6 +9,7 @@
#include <algorithm>
#include <cassert>
+#include <cstdint>
#include "string_tools.h"
#include <boost/detail/atomic_count.hpp>
@@ -76,7 +77,7 @@ protected:
newDescr->length = size;
newDescr->capacity = newCapacity;
- return reinterpret_cast<Char*>(newDescr + 1);
+ return reinterpret_cast<Char*>(newDescr + 1); //alignment note: "newDescr + 1" is Descriptor-aligned, which is larger than alignment for Char-array! => no problem!
}
static Char* clone(Char* ptr)
@@ -101,8 +102,8 @@ protected:
private:
struct Descriptor
{
- size_t length;
- size_t capacity; //allocated size without null-termination
+ std::uint32_t length;
+ std::uint32_t capacity; //allocated size without null-termination
};
static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
@@ -173,8 +174,8 @@ private:
Descriptor(long rc, size_t len, size_t cap) : refCount(rc), length(len), capacity(cap) {}
boost::detail::atomic_count refCount; //practically no perf loss: ~0.2%! (FFS comparison)
- size_t length;
- size_t capacity; //allocated size without null-termination
+ std::uint32_t length;
+ std::uint32_t capacity; //allocated size without null-termination
};
static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index dfbbba6d..b66d5c0e 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -68,7 +68,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError
{
activatePrivilege(SE_BACKUP_NAME); //throw FileError
}
- catch (...) {}
+ catch (FileError&) {}
const HANDLE hLink = ::CreateFile(applyLongPathPrefix(linkPath).c_str(),
GENERIC_READ,
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index a559f9de..38d9b4c4 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -128,41 +128,41 @@ int z_impl::compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA
const int minSize = std::min<int>(sizeA, sizeB);
- if (minSize == 0) //LCMapString does not allow input sizes of 0!
- return static_cast<int>(sizeA) - static_cast<int>(sizeB);
-
- int rv = 0; //always initialize...
- if (minSize <= 5000) //performance optimization: stack
- {
- wchar_t bufferA[5000];
- wchar_t bufferB[5000];
-
- //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString()
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale,
- LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
- a, //__in LPCTSTR lpSrcStr,
- minSize, //__in int cchSrc,
- bufferA, //__out LPTSTR lpDestStr,
- 5000) == 0) //__in int cchDest
- throw std::runtime_error("Error comparing strings! (LCMapString)");
-
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, b, minSize, bufferB, 5000) == 0)
- throw std::runtime_error("Error comparing strings! (LCMapString)");
-
- rv = ::wmemcmp(bufferA, bufferB, minSize);
- }
- else //use freestore
+ int rv = 0;
+ if (minSize != 0) //LCMapString does not allow input sizes of 0!
{
- std::vector<wchar_t> bufferA(minSize);
- std::vector<wchar_t> bufferB(minSize);
-
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, a, minSize, &bufferA[0], minSize) == 0)
- throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
-
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, b, minSize, &bufferB[0], minSize) == 0)
- throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
-
- rv = ::wmemcmp(&bufferA[0], &bufferB[0], minSize);
+ if (minSize <= 5000) //performance optimization: stack
+ {
+ wchar_t bufferA[5000];
+ wchar_t bufferB[5000];
+
+ //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString()
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale,
+ LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
+ a, //__in LPCTSTR lpSrcStr,
+ minSize, //__in int cchSrc,
+ bufferA, //__out LPTSTR lpDestStr,
+ 5000) == 0) //__in int cchDest
+ throw std::runtime_error("Error comparing strings! (LCMapString)");
+
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, b, minSize, bufferB, 5000) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString)");
+
+ rv = ::wmemcmp(bufferA, bufferB, minSize);
+ }
+ else //use freestore
+ {
+ std::vector<wchar_t> bufferA(minSize);
+ std::vector<wchar_t> bufferB(minSize);
+
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, a, minSize, &bufferA[0], minSize) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
+
+ if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, b, minSize, &bufferB[0], minSize) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
+
+ rv = ::wmemcmp(&bufferA[0], &bufferB[0], minSize);
+ }
}
return rv == 0 ?
bgstack15