summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/dir_watcher.cpp15
-rw-r--r--zen/file_handling.cpp156
-rw-r--r--zen/file_handling.h3
-rw-r--r--zen/file_io.cpp35
-rw-r--r--zen/file_traverser.cpp33
-rw-r--r--zen/file_traverser.h1
-rw-r--r--zen/long_path_prefix.h4
-rw-r--r--zen/notify_removal.cpp38
-rw-r--r--zen/recycler.cpp95
-rw-r--r--zen/recycler.h18
-rw-r--r--zen/string_base.h83
-rw-r--r--zen/symlink_target.h30
-rw-r--r--zen/thread.h23
-rw-r--r--zen/type_traits.h6
-rw-r--r--zen/utf.h4
-rw-r--r--zen/win_ver.h66
-rw-r--r--zen/xml_io.cpp13
-rw-r--r--zen/zstring.cpp102
18 files changed, 351 insertions, 374 deletions
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 951dd3ae..8ba9eb19 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -147,13 +147,14 @@ public:
dirnamePf(appendSeparator(directory)),
hDir(INVALID_HANDLE_VALUE)
{
- hDir = ::CreateFile(applyLongPathPrefix(dirnamePf).c_str(),
- FILE_LIST_DIRECTORY,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
- nullptr);
+ hDir = ::CreateFile(applyLongPathPrefix(dirnamePf).c_str(), //_In_ LPCTSTR lpFileName,
+ FILE_LIST_DIRECTORY, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OVERLAPPED, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hDir == INVALID_HANDLE_VALUE)
throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(directory)), formatSystemError(L"CreateFile", getLastError()));
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index e7503dad..b1b2e026 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -146,14 +146,11 @@ Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string
const DllFun<FunType_freeString> freeString (getDllName(), funName_freeString);
if (getLockingProcesses && freeString)
- {
- const wchar_t* procList = nullptr;
- if (getLockingProcesses(filename.c_str(), procList))
+ if (const wchar_t* procList = getLockingProcesses(filename.c_str()))
{
ZEN_ON_SCOPE_EXIT(freeString(procList));
return procList;
}
- }
}
return Zstring();
}
@@ -180,13 +177,14 @@ UInt64 zen::getFilesize(const Zstring& filename) //throw FileError
return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
else
{
- const HANDLE hFile = ::CreateFile(applyLongPathPrefix(filename).c_str(), //open handle to target of symbolic link
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory
- nullptr);
+ //open handle to target of symbolic link
+ const HANDLE hFile = ::CreateFile(applyLongPathPrefix(filename).c_str(), //_In_ LPCTSTR lpFileName,
+ 0, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hFile == INVALID_HANDLE_VALUE)
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)), formatSystemError(L"CreateFile", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
@@ -346,7 +344,7 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File
if (::rename(oldName.c_str(), newName.c_str()) != 0)
{
const int lastError = errno; //copy before making other system calls!
- std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName));
+ const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName));
const std::wstring errorDescr = formatSystemError(L"rename", lastError);
if (lastError == EXDEV)
@@ -415,10 +413,10 @@ bool have8dot3NameClash(const Zstring& filename)
{
const Zstring origName = afterLast(filename, FILE_NAME_SEPARATOR); //returns the whole string if ch not found
const Zstring shortName = afterLast(getFilenameFmt(filename, ::GetShortPathName), FILE_NAME_SEPARATOR); //throw() returns empty string on error
- const Zstring longName = afterLast(getFilenameFmt(filename, ::GetLongPathName) , FILE_NAME_SEPARATOR); //
+ const Zstring longName = afterLast(getFilenameFmt(filename, ::GetLongPathName ), FILE_NAME_SEPARATOR); //
if (!shortName.empty() &&
- !longName.empty() &&
+ !longName .empty() &&
EqualFilename()(origName, shortName) &&
!EqualFilename()(shortName, longName))
{
@@ -649,7 +647,7 @@ void setFileTimeRaw(const Zstring& filename, const FILETIME& creationTime, const
auto openFile = [&](bool conservativeApproach)
{
- return ::CreateFile(applyLongPathPrefix(filename).c_str(),
+ return ::CreateFile(applyLongPathPrefix(filename).c_str(), //_In_ LPCTSTR lpFileName,
(conservativeApproach ?
//some NAS seem to have issues with FILE_WRITE_ATTRIBUTES, even worse, they may fail silently!
//http://sourceforge.net/tracker/?func=detail&atid=1093081&aid=3536680&group_id=234430
@@ -658,13 +656,13 @@ void setFileTimeRaw(const Zstring& filename, const FILETIME& creationTime, const
//avoids mysterious "access denied" when using "GENERIC_READ | GENERIC_WRITE" on a read-only file, even *after* read-only was removed directly before the call!
//http://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430
//since former gives an error notification we may very well try FILE_WRITE_ATTRIBUTES second.
- FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES),
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | //needed to open a directory
- (procSl == ProcSymlink::DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //process symlinks
- nullptr);
+ FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES), //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ (procSl == ProcSymlink::DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0) |
+ FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
};
HANDLE hFile = INVALID_HANDLE_VALUE;
@@ -815,14 +813,14 @@ void setFileTimeRaw(const Zstring& filename, const FILETIME& creationTime, const
FILETIME creationTimeDbg = {};
FILETIME lastWriteTimeDbg = {};
- HANDLE hFile = ::CreateFile(applyLongPathPrefix(filename).c_str(),
- FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | //needed to open a directory
- (procSl == ProcSymlink::DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0),
- nullptr);
+ HANDLE hFile = ::CreateFile(applyLongPathPrefix(filename).c_str(), //_In_ LPCTSTR lpFileName,
+ FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ (procSl == ProcSymlink::DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0) |
+ FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
assert(hFile != INVALID_HANDLE_VALUE);
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
@@ -1301,13 +1299,14 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
{
#ifdef ZEN_WIN
//try to copy file attributes (dereference symlinks and junctions)
- const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS /*needed to open a directory*/ /*| FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!*/ ,
- nullptr);
+ const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(), //_In_ LPCTSTR lpFileName,
+ 0, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ // FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!
+ FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hDirSrc != INVALID_HANDLE_VALUE) //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error...
{
ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirSrc));
@@ -1324,13 +1323,16 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
if (isEncrypted)
::EncryptFile(directory.c_str()); //seems no long path is required (check passed!)
- HANDLE hDirTrg = ::CreateFile(applyLongPathPrefix(directory).c_str(),
- GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- nullptr);
+ HANDLE hDirTrg = ::CreateFile(applyLongPathPrefix(directory).c_str(), //_In_ LPCTSTR lpFileName,
+ GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess,
+ /*read access required for FSCTL_SET_COMPRESSION*/
+ FILE_SHARE_READ |
+ FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hDirTrg != INVALID_HANDLE_VALUE)
{
ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirTrg));
@@ -1518,13 +1520,15 @@ bool canCopyAsSparse(DWORD fileAttrSource, const Zstring& targetFile) //throw ()
bool canCopyAsSparse(const Zstring& sourceFile, const Zstring& targetFile) //throw ()
{
//follow symlinks!
- HANDLE hSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //all shared modes are required to read files that are open in other applications
- nullptr,
- OPEN_EXISTING,
- 0,
- nullptr);
+ HANDLE hSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), //_In_ LPCTSTR lpFileName,
+ 0, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | //all shared modes are required to read files that are open in other applications
+ FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ 0, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hSource == INVALID_HANDLE_VALUE)
return false;
ZEN_ON_SCOPE_EXIT(::CloseHandle(hSource));
@@ -1552,13 +1556,16 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
catch (const FileError&) {}
//open sourceFile for reading
- HANDLE hFileSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(),
- GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING, //FILE_FLAG_OVERLAPPED must not be used!
- FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_BACKUP_SEMANTICS, //FILE_FLAG_NO_BUFFERING should not be used!
- nullptr);
+ HANDLE hFileSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), //_In_ LPCTSTR lpFileName,
+ GENERIC_READ, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ //FILE_FLAG_OVERLAPPED must not be used!
+ //FILE_FLAG_NO_BUFFERING should not be used!
+ FILE_FLAG_SEQUENTIAL_SCAN |
+ FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hFileSource == INVALID_HANDLE_VALUE)
{
const DWORD lastError = ::GetLastError();
@@ -1595,14 +1602,18 @@ void copyFileWindowsSparse(const Zstring& sourceFile,
//FILE_ATTRIBUTE_ENCRYPTED -> no!
//create targetFile and open it for writing
- HANDLE hFileTarget = ::CreateFile(applyLongPathPrefix(targetFile).c_str(),
- GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION
- FILE_SHARE_DELETE, //FILE_SHARE_DELETE is required to rename file while handle is open!
- nullptr,
- CREATE_NEW,
- FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_BACKUP_SEMANTICS | (fileInfoSource.dwFileAttributes & validAttribs),
+ HANDLE hFileTarget = ::CreateFile(applyLongPathPrefix(targetFile).c_str(), //_In_ LPCTSTR lpFileName,
+ GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess,
+ //read access required for FSCTL_SET_COMPRESSION
+ FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ //FILE_SHARE_DELETE is required to rename file while handle is open!
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ CREATE_NEW, //_In_ DWORD dwCreationDisposition,
//FILE_FLAG_OVERLAPPED must not be used! FILE_FLAG_NO_BUFFERING should not be used!
- nullptr);
+ (fileInfoSource.dwFileAttributes & validAttribs) |
+ FILE_FLAG_SEQUENTIAL_SCAN |
+ FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hFileTarget == INVALID_HANDLE_VALUE)
{
const ErrorCode lastError = getLastError(); //copy before making other system calls!
@@ -1804,12 +1815,10 @@ public:
throw ErrorShouldCopyAsSparse(L"sparse dummy value");
if (exceptionInUserCallback)
- try
- {
- exceptionInUserCallback(0); //should throw again!!!
- assert(false);
- }
- catch (...) { throw; }
+ {
+ exceptionInUserCallback(0); //should throw again!!!
+ assert(false); //
+ }
if (!errorMsg.first.empty())
throw FileError(errorMsg.first, errorMsg.second);
@@ -1818,7 +1827,7 @@ public:
private:
bool shouldCopyAsSparse; //
std::pair<std::wstring, std::wstring> errorMsg; //these are exclusive!
- std::function<void(Int64 bytesDelta)> exceptionInUserCallback; // -> optional
+ std::function<void(Int64 bytesDelta)> exceptionInUserCallback; // -> optional
};
@@ -1946,7 +1955,6 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destination is not supported with Windows 2000
-//const bool supportUnbufferedCopy = vistaOrLater();
//caveat: function scope static initialization is not thread-safe in VS 2010!
@@ -1969,7 +1977,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
if (supportNonEncryptedDestination)
copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrytped location
- //if (supportUnbufferedCopy) //see http://blogs.technet.com/b/askperf/archive/2007/05/08/slow-large-file-copy-issues.aspx
+ //if (vistaOrLater()) //see http://blogs.technet.com/b/askperf/archive/2007/05/08/slow-large-file-copy-issues.aspx
// copyFlags |= COPY_FILE_NO_BUFFERING; //no perf difference at worst, huge improvement for large files (20% in test NTFS -> NTFS)
//It's a shame this flag causes file corruption! https://sourceforge.net/projects/freefilesync/forums/forum/847542/topic/5177950
//documentation on CopyFile2() even states: "It is not recommended to pause copies that are using this flag." How dangerous is this thing, why offer it at all???
diff --git a/zen/file_handling.h b/zen/file_handling.h
index 48a762d7..d14ed27f 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -75,7 +75,8 @@ void copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissi
//Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop.
// => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending:
-const Zstring TEMP_FILE_ENDING = Zstr(".ffs_tmp");
+
+const Zchar TEMP_FILE_ENDING[] = Zstr(".ffs_tmp"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace!
void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError
}
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index fbca9089..5eab476a 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -33,14 +33,11 @@ Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string
const DllFun<FunType_freeString> freeString (getDllName(), funName_freeString);
if (getLockingProcesses && freeString)
- {
- const wchar_t* procList = nullptr;
- if (getLockingProcesses(filename.c_str(), procList))
+ if (const wchar_t* procList = getLockingProcesses(filename.c_str()))
{
ZEN_ON_SCOPE_EXIT(freeString(procList));
return procList;
}
- }
}
return Zstring();
}
@@ -81,12 +78,12 @@ FileInput::FileInput(const Zstring& filename) : FileInputBase(filename) //throw
{
#ifdef ZEN_WIN
const wchar_t functionName[] = L"CreateFile";
- fileHandle = ::CreateFile(applyLongPathPrefix(filename).c_str(),
- GENERIC_READ,
- FILE_SHARE_READ | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_SEQUENTIAL_SCAN,
+ fileHandle = ::CreateFile(applyLongPathPrefix(filename).c_str(), //_In_ LPCTSTR lpFileName,
+ GENERIC_READ, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ FILE_FLAG_SEQUENTIAL_SCAN, //_In_ DWORD dwFlagsAndAttributes,
/* possible values: (Reference http://msdn.microsoft.com/en-us/library/aa363858(VS.85).aspx#caching_behavior)
FILE_FLAG_NO_BUFFERING
FILE_FLAG_RANDOM_ACCESS
@@ -111,7 +108,7 @@ FileInput::FileInput(const Zstring& filename) : FileInputBase(filename) //throw
for FFS most comparisons are probably between different disks => let's use FILE_FLAG_SEQUENTIAL_SCAN
*/
- nullptr);
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (fileHandle == INVALID_HANDLE_VALUE)
#elif defined ZEN_LINUX || defined ZEN_MAC
checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever!
@@ -198,18 +195,20 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
auto getHandle = [&](DWORD dwFlagsAndAttributes)
{
- return ::CreateFile(applyLongPathPrefix(filename).c_str(),
- GENERIC_READ | GENERIC_WRITE,
+ return ::CreateFile(applyLongPathPrefix(filename).c_str(), //_In_ LPCTSTR lpFileName,
+ GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess,
/* http://msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
quote: When an application creates a file across a network, it is better
to use GENERIC_READ | GENERIC_WRITE for dwDesiredAccess than to use GENERIC_WRITE alone.
The resulting code is faster, because the redirector can use the cache manager and send fewer SMBs with more data.
This combination also avoids an issue where writing to a file across a network can occasionally return ERROR_ACCESS_DENIED. */
- FILE_SHARE_DELETE, //FILE_SHARE_DELETE is required to rename file while handle is open!
- nullptr,
- dwCreationDisposition,
- dwFlagsAndAttributes | FILE_FLAG_SEQUENTIAL_SCAN,
- nullptr);
+ FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ //FILE_SHARE_DELETE is required to rename file while handle is open!
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ dwCreationDisposition, //_In_ DWORD dwCreationDisposition,
+ dwFlagsAndAttributes |
+ FILE_FLAG_SEQUENTIAL_SCAN, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
};
fileHandle = getHandle(FILE_ATTRIBUTE_NORMAL);
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 389833f2..4fe521a6 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -81,13 +81,14 @@ bool tryReportingItemError(Command cmd, zen::TraverseCallback& callback, const Z
void getInfoFromFileSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output) //throw FileError
{
//open handle to target of symbolic link
- HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(linkName).c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory -> keep it even if we expect to open a file! See comment below
- nullptr);
+ HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(linkName).c_str(), //_In_ LPCTSTR lpFileName,
+ 0, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
+ //needed to open a directory -> keep it even if we expect to open a file! See comment below
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hFile == INVALID_HANDLE_VALUE)
throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)), formatSystemError(L"CreateFile", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
@@ -117,14 +118,16 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if s
//- root paths "C:\", "D:\"
//- network shares: \\share\dirname
//- indirection: subst S: %USERPROFILE%
- // -> GetVolumePathName()+GetVolumeInformation() OTOH incorrectly resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try...
- const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(pathName).c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS /*needed to open a directory*/ /*| FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!*/ ,
- nullptr);
+ // -> GetVolumePathName() + GetVolumeInformation() OTOH incorrectly resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try...
+ const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(pathName).c_str(), //_In_ LPCTSTR lpFileName,
+ 0, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ // FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!
+ FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
+ /*needed to open a directory*/
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hDir == INVALID_HANDLE_VALUE)
return 0;
ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index d686e9b8..2944c5ba 100644
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -7,7 +7,6 @@
#ifndef FILETRAVERSER_H_INCLUDED
#define FILETRAVERSER_H_INCLUDED
-//#include <memory>
#include "zstring.h"
#include "int64.h"
#include "file_id_def.h"
diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h
index cdff09fa..ae09c5f5 100644
--- a/zen/long_path_prefix.h
+++ b/zen/long_path_prefix.h
@@ -48,8 +48,8 @@ Zstring removeLongPathPrefix(const Zstring& path); //throw()
//################## implementation ##################
//there are two flavors of long path prefix: one for UNC paths, one for regular paths
-const Zstring LONG_PATH_PREFIX = L"\\\\?\\";
-const Zstring LONG_PATH_PREFIX_UNC = L"\\\\?\\UNC";
+const wchar_t LONG_PATH_PREFIX [] = L"\\\\?\\"; //don't use Zstring as global constant: avoid static initialization order problem in global namespace!
+const wchar_t LONG_PATH_PREFIX_UNC[] = L"\\\\?\\UNC"; //
template <size_t maxPath> inline
Zstring applyLongPathPrefixImpl(const Zstring& path)
diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp
index d1fbc6ed..678675b5 100644
--- a/zen/notify_removal.cpp
+++ b/zen/notify_removal.cpp
@@ -13,25 +13,17 @@
using namespace zen;
-namespace
-{
-bool messageProviderConstructed = false;
-}
-
-
class MessageProvider //administrates a single dummy window to receive messages
{
public:
- static MessageProvider& instance() //throw (FileError)
+ static MessageProvider& instance() //throw FileError
{
static MessageProvider inst;
- messageProviderConstructed = true;
return inst;
}
- class Listener
+ struct Listener
{
- public:
virtual ~Listener() {}
virtual void onMessage(UINT message, WPARAM wParam, LPARAM lParam) = 0; //throw()!
};
@@ -48,7 +40,8 @@ private:
static const wchar_t dummyWindowName[];
- friend LRESULT CALLBACK topWndProc(HWND, UINT, WPARAM, LPARAM);
+ static LRESULT CALLBACK topWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
void processMessage(UINT message, WPARAM wParam, LPARAM lParam);
const HINSTANCE hMainModule;
@@ -61,15 +54,15 @@ private:
const wchar_t MessageProvider::dummyWindowName[] = L"E6AD5EB1-527B-4EEF-AC75-27883B233380"; //random name
-LRESULT CALLBACK topWndProc(HWND hwnd, //handle to window
- UINT uMsg, //message identifier
- WPARAM wParam, //first message parameter
- LPARAM lParam) //second message parameter
+LRESULT CALLBACK MessageProvider::topWndProc(HWND hwnd, //handle to window
+ UINT uMsg, //message identifier
+ WPARAM wParam, //first message parameter
+ LPARAM lParam) //second message parameter
{
- if (messageProviderConstructed) //attention: this callback is triggered in the middle of singleton construction! It is a bad idea to to call back at this time!
+ if (auto pThis = reinterpret_cast<MessageProvider*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA)))
try
{
- MessageProvider::instance().processMessage(uMsg, wParam, lParam); //not supposed to throw
+ pThis->processMessage(uMsg, wParam, lParam); //not supposed to throw!
}
catch (...) { assert(false); }
@@ -110,6 +103,13 @@ MessageProvider::MessageProvider() :
if (!windowHandle)
throw FileError(_("Unable to register to receive system messages."), formatSystemError(L"CreateWindow", getLastError()));
+ //store this-pointer for topWndProc() to use: do this AFTER CreateWindow() to avoid processing messages while this constructor is running!!!
+ //unlike: http://blogs.msdn.com/b/oldnewthing/archive/2014/02/03/10496248.aspx
+ ::SetLastError(ERROR_SUCCESS); //[!] required for proper error handling, see MSDN, SetWindowLongPtr
+ if (::SetWindowLongPtr(windowHandle, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this)) == 0)
+ if (::GetLastError() != ERROR_SUCCESS)
+ throw FileError(_("Unable to register to receive system messages."), formatSystemError(L"SetWindowLongPtr", getLastError()));
+
guardClass.dismiss();
}
@@ -125,8 +125,8 @@ MessageProvider::~MessageProvider()
void MessageProvider::processMessage(UINT message, WPARAM wParam, LPARAM lParam)
{
- std::for_each(listener.begin(), listener.end(),
- [&](Listener* ls) { ls->onMessage(message, wParam, lParam); });
+ for (Listener* ls : listener)
+ ls->onMessage(message, wParam, lParam);
}
//####################################################################################################
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 11b599d4..b9245259 100644
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -5,17 +5,14 @@
// **************************************************************************
#include "recycler.h"
-//#include <stdexcept>
-//#include <iterator>
#include <zen/file_handling.h>
#ifdef ZEN_WIN
-//#include <algorithm>
-//#include <functional>
-#include <zen/dll.h>
-#include <zen/assert_static.h>
-#include <zen/win_ver.h>
-#include <zen/long_path_prefix.h>
+#include "thread.h"
+#include "dll.h"
+#include "assert_static.h"
+#include "win_ver.h"
+#include "long_path_prefix.h"
#include "IFileOperation/file_op.h"
#elif defined ZEN_LINUX
@@ -45,7 +42,6 @@ IFileOperation - multiple files 2,1s
Nevertheless, let's use IFileOperation for better error reporting (including details on locked files)!
*/
-const bool useIFileOperation = vistaOrLater(); //caveat: function scope static initialization is not thread-safe in VS 2010!
struct CallbackData
{
@@ -58,7 +54,7 @@ struct CallbackData
};
-bool recyclerCallback(const wchar_t* filename, void* sink)
+bool onRecyclerCallback(const wchar_t* filename, void* sink)
{
CallbackData& cbd = *static_cast<CallbackData*>(sink); //sink is NOT optional here
@@ -76,6 +72,7 @@ bool recyclerCallback(const wchar_t* filename, void* sink)
}
}
+
void zen::recycleOrDelete(const std::vector<Zstring>& filenames, const std::function<void (const Zstring& currentItem)>& notifyDeletionStatus)
{
if (filenames.empty())
@@ -85,7 +82,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, const std::func
//both ::SHFileOperation() and ::IFileOperation() cannot delete a folder named "System Volume Information" with normal attributes but shamelessly report success
//both ::SHFileOperation() and ::IFileOperation() can't handle \\?\-prefix!
- if (useIFileOperation) //new recycle bin usage: available since Vista
+ if (vistaOrLater()) //new recycle bin usage: available since Vista
{
using namespace fileop;
const DllFun<FunType_moveToRecycleBin> moveToRecycler(getDllName(), funName_moveToRecycleBin);
@@ -100,16 +97,14 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, const std::func
cNames.push_back(it->c_str());
CallbackData cbd(notifyDeletionStatus);
- if (!moveToRecycler(&cNames[0], cNames.size(), recyclerCallback, &cbd))
+ if (!moveToRecycler(&cNames[0], cNames.size(), onRecyclerCallback, &cbd))
{
if (cbd.exceptionInUserCallback)
- try
- {
- assert(notifyDeletionStatus);
- notifyDeletionStatus(Zstring()); //should throw again!!!
- assert(false);
- }
- catch (...) { throw; }
+ {
+ assert(notifyDeletionStatus);
+ notifyDeletionStatus(Zstring()); //should throw again!!!
+ assert(false);
+ }
std::wstring filenameFmt = fmtFileName(filenames[0]); //probably not the correct file name for file lists larger than 1!
if (filenames.size() > 1)
@@ -174,7 +169,7 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
//implement same behavior as in Windows: if recycler is not existing, delete permanently
if (error->code == G_IO_ERROR_NOT_SUPPORTED)
{
- struct stat fileInfo = {};
+ struct ::stat fileInfo = {};
if (::lstat(filename.c_str(), &fileInfo) != 0)
return false;
@@ -205,7 +200,7 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
throw FileError(errorMsg, errorDescr);
};
- FSRef objectRef;
+ FSRef objectRef = {}; //= POD structure not a pointer type!
OSStatus rv = ::FSPathMakeRefWithOptions(filenameUtf8, //const UInt8 *path,
kFSPathMakeRefDoNotFollowLeafSymlink, //OptionBits options,
&objectRef, //FSRef *ref,
@@ -222,7 +217,7 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
//implement same behavior as in Windows: if recycler is not existing, delete permanently
if (rv2 == -120) //=="Directory not found or incomplete pathname." but should really be "recycle bin directory not found"!
{
- struct stat fileInfo = {};
+ struct ::stat fileInfo = {};
if (::lstat(filename.c_str(), &fileInfo) != 0)
return false;
@@ -241,31 +236,53 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
#ifdef ZEN_WIN
-StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
+bool zen::recycleBinExists(const Zstring& pathName, const std::function<void ()>& onUpdateGui) //throw FileError
{
- const DWORD bufferSize = MAX_PATH + 1;
- std::vector<wchar_t> buffer(bufferSize);
- if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
- &buffer[0], //__out LPTSTR lpszVolumePathName,
- bufferSize)) //__in DWORD cchBufferLength
- return STATUS_REC_UNKNOWN;
+ if (vistaOrLater())
+ {
+ using namespace fileop;
+ const DllFun<FunType_getRecycleBinStatus> getRecycleBinStatus(getDllName(), funName_getRecycleBinStatus);
+ const DllFun<FunType_getLastError> getLastError (getDllName(), funName_getLastError);
+
+ if (!getRecycleBinStatus || !getLastError)
+ throw FileError(replaceCpy(_("Checking recycle bin failed for folder %x."), L"%x", fmtFileName(pathName)),
+ replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName())));
+
+ bool hasRecycler = false;
+ if (!getRecycleBinStatus(pathName.c_str(), hasRecycler))
+ throw FileError(replaceCpy(_("Checking recycle bin failed for folder %x."), L"%x", fmtFileName(pathName)), getLastError());
+
+ return hasRecycler;
+ }
+ else
+ {
+ //excessive runtime if recycle bin exists, is full and drive is slow:
+ auto ft = async([pathName]()
+ {
+ SHQUERYRBINFO recInfo = {};
+ recInfo.cbSize = sizeof(recInfo);
+ return ::SHQueryRecycleBin(pathName.c_str(), //__in_opt LPCTSTR pszRootPath,
+ &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo
+ });
- const Zstring rootPathPf = appendSeparator(&buffer[0]);
+ while (!ft.timed_wait(boost::posix_time::milliseconds(50)))
+ if (onUpdateGui)
+ onUpdateGui(); //may throw!
+
+ return ft.get() == S_OK;
+ }
- SHQUERYRBINFO recInfo = {};
- recInfo.cbSize = sizeof(recInfo);
- HRESULT rv = ::SHQueryRecycleBin(rootPathPf.c_str(), //__in_opt LPCTSTR pszRootPath,
- &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo
+ //1. ::SHQueryRecycleBin() is excessive: traverses whole $Recycle.Bin directory tree each time!!!! But it's safe and correct.
- return rv == S_OK ? STATUS_REC_EXISTS : STATUS_REC_MISSING;
- //1. excessive: traverses whole C:\$Recycle.Bin directory tree each time!!!! But it's safe and correct.
+ //2. we can't simply buffer the ::SHQueryRecycleBin() based on volume serial number:
+ // "subst S:\ C:\" => GetVolumeInformation() returns same serial for C:\ and S:\, but S:\ does not support recycle bin!
- //2. we would prefer to use CLSID_RecycleBinManager beginning with Vista... if only this interface were documented!!!
+ //3. we would prefer to use CLSID_RecycleBinManager beginning with Vista... if only this interface were documented!!!
- //3. check directory existence of "C:\$Recycle.Bin, C:\RECYCLER, C:\RECYCLED"
+ //4. check directory existence of "C:\$Recycle.Bin, C:\RECYCLER, C:\RECYCLED"
// -> not upward-compatible, wrong result for subst-alias: recycler assumed existing, although it is not!
- //4. alternative approach a'la Raymond Chen: http://blogs.msdn.com/b/oldnewthing/archive/2008/09/18/8956382.aspx
+ //5. alternative approach a'la Raymond Chen: http://blogs.msdn.com/b/oldnewthing/archive/2008/09/18/8956382.aspx
//caveat: might not be reliable, e.g. "subst"-alias of volume contains "$Recycle.Bin" although it is not available!
/*
diff --git a/zen/recycler.h b/zen/recycler.h
index 8068c4ec..255f11ef 100644
--- a/zen/recycler.h
+++ b/zen/recycler.h
@@ -4,11 +4,11 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef RECYCLER_H_INCLUDED
-#define RECYCLER_H_INCLUDED
+#ifndef RECYCLER_H_INCLUDED_18345067341545
+#define RECYCLER_H_INCLUDED_18345067341545
-#include <functional>
#include <vector>
+#include <functional>
#include <zen/file_error.h>
#include <zen/zstring.h>
@@ -36,14 +36,8 @@ bool recycleOrDelete(const Zstring& filename); //throw FileError, return "true"
#ifdef ZEN_WIN
-enum StatusRecycler
-{
- STATUS_REC_EXISTS,
- STATUS_REC_MISSING,
- STATUS_REC_UNKNOWN
-};
-StatusRecycler recycleBinStatus(const Zstring& pathName); //test existence of Recycle Bin API for certain path
-//Win: blocks heavily if recycle bin is really full and drive is slow!!!
+//can take a long time if recycle bin is full and drive is slow!!! => buffer volume ids!
+bool recycleBinExists(const Zstring& pathName, const std::function<void ()>& onUpdateGui); //throw FileError
void recycleOrDelete(const std::vector<Zstring>& filenames, //throw FileError, return "true" if file/dir was actually deleted
//may throw: first exception is swallowed, updateStatus() is then called again where it should throw again and the exception will propagate as expected
@@ -51,4 +45,4 @@ void recycleOrDelete(const std::vector<Zstring>& filenames, //throw FileError, r
#endif
}
-#endif // RECYCLER_H_INCLUDED
+#endif //RECYCLER_H_INCLUDED_18345067341545
diff --git a/zen/string_base.h b/zen/string_base.h
index c134a6fc..23cfcdf0 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef Z_BASE_H_INCLUDED
-#define Z_BASE_H_INCLUDED
+#ifndef Z_BASE_H_INCLUDED_08321745456
+#define Z_BASE_H_INCLUDED_08321745456
#include <algorithm>
#include <cassert>
@@ -186,9 +186,9 @@ private:
//perf note: interestingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison
-template <class Char, //Character Type
+template <class Char, //Character Type
template <class, class> class SP = StorageRefCountThreadSafe, //Storage Policy
- class AP = AllocatorOptimalSpeed> //Allocator Policy
+ class AP = AllocatorOptimalSpeed> //Allocator Policy
class Zbase : public SP<Char, AP>
{
public:
@@ -381,8 +381,9 @@ template <class Char, template <class, class> class SP, class AP> inline
size_t Zbase<Char, SP, AP>::find(const Zbase& str, size_t pos) const
{
assert(pos <= length());
- const Char* thisEnd = end(); //respect embedded 0
- const Char* it = std::search(begin() + pos, thisEnd,
+ const size_t len = length();
+ const Char* thisEnd = begin() + len; //respect embedded 0
+ const Char* it = std::search(begin() + std::min(pos, len), thisEnd,
str.begin(), str.end());
return it == thisEnd ? npos : it - begin();
}
@@ -392,8 +393,9 @@ template <class Char, template <class, class> class SP, class AP> inline
size_t Zbase<Char, SP, AP>::find(const Char* str, size_t pos) const
{
assert(pos <= length());
- const Char* thisEnd = end(); //respect embedded 0
- const Char* it = std::search(begin() + pos, thisEnd,
+ const size_t len = length();
+ const Char* thisEnd = begin() + len; //respect embedded 0
+ const Char* it = std::search(begin() + std::min(pos, len), thisEnd,
str, str + strLength(str));
return it == thisEnd ? npos : it - begin();
}
@@ -403,8 +405,9 @@ template <class Char, template <class, class> class SP, class AP> inline
size_t Zbase<Char, SP, AP>::find(Char ch, size_t pos) const
{
assert(pos <= length());
- const Char* thisEnd = end();
- const Char* it = std::find(begin() + pos, thisEnd, ch); //respect embedded 0
+ const size_t len = length();
+ const Char* thisEnd = begin() + len; //respect embedded 0
+ const Char* it = std::find(begin() + std::min(pos, len), thisEnd, ch);
return it == thisEnd ? npos : it - begin();
}
@@ -413,9 +416,8 @@ template <class Char, template <class, class> class SP, class AP> inline
size_t Zbase<Char, SP, AP>::rfind(Char ch, size_t pos) const
{
assert(pos == npos || pos <= length());
-
- const Char* currEnd = pos == npos ? end() : begin() + std::min(pos + 1, length());
-
+ const size_t len = length();
+ const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + 1, len));
const Char* it = find_last(begin(), currEnd, ch);
return it == currEnd ? npos : it - begin();
}
@@ -425,62 +427,15 @@ template <class Char, template <class, class> class SP, class AP> inline
size_t Zbase<Char, SP, AP>::rfind(const Char* str, size_t pos) const
{
assert(pos == npos || pos <= length());
-
const size_t strLen = strLength(str);
- const Char* currEnd = pos == npos ? end() : begin() + std::min(pos + strLen, length());
-
+ const size_t len = length();
+ const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + strLen, len));
const Char* it = search_last(begin(), currEnd,
str, str + strLen);
return it == currEnd ? npos : it - begin();
}
-/* -> dead code ahead: better use zen::replace template instead!
-template <class Char, template <class, class> class SP, class AP>
-Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::replace(size_t pos1, size_t n1, const Zbase& str)
-{
- assert(str.data() < rawStr || rawStr + length() < str.data()); //str mustn't point to data in this string
- assert(pos1 + n1 <= length());
-
- const size_t n2 = str.length();
-
- const size_t oldLen = length();
- if (oldLen == 0)
- return *this = str;
-
- const size_t newLen = oldLen - n1 + n2;
-
- if (this->canWrite(rawStr, newLen))
- {
- if (n1 < n2) //move remainder right -> std::copy_backward
- {
- std::copy_backward(rawStr + pos1 + n1, rawStr + oldLen + 1, rawStr + newLen + 1); //include null-termination
- this->setLength(rawStr, newLen);
- }
- else if (n1 > n2) //shift left -> std::copy
- {
- std::copy(rawStr + pos1 + n1, rawStr + oldLen + 1, rawStr + pos1 + n2); //include null-termination
- this->setLength(rawStr, newLen);
- }
-
- std::copy(str.data(), str.data() + n2, rawStr + pos1);
- }
- else
- {
- //copy directly into new string
- Char* const newStr = this->create(newLen);
-
- std::copy(rawStr, rawStr + pos1, newStr);
- std::copy(str.data(), str.data() + n2, newStr + pos1);
- std::copy(rawStr + pos1 + n1, rawStr + oldLen + 1, newStr + pos1 + n2); //include null-termination
-
- this->destroy(rawStr);
- rawStr = newStr;
- }
- return *this;
-}
-*/
-
template <class Char, template <class, class> class SP, class AP> inline
void Zbase<Char, SP, AP>::resize(size_t newSize, Char fillChar)
{
@@ -558,7 +513,7 @@ size_t Zbase<Char, SP, AP>::length() const
template <class Char, template <class, class> class SP, class AP> inline
const Char Zbase<Char, SP, AP>::operator[](size_t pos) const
{
- assert(pos < length());
+ assert(pos < length()); //design by contract! no runtime check!
return rawStr[pos];
}
@@ -712,4 +667,4 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::operator+=(Char ch)
}
}
-#endif //Z_BASE_H_INCLUDED
+#endif //Z_BASE_H_INCLUDED_08321745456
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 73d67927..4c4c3745 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -92,13 +92,14 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
{ activatePrivilege(SE_BACKUP_NAME); } //throw FileError
catch (FileError&) {} //This shall not cause an error in user mode!
- const HANDLE hLink = ::CreateFile(applyLongPathPrefix(linkPath).c_str(),
- 0, //it seems we do not even need GENERIC_READ!
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
- nullptr);
+ const HANDLE hLink = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), //_In_ LPCTSTR lpFileName,
+ //it seems we do not even need GENERIC_READ!
+ 0, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hLink == INVALID_HANDLE_VALUE)
throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"CreateFile", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hLink));
@@ -162,13 +163,14 @@ Zstring getResolvedFilePath_impl(const Zstring& linkPath) //throw FileError
{
using namespace zen;
#ifdef ZEN_WIN
- const HANDLE hDir = ::CreateFile(applyLongPathPrefix(linkPath).c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory
- nullptr);
+ const HANDLE hDir = ::CreateFile(applyLongPathPrefix(linkPath).c_str(), //_In_ LPCTSTR lpFileName,
+ 0, //_In_ DWORD dwDesiredAccess,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode,
+ nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes,
+ OPEN_EXISTING, //_In_ DWORD dwCreationDisposition,
+ //needed to open a directory:
+ FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes,
+ nullptr); //_In_opt_ HANDLE hTemplateFile
if (hDir == INVALID_HANDLE_VALUE)
throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(linkPath)), formatSystemError(L"CreateFile", getLastError()));
ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
diff --git a/zen/thread.h b/zen/thread.h
index 5c6eecc5..95cdf074 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef BOOST_THREAD_WRAP_H
-#define BOOST_THREAD_WRAP_H
+#ifndef BOOST_THREAD_WRAP_H_78963234
+#define BOOST_THREAD_WRAP_H_78963234
//temporary solution until C++11 thread becomes fully available (considering std::thread's non-interruptibility and std::async craziness, this may be NEVER)
#include <memory>
@@ -57,10 +57,10 @@ bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration&
//wait until first job is successful or all failed
template <class T>
-class RunUntilFirstHit
+class GetFirstResult
{
public:
- RunUntilFirstHit();
+ GetFirstResult();
template <class Fun>
void addJob(Fun f); //f must return a std::unique_ptr<T> containing a value if successful
@@ -118,7 +118,7 @@ bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration&
template <class T>
-class RunUntilFirstHit<T>::AsyncResult
+class GetFirstResult<T>::AsyncResult
{
public:
AsyncResult() :
@@ -164,6 +164,7 @@ public:
private:
bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished >= jobsTotal); } //call while locked!
+
#ifndef NDEBUG
bool returnedResult;
#endif
@@ -177,14 +178,14 @@ private:
template <class T> inline
-RunUntilFirstHit<T>::RunUntilFirstHit() : result(std::make_shared<AsyncResult>()), jobsTotal(0) {}
+GetFirstResult<T>::GetFirstResult() : result(std::make_shared<AsyncResult>()), jobsTotal(0) {}
template <class T>
template <class Fun> inline
-void RunUntilFirstHit<T>::addJob(Fun f) //f must return a std::unique_ptr<T> containing a value on success
+void GetFirstResult<T>::addJob(Fun f) //f must return a std::unique_ptr<T> containing a value on success
{
- auto result2 = result; //MSVC 2010: this is ridiculous!!!
+ auto result2 = result; //capture member variable, not "this"!
boost::thread t([result2, f] { result2->reportFinished(f()); });
++jobsTotal;
t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
@@ -193,11 +194,11 @@ void RunUntilFirstHit<T>::addJob(Fun f) //f must return a std::unique_ptr<T> con
template <class T>
template <class Duration> inline
-bool RunUntilFirstHit<T>::timedWait(const Duration& duration) const { return result->waitForResult(jobsTotal, duration); }
+bool GetFirstResult<T>::timedWait(const Duration& duration) const { return result->waitForResult(jobsTotal, duration); }
template <class T> inline
-std::unique_ptr<T> RunUntilFirstHit<T>::get() const { return result->getResult(jobsTotal); }
+std::unique_ptr<T> GetFirstResult<T>::get() const { return result->getResult(jobsTotal); }
}
-#endif //BOOST_THREAD_WRAP_H
+#endif //BOOST_THREAD_WRAP_H_78963234
diff --git a/zen/type_traits.h b/zen/type_traits.h
index b6e05871..f69c155a 100644
--- a/zen/type_traits.h
+++ b/zen/type_traits.h
@@ -142,7 +142,7 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
template <class U> static No& hasMember(Helper<int Fallback::*, &Helper2<U>::NAME>*); \
template <class U> static Yes& hasMember(...); \
public: \
- enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \
+ enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \
}; \
\
template<class T> \
@@ -166,7 +166,7 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
template <class T> static Yes& hasMember(Helper<TYPE, &T::NAME>*); \
template <class T> static No& hasMember(...); \
public: \
- enum { value = sizeof(hasMember<U>(nullptr)) == sizeof(Yes) }; \
+ enum { value = sizeof(hasMember<U>(nullptr)) == sizeof(Yes) }; \
};
//####################################################################
@@ -183,7 +183,7 @@ struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
template <class U> static Yes& hasMemberType(Helper<typename U::TYPENAME>*); \
template <class U> static No& hasMemberType(...); \
public: \
- enum { value = sizeof(hasMemberType<T>(nullptr)) == sizeof(Yes) }; \
+ enum { value = sizeof(hasMemberType<T>(nullptr)) == sizeof(Yes) }; \
};
}
diff --git a/zen/utf.h b/zen/utf.h
index 16e543c8..a50466f2 100644
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -53,8 +53,8 @@ size_t findUnicodePos(const UtfString& str, size_t unicodePos); //return positio
//----------------------- implementation ----------------------------------
namespace implementation
{
-typedef std::uint_fast32_t CodePoint; //must be at least four bytes
-typedef std::uint_fast16_t Char16; //we need an unsigned type
+typedef std::uint32_t CodePoint;
+typedef std::uint16_t Char16;
typedef unsigned char Char8;
const CodePoint LEAD_SURROGATE = 0xd800;
diff --git a/zen/win_ver.h b/zen/win_ver.h
index 2c5e2f81..8797b596 100644
--- a/zen/win_ver.h
+++ b/zen/win_ver.h
@@ -7,19 +7,28 @@
#ifndef WINDOWS_VERSION_HEADER_238470348254325
#define WINDOWS_VERSION_HEADER_238470348254325
+#include <cstdint>
#include <zen/win.h> //includes "windows.h"
namespace zen
{
-bool winXpOrLater();
-bool winServer2003orLater();
-bool vistaOrLater();
-bool win7OrLater();
-bool win8OrLater();
-
-
+std::uint64_t getOsVersion();
+std::uint64_t toBigOsNumber(DWORD high, DWORD low);
+//version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
+const std::uint64_t osVersionWin81 = toBigOsNumber(6, 3);
+const std::uint64_t osVersionWin8 = toBigOsNumber(6, 2);
+const std::uint64_t osVersionWin7 = toBigOsNumber(6, 1);
+const std::uint64_t osVersionWinVista = toBigOsNumber(6, 0);
+const std::uint64_t osVersionWinServer2003 = toBigOsNumber(5, 2);
+const std::uint64_t osVersionWinXp = toBigOsNumber(5, 1);
+inline bool win81OrLater () { return getOsVersion() >= osVersionWin81; }
+inline bool win8OrLater () { return getOsVersion() >= osVersionWin8; }
+inline bool win7OrLater () { return getOsVersion() >= osVersionWin7; }
+inline bool vistaOrLater () { return getOsVersion() >= osVersionWinVista; }
+inline bool winServer2003orLater() { return getOsVersion() >= osVersionWinServer2003; }
+inline bool winXpOrLater () { return getOsVersion() >= osVersionWinXp; }
@@ -29,42 +38,27 @@ bool win8OrLater();
//######################### implementation #########################
-//version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
+inline
+std::uint64_t toBigOsNumber(DWORD high, DWORD low)
+{
+ ULARGE_INTEGER tmp = {};
+ tmp.HighPart = high;
+ tmp.LowPart = low;
+
+ static_assert(sizeof(tmp) == sizeof(std::uint64_t), "");
+ return tmp.QuadPart;
+}
-//2000 is version 5.0
-//XP is version 5.1
-//Server 2003 is version 5.2
-//Vista is version 6.0
-//Seven is version 6.1
-//Eight is version 6.2
-namespace impl
-{
inline
-bool winXyOrLater(DWORD major, DWORD minor)
+std::uint64_t getOsVersion()
{
OSVERSIONINFO osvi = {};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
- if (::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread safe statics right now...
- return osvi.dwMajorVersion != major ? osvi.dwMajorVersion > major : osvi.dwMinorVersion >= minor;
- return false;
-}
+ if (!::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread safe statics right now...
+ return 0;
+ return toBigOsNumber(osvi.dwMajorVersion, osvi.dwMinorVersion);
}
-
-inline
-bool winXpOrLater() { return impl::winXyOrLater(5, 1); }
-
-inline
-bool winServer2003orLater() { return impl::winXyOrLater(5, 2); }
-
-inline
-bool vistaOrLater() { return impl::winXyOrLater(6, 0); }
-
-inline
-bool win7OrLater() { return impl::winXyOrLater(6, 1); }
-
-inline
-bool win8OrLater() { return impl::winXyOrLater(6, 2); }
}
#endif //WINDOWS_VERSION_HEADER_238470348254325
diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp
index 4b9abc29..a8236300 100644
--- a/zen/xml_io.cpp
+++ b/zen/xml_io.cpp
@@ -5,16 +5,16 @@
// **************************************************************************
#include "xml_io.h"
-#include <zen/file_handling.h>
-#include <zen/file_io.h>
-#include <zen/serialize.h>
+#include "file_handling.h"
+#include "file_io.h"
+#include "serialize.h"
using namespace zen;
XmlDoc zen::loadXmlDocument(const Zstring& filename) //throw FileError
{
- //can't simply use zen::loadBinStream() due to the short-circuit xml-validation below!
+ //can't simply use zen::loadBinStream() due to the short-circuit xml-validation below!
std::string stream;
@@ -28,7 +28,7 @@ XmlDoc zen::loadXmlDocument(const Zstring& filename) //throw FileError
stream.resize(bytesRead);
if (!startsWith(stream, xmlBegin) &&
- !startsWith(stream, BYTE_ORDER_MARK_UTF8 + xmlBegin)) //respect BOM!
+ !startsWith(stream, BYTE_ORDER_MARK_UTF8 + xmlBegin)) //allow BOM!
throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtFileName(filename)));
}
@@ -62,10 +62,11 @@ void zen::saveXmlDocument(const XmlDoc& doc, const Zstring& filename) //throw Fi
{
std::string stream = serialize(doc); //noexcept
+ //only update xml file if there are real changes
try
{
if (getFilesize(filename) == stream.size()) //throw FileError
- if (loadBinStream<std::string>(filename) == stream) //throw FileError
+ if (loadBinStream<std::string>(filename) == stream) //throw FileError
return;
}
catch (FileError&) {}
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index c33ea707..8803d83e 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -30,6 +30,13 @@ namespace
class LeakChecker //small test for memory leaks
{
public:
+ static LeakChecker& get()
+ {
+ //meyers singleton: avoid static initialization order problem in global namespace!
+ static LeakChecker inst;
+ return inst;
+ }
+
void insert(const void* ptr, size_t size)
{
std::lock_guard<std::mutex> dummy(lockActStrings);
@@ -44,10 +51,9 @@ public:
reportProblem("Serious Error: No memory available for deallocation at this location!");
}
- static LeakChecker& instance() { static LeakChecker inst; return inst; }
-
private:
LeakChecker() {}
+
~LeakChecker()
{
if (!activeStrings.empty())
@@ -61,7 +67,7 @@ private:
const std::string message = std::string("Memory leak detected!") + "\n\n"
+ "Candidates:\n" + leakingStrings;
#ifdef ZEN_WIN
- MessageBoxA(nullptr, message.c_str(), "Error", 0);
+ MessageBoxA(nullptr, message.c_str(), "Error", MB_SERVICE_NOTIFICATION | MB_ICONERROR);
#else
std::cerr << message;
std::abort();
@@ -75,14 +81,14 @@ private:
static std::string rawMemToString(const void* ptr, size_t size)
{
std::string output(reinterpret_cast<const char*>(ptr), std::min<size_t>(size, 100));
- std::replace(output.begin(), output.end(), '\0', ' '); //don't stop at 0-termination
+ replace(output, '\0', ' '); //don't stop at 0-termination
return output;
}
void reportProblem(const std::string& message) //throw std::logic_error
{
#ifdef ZEN_WIN
- ::MessageBoxA(nullptr, message.c_str(), "Error", 0);
+ ::MessageBoxA(nullptr, message.c_str(), "Error", MB_SERVICE_NOTIFICATION | MB_ICONERROR);
#else
std::cerr << message;
#endif
@@ -93,12 +99,12 @@ private:
zen::hash_map<const void*, size_t> activeStrings;
};
-//caveat: function scope static initialization is not thread-safe in VS 2010! => make sure to call at app start!
-const LeakChecker& dummy = LeakChecker::instance();
+//caveat: function scope static initialization is not thread-safe in VS 2010!
+auto& dummy = LeakChecker::get();
}
-void z_impl::leakCheckerInsert(const void* ptr, size_t size) { LeakChecker::instance().insert(ptr, size); }
-void z_impl::leakCheckerRemove(const void* ptr ) { LeakChecker::instance().remove(ptr); }
+void z_impl::leakCheckerInsert(const void* ptr, size_t size) { LeakChecker::get().insert(ptr, size); }
+void z_impl::leakCheckerRemove(const void* ptr ) { LeakChecker::get().remove(ptr); }
#endif //NDEBUG
@@ -134,18 +140,16 @@ const LCID ZSTRING_INVARIANT_LOCALE = zen::winXpOrLater() ?
//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);
+typedef int (WINAPI* CompareStringOrdinalFunc)(LPCWSTR lpString1, int cchCount1,
+ LPCWSTR lpString2, int cchCount2, BOOL bIgnoreCase);
const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal");
+//caveat: function scope static initialization is not thread-safe in VS 2010!
+//No global dependencies => no static initialization order problem in global namespace!
}
int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_t sizeLhs, size_t sizeRhs)
{
- //caveat: function scope static initialization is not thread-safe in VS 2010!
if (compareStringOrdinal) //this additional test has no noticeable performance impact
{
const int rv = compareStringOrdinal(lhs, //__in LPCWSTR lpString1,
@@ -163,48 +167,46 @@ int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_
//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 auto minSize = static_cast<unsigned int>(std::min(sizeLhs, sizeRhs));
-
- if (minSize > 0) //LCMapString does not allow input sizes of 0!
+ auto copyToUpperCase = [](const wchar_t* strIn, wchar_t* strOut, size_t len)
{
- if (minSize <= MAX_PATH) //performance optimization: stack
- {
- wchar_t bufferA[MAX_PATH];
- wchar_t bufferB[MAX_PATH];
-
- //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,
- lhs, //__in LPCTSTR lpSrcStr,
- minSize, //__in int cchSrc,
- bufferA, //__out LPTSTR lpDestStr,
- MAX_PATH) == 0) //__in int cchDest
- throw std::runtime_error("Error comparing strings (LCMapString).");
-
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, rhs, minSize, bufferB, MAX_PATH) == 0)
- throw std::runtime_error("Error comparing strings (LCMapString).");
-
- const int rv = ::wmemcmp(bufferA, bufferB, minSize);
- if (rv != 0)
- return rv;
- }
- else //use freestore
+ //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,
+ strIn, //__in LPCTSTR lpSrcStr,
+ static_cast<int>(len), //__in int cchSrc,
+ strOut, //__out LPTSTR lpDestStr,
+ static_cast<int>(len)) == 0) //__in int cchDest
+ throw std::runtime_error("Error comparing strings (LCMapString).");
+ };
+
+ const auto minSize = std::min(sizeLhs, sizeRhs);
+
+ auto eval = [&](wchar_t* bufL, wchar_t* bufR)
+ {
+ 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, lhs, minSize, &bufferA[0], minSize) == 0)
- throw std::runtime_error("Error comparing strings (LCMapString: FS).");
-
- if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, rhs, minSize, &bufferB[0], minSize) == 0)
- throw std::runtime_error("Error comparing strings (LCMapString: FS).");
+ copyToUpperCase(lhs, bufL, minSize);
+ copyToUpperCase(rhs, bufR, minSize);
- const int rv = ::wmemcmp(&bufferA[0], &bufferB[0], minSize);
+ const int rv = ::wmemcmp(bufL, bufR, minSize);
if (rv != 0)
return rv;
}
+ return static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs);
+ };
+
+ if (minSize <= MAX_PATH) //performance optimization: stack
+ {
+ wchar_t bufferL[MAX_PATH] = {};
+ wchar_t bufferR[MAX_PATH] = {};
+ return eval(bufferL, bufferR);
+ }
+ else //use freestore
+ {
+ std::vector<wchar_t> bufferL(minSize);
+ std::vector<wchar_t> bufferR(minSize);
+ return eval(&bufferL[0], &bufferR[0]);
}
- return static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs);
}
}
bgstack15