summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/IFileOperation/file_op.cpp30
-rw-r--r--zen/com_error.h31
-rw-r--r--zen/com_ptr.h2
-rw-r--r--zen/dst_hack.cpp22
-rw-r--r--zen/file_handling.cpp266
-rw-r--r--zen/file_handling.h10
-rw-r--r--zen/file_traverser.cpp32
-rw-r--r--zen/file_traverser.h5
-rw-r--r--zen/i18n.h2
-rw-r--r--zen/optional.h2
-rw-r--r--zen/osx_error.h27
-rw-r--r--zen/osx_string.h82
-rw-r--r--zen/osx_throw_exception.h56
-rw-r--r--zen/privilege.cpp2
-rw-r--r--zen/recycler.cpp1
-rw-r--r--zen/scope_guard.h10
-rw-r--r--zen/thread.h2
-rw-r--r--zen/zstring.cpp12
-rw-r--r--zen/zstring.h12
19 files changed, 407 insertions, 199 deletions
diff --git a/zen/IFileOperation/file_op.cpp b/zen/IFileOperation/file_op.cpp
index 0691ac5b..b3990ee0 100644
--- a/zen/IFileOperation/file_op.cpp
+++ b/zen/IFileOperation/file_op.cpp
@@ -166,14 +166,14 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
void* sink)
{
ComPtr<IFileOperation> fileOp;
- ZEN_CHECK_COM(::CoCreateInstance(CLSID_FileOperation, //throw ComError
+ ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw ComError
nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(fileOp.init())));
// Set the operation flags. Turn off all UI from being shown to the user during the
// operation. This includes error, confirmation and progress dialogs.
- ZEN_CHECK_COM(fileOp->SetOperationFlags(FOF_ALLOWUNDO |
+ ZEN_COM_CHECK(fileOp->SetOperationFlags(FOF_ALLOWUNDO |
FOF_NOCONFIRMATION |
FOF_SILENT | //no progress dialog box
FOF_NOERRORUI |
@@ -192,7 +192,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
throw ComError(L"Error creating RecyclerProgressCallback.", E_OUTOFMEMORY);
DWORD callbackID = 0;
- ZEN_CHECK_COM(fileOp->Advise(opProgress.get(), &callbackID));
+ ZEN_COM_CHECK(fileOp->Advise(opProgress.get(), &callbackID));
ZEN_ON_SCOPE_EXIT(fileOp->Unadvise(callbackID)); //RecyclerProgressCallback might outlive current scope, so cut access to "callback, sink"
int operationCount = 0;
@@ -226,7 +226,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
throw ComError(std::wstring(L"Error calling \"SHCreateItemFromParsingName\" for file:\n") + L"\'" + fileNames[i] + L"\'.", hr);
}
- ZEN_CHECK_COM(fileOp->DeleteItem(psiFile.get(), nullptr));
+ ZEN_COM_CHECK(fileOp->DeleteItem(psiFile.get(), nullptr));
++operationCount;
}
@@ -237,7 +237,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
//perform planned operations
try
{
- ZEN_CHECK_COM(fileOp->PerformOperations());
+ ZEN_COM_CHECK(fileOp->PerformOperations());
}
catch (const ComError&)
{
@@ -263,7 +263,7 @@ void moveToRecycleBin(const wchar_t* fileNames[], //throw ComError
//if FOF_NOERRORUI without FOFX_EARLYFAILURE is set, PerformOperations() can return with success despite errors, but sets the following "aborted" flag instead
BOOL pfAnyOperationsAborted = FALSE;
- ZEN_CHECK_COM(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted));
+ ZEN_COM_CHECK(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted));
if (pfAnyOperationsAborted == TRUE)
throw ComError(L"Operation did not complete successfully.");
@@ -274,7 +274,7 @@ void copyFile(const wchar_t* sourceFile, //throw ComError
const wchar_t* targetFile)
{
ComPtr<IFileOperation> fileOp;
- ZEN_CHECK_COM(::CoCreateInstance(CLSID_FileOperation, //throw ComError
+ ZEN_COM_CHECK(::CoCreateInstance(CLSID_FileOperation, //throw ComError
nullptr,
CLSCTX_ALL,
IID_PPV_ARGS(fileOp.init())));
@@ -283,7 +283,7 @@ void copyFile(const wchar_t* sourceFile, //throw ComError
// from being shown to the user during the
// operation. This includes error, confirmation
// and progress dialogs.
- ZEN_CHECK_COM(fileOp->SetOperationFlags(FOF_NOCONFIRMATION | //throw ComError
+ ZEN_COM_CHECK(fileOp->SetOperationFlags(FOF_NOCONFIRMATION | //throw ComError
FOF_SILENT |
FOFX_EARLYFAILURE |
FOF_NOERRORUI));
@@ -315,14 +315,14 @@ void copyFile(const wchar_t* sourceFile, //throw ComError
}
//schedule file copy operation
- ZEN_CHECK_COM(fileOp->CopyItem(psiSourceFile.get(), psiTargetFolder.get(), targetFileNameShort.c_str(), nullptr));
+ ZEN_COM_CHECK(fileOp->CopyItem(psiSourceFile.get(), psiTargetFolder.get(), targetFileNameShort.c_str(), nullptr));
//perform actual operations
- ZEN_CHECK_COM(fileOp->PerformOperations());
+ ZEN_COM_CHECK(fileOp->PerformOperations());
//check if errors occured: if FOFX_EARLYFAILURE is not used, PerformOperations() can return with success despite errors!
BOOL pfAnyOperationsAborted = FALSE;
- ZEN_CHECK_COM(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted));
+ ZEN_COM_CHECK(fileOp->GetAnyOperationsAborted(&pfAnyOperationsAborted));
if (pfAnyOperationsAborted == TRUE)
throw ComError(L"Operation did not complete successfully.");
@@ -332,10 +332,10 @@ void copyFile(const wchar_t* sourceFile, //throw ComError
void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw ComError
{
ComPtr<IShellFolder> desktopFolder;
- ZEN_CHECK_COM(::SHGetDesktopFolder(desktopFolder.init())); //throw ComError
+ ZEN_COM_CHECK(::SHGetDesktopFolder(desktopFolder.init())); //throw ComError
PIDLIST_RELATIVE pidlFolder = nullptr;
- ZEN_CHECK_COM(desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd,
+ ZEN_COM_CHECK(desktopFolder->ParseDisplayName(nullptr, // [in] HWND hwnd,
nullptr, // [in] IBindCtx *pbc,
const_cast<LPWSTR>(dirname), // [in] LPWSTR pszDisplayName,
nullptr, // [out] ULONG *pchEaten,
@@ -344,11 +344,11 @@ void getFolderClsid(const wchar_t* dirname, CLSID& pathCLSID) //throw ComError
ZEN_ON_SCOPE_EXIT(::ILFree(pidlFolder)); //older version: ::CoTaskMemFree
ComPtr<IPersist> persistFolder;
- ZEN_CHECK_COM(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl,
+ ZEN_COM_CHECK(desktopFolder->BindToObject(pidlFolder, // [in] PCUIDLIST_RELATIVE pidl,
nullptr, // [in] IBindCtx *pbc,
IID_PPV_ARGS(persistFolder.init()))); //throw ComError
- ZEN_CHECK_COM(persistFolder->GetClassID(&pathCLSID)); //throw ComError
+ ZEN_COM_CHECK(persistFolder->GetClassID(&pathCLSID)); //throw ComError
}
diff --git a/zen/com_error.h b/zen/com_error.h
index e6f5b492..0e0448a7 100644
--- a/zen/com_error.h
+++ b/zen/com_error.h
@@ -26,10 +26,11 @@ private:
std::wstring msg_;
};
-#define ZEN_CHECK_COM(func) ZEN_CHECK_COM_ERROR(func, #func) //throw ComError
-/*Convenience Macro checking for COM errors:
+//Convenience Macros checking for COM errors:
-Example: ZEN_CHECK_COM(backupComp->InitializeForBackup());
+#define ZEN_COM_CHECK(func) ZEN_COM_CHECK_IMPL(func, #func) //throw ComError
+/*
+Example: ZEN_COM_CHECK(backupComp->InitializeForBackup());
Equivalent to:
{
@@ -39,6 +40,15 @@ Equivalent to:
}
*/
+#define ZEN_COM_ASSERT(obj) ZEN_COM_ASSERT_IMPL(obj, #obj) //throw ComError
+/*
+Example: ZEN_COM_ASSERT(obj);
+
+Equivalent to:
+ if (!obj)
+ throw ComError(L"Assertion failed: \"obj\".", E_FAIL);
+*/
+
@@ -218,16 +228,13 @@ std::wstring generateErrorMsg(const std::wstring& input, HRESULT hr)
}
-#define ZEN_CHECK_COM_ERROR(func, txt) \
- { \
- HRESULT hrInternal = func; \
- if (FAILED(hrInternal)) \
- throw ComError(L"Error calling \"" ## ZEN_CONCAT_SUB(L, txt) ## L"\".", hrInternal); \
+#define ZEN_COM_CHECK_IMPL(func, txt) \
+ { \
+ HRESULT hrInternal = func; \
+ if (FAILED(hrInternal)) \
+ throw zen::ComError(std::wstring(L"Error calling \"") + L ## txt + L"\".", hrInternal); \
}
-#ifndef ZEN_CONCAT //redeclare those macros: avoid dependency to scope_guard.h
-#define ZEN_CONCAT_SUB(X, Y) X ## Y
-#define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y)
-#endif
+#define ZEN_COM_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::ComError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".", E_FAIL);
}
#endif //COM_ERROR_HEADER
diff --git a/zen/com_ptr.h b/zen/com_ptr.h
index 030a0801..9944ea56 100644
--- a/zen/com_ptr.h
+++ b/zen/com_ptr.h
@@ -36,7 +36,7 @@ class ComPtr
public:
ComPtr() : ptr(nullptr) {} //
ComPtr(const ComPtr& other) : ptr(other.ptr) { if (ptr) ptr->AddRef(); } //noexcept in C++11
- ComPtr( ComPtr&& other) : ptr(other.ptr) { other.ptr = nullptr; } //
+ ComPtr( ComPtr&& other) : ptr(other.release()) {} //
~ComPtr() { if (ptr) ptr->Release(); } //has exception spec of compiler-generated destructor by default
ComPtr& operator=(const ComPtr& other) { ComPtr(other).swap(*this); return *this; } //noexcept in C++11
diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp
index 6e5c2230..a70ef13b 100644
--- a/zen/dst_hack.cpp
+++ b/zen/dst_hack.cpp
@@ -52,22 +52,22 @@ Zstring getVolumeName(const Zstring& filename)
bool dst::isFatDrive(const Zstring& fileName) //throw()
{
- const size_t BUFFER_SIZE = MAX_PATH + 1;
- wchar_t fsName[BUFFER_SIZE];
-
const Zstring volumePath = getVolumeName(fileName);
if (volumePath.empty())
return false;
+ const DWORD bufferSize = MAX_PATH + 1;
+ wchar_t fsName[bufferSize];
+
//suprisingly fast: ca. 0.03 ms per call!
- if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName,
- nullptr, //__out LPTSTR lpVolumeNameBuffer,
- 0, //__in DWORD nVolumeNameSize,
- nullptr, //__out_opt LPDWORD lpVolumeSerialNumber,
- nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
- nullptr, //__out_opt LPDWORD lpFileSystemFlags,
- fsName, //__out LPTSTR lpFileSystemNameBuffer,
- BUFFER_SIZE)) //__in DWORD nFileSystemNameSize
+ if (!::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName,
+ nullptr, //__out LPTSTR lpVolumeNameBuffer,
+ 0, //__in DWORD nVolumeNameSize,
+ nullptr, //__out_opt LPDWORD lpVolumeSerialNumber,
+ nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
+ nullptr, //__out_opt LPDWORD lpFileSystemFlags,
+ fsName, //__out LPTSTR lpFileSystemNameBuffer,
+ bufferSize)) //__in DWORD nFileSystemNameSize
{
assert(false); //shouldn't happen
return false;
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index bf829010..3565700a 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -40,7 +40,7 @@
#if defined FFS_LINUX || defined FFS_MAC
#include <sys/stat.h>
-//#include <sys/time.h>
+//#include <sys/time.h>
#endif
using namespace zen;
@@ -250,26 +250,24 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
{
//note: this even works for network shares: \\share\dirname
- const DWORD BUFFER_SIZE = 10000;
- std::vector<wchar_t> buffer(BUFFER_SIZE);
+ const DWORD bufferSize = 10000;
+ std::vector<wchar_t> buffer(bufferSize);
//full pathName need not yet exist!
if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
&buffer[0], //__out LPTSTR lpszVolumePathName,
- BUFFER_SIZE)) //__in DWORD cchBufferLength
+ bufferSize)) //__in DWORD cchBufferLength
return 0;
- Zstring volumePath = appendSeparator(&buffer[0]);
-
DWORD volumeSerial = 0;
- if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName,
- nullptr, //__out LPTSTR lpVolumeNameBuffer,
- 0, //__in DWORD nVolumeNameSize,
- &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber,
- nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
- nullptr, //__out_opt LPDWORD lpFileSystemFlags,
- nullptr, //__out LPTSTR lpFileSystemNameBuffer,
- 0)) //__in DWORD nFileSystemNameSize
+ if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName,
+ nullptr, //__out LPTSTR lpVolumeNameBuffer,
+ 0, //__in DWORD nVolumeNameSize,
+ &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber,
+ nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
+ nullptr, //__out_opt LPDWORD lpFileSystemFlags,
+ nullptr, //__out LPTSTR lpFileSystemNameBuffer,
+ 0)) //__in DWORD nFileSystemNameSize
return 0;
return volumeSerial;
@@ -376,7 +374,7 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File
{
DWORD lastError = ::GetLastError();
- const std::wstring shortMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", fmtFileName(oldName)), L"%y", fmtFileName(newName));
+ const std::wstring shortMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName));
if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message!
lastError == ERROR_LOCK_VIOLATION)
@@ -429,7 +427,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;
- std::wstring errorMessage = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", fmtFileName(oldName)), L"%y", fmtFileName(newName)) +
+ std::wstring errorMessage = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)) +
L"\n\n" + getLastErrorFormatted(lastError);
if (lastError == EXDEV)
@@ -513,7 +511,7 @@ bool have8dot3NameClash(const Zstring& filename)
return false;
}
-class Fix8Dot3NameClash
+class Fix8Dot3NameClash //throw FileError
{
public:
Fix8Dot3NameClash(const Zstring& filename)
@@ -560,7 +558,7 @@ void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw Fil
//try to handle issues with already existing short 8.3 file names on Windows
if (have8dot3NameClash(newName))
{
- Fix8Dot3NameClash dummy(newName); //move clashing filename to the side
+ Fix8Dot3NameClash dummy(newName); //throw FileError; move clashing filename to the side
//now try again...
renameFile_sub(oldName, newName); //throw FileError
return;
@@ -1259,11 +1257,96 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
}
-void createDirectoryStraight(const Zstring& directory, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
+void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTargetExisting
+{
+ assert(!endsWith(directory, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input!
+
+ try
+ {
+ makeDirectoryPlain(directory, Zstring(), false); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
+ }
+ catch (const ErrorTargetPathMissing&)
+ {
+ //we need to create parent directories first
+ const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR);
+ if (!dirParent.empty())
+ {
+ //recurse...
+ try
+ {
+ makeDirectoryRecursively(dirParent); //throw FileError, (ErrorTargetExisting)
+ }
+ catch (const ErrorTargetExisting& e) { throw FileError(e.toString()); }
+ //yes it's pathological, but we do not want to emit ErrorTargetExisting when creating parent directories!
+
+ //now try again...
+ makeDirectoryPlain(directory, Zstring(), false); //throw FileError, ErrorTargetExisting, (ErrorTargetPathMissing)
+ return;
+ }
+ throw;
+ }
+}
+}
+
+
+void zen::makeDirectory(const Zstring& directory, bool failIfExists) //throw FileError, ErrorTargetExisting
+{
+ //remove trailing separator (even for C:\ root directories)
+ const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ?
+ beforeLast(directory, FILE_NAME_SEPARATOR) :
+ directory;
+
+ try
+ {
+ makeDirectoryRecursively(dirFormatted); //FileError, ErrorTargetExisting
+ }
+ catch (const ErrorTargetExisting&)
+ {
+ //avoid any file system race-condition by *not* checking directory existence again here!!!
+ if (failIfExists)
+ throw;
+ }
+ catch (const FileError&)
+ {
+ if (dirExists(directory)) //a file system race-condition!
+ {
+ /*
+ could there be situations where a directory/network path exists,
+ but creation fails with error different than "ErrorTargetExisting"??
+ - creation of C:\ fails with ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS
+ */
+ assert(false);
+ if (failIfExists)
+ throw; //do NOT convert to ErrorTargetExisting: if "failIfExists", not getting a ErrorTargetExisting *atomically* is unexpected!
+ }
+ else
+ throw;
+ }
+}
+
+
+void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
const Zstring& templateDir,
bool copyFilePermissions)
{
#ifdef FFS_WIN
+ //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS!
+ Zstring dirTmp = removeLongPathPrefix(endsWith(directory, FILE_NAME_SEPARATOR) ?
+ beforeLast(directory, FILE_NAME_SEPARATOR) :
+ directory);
+ if (dirTmp.size() == 2 &&
+ std::iswalpha(dirTmp[0]) && dirTmp[1] == L':')
+ {
+ dirTmp += FILE_NAME_SEPARATOR; //we do not support "C:" to represent a relative path!
+
+ const ErrorCode lastError = dirExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND;
+
+ const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp)) + L"\n\n" + getLastErrorFormatted(lastError);
+ if (lastError == ERROR_ALREADY_EXISTS)
+ throw ErrorTargetExisting(msg);
+ throw FileError(msg); //[!] this is NOT a ErrorTargetPathMissing case!
+ }
+
//don't use ::CreateDirectoryEx:
//- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage
//- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)!
@@ -1271,14 +1354,30 @@ void createDirectoryStraight(const Zstring& directory, //throw FileError, ErrorT
if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), //__in LPCTSTR lpPathName,
nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes
{
- const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted();
- const ErrorCode lastError = getLastError();
+ ErrorCode lastError = getLastError();
+ //handle issues with already existing short 8.3 file names on Windows
if (lastError == ERROR_ALREADY_EXISTS)
- throw ErrorTargetExisting(msg);
- else if (lastError == ERROR_PATH_NOT_FOUND)
- throw ErrorTargetPathMissing(msg);
- throw FileError(msg);
+ if (have8dot3NameClash(directory))
+ {
+ Fix8Dot3NameClash dummy(directory); //throw FileError; move clashing object to the side
+
+ //now try again...
+ if (::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), nullptr))
+ lastError = ERROR_SUCCESS;
+ else
+ lastError = getLastError();
+ }
+
+ if (lastError != ERROR_SUCCESS)
+ {
+ const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(lastError);
+ if (lastError == ERROR_ALREADY_EXISTS)
+ throw ErrorTargetExisting(msg);
+ else if (lastError == ERROR_PATH_NOT_FOUND)
+ throw ErrorTargetPathMissing(msg);
+ throw FileError(msg);
+ }
}
#elif defined FFS_LINUX || defined FFS_MAC
@@ -1301,19 +1400,17 @@ void createDirectoryStraight(const Zstring& directory, //throw FileError, ErrorT
//try to copy file attributes
Zstring sourcePath;
- if (symlinkExists(templateDir)) //dereference symlink!
- {
+ if (symlinkExists(templateDir))
try
{
//get target directory of symbolic link
sourcePath = getSymlinkTargetPath(templateDir); //throw FileError
}
catch (FileError&) {} //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error...
- }
- else //no symbolic link
+ else
sourcePath = templateDir;
- //try to copy file attributes
+ //*try* to copy file attributes
if (!sourcePath.empty())
{
const DWORD sourceAttr = ::GetFileAttributes(applyLongPathPrefix(sourcePath).c_str());
@@ -1368,95 +1465,6 @@ void createDirectoryStraight(const Zstring& directory, //throw FileError, ErrorT
}
-void createDirectoryRecursively(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions) //FileError, ErrorTargetExisting
-{
- try
- {
- createDirectoryStraight(directory, templateDir, copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
- }
- catch (const ErrorTargetExisting&)
- {
-#ifdef FFS_WIN
- //handle issues with already existing short 8.3 file names on Windows
- if (have8dot3NameClash(directory))
- {
- Fix8Dot3NameClash dummy(directory); //move clashing object to the side
-
- //now try again...
- createDirectoryStraight(directory, templateDir, copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
- return;
- }
-#endif
- throw;
- }
- catch (const ErrorTargetPathMissing&)
- {
- //we need to create parent directories first
- const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR);
- if (!dirParent.empty())
- {
- //call function recursively
- const Zstring templateParent = beforeLast(templateDir, FILE_NAME_SEPARATOR); //returns empty string if ch not found
- createDirectoryRecursively(dirParent, templateParent, copyFilePermissions); //throw
-
- //now try again...
- createDirectoryStraight(directory, templateDir, copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
- return;
- }
- throw;
- }
-}
-}
-
-
-void zen::makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions) //FileError, ErrorTargetExisting
-{
-#ifdef FFS_WIN
- //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS!
- const Zstring dirTmp = removeLongPathPrefix(directory);
- if (dirTmp.size() == 3 &&
- std::iswalpha(dirTmp[0]) && endsWith(dirTmp, L":\\"))
- {
- const ErrorCode lastError = dirExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND;
-
- const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp)) + L"\n\n" + getLastErrorFormatted(lastError);
- if (lastError == ERROR_ALREADY_EXISTS)
- throw ErrorTargetExisting(msg);
- throw FileError(msg);
- }
-#endif
- //remove trailing separator (except for volume root directories!)
- const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ?
- beforeLast(directory, FILE_NAME_SEPARATOR) :
- directory;
-
- const Zstring templateFormatted = endsWith(templateDir, FILE_NAME_SEPARATOR) ?
- beforeLast(templateDir, FILE_NAME_SEPARATOR) :
- templateDir;
-
- createDirectoryRecursively(dirFormatted, templateFormatted, copyFilePermissions); //FileError, ErrorTargetExisting
-}
-
-
-void zen::makeDirectory(const Zstring& directory)
-{
- try
- {
- makeNewDirectory(directory, Zstring(), false); //FileError, ErrorTargetExisting
- }
- catch (const FileError& e)
- {
- assert(dynamic_cast<const ErrorTargetExisting*>(&e));
- (void)e;
- //could there be situations where a directory/network path exists, but creation fails with
- //error different than "ErrorTargetExisting"?? => better catch all "FileError" and check existence again
- if (dirExists(directory)) //technically a file system race-condition!
- return;
- throw;
- }
-}
-
-
void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions) //throw FileError
{
const Zstring linkPath = getSymlinkRawTargetString(sourceLink); //accept broken symlinks; throw FileError
@@ -1481,7 +1489,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
#elif defined FFS_LINUX || defined FFS_MAC
if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0)
#endif
- throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", fmtFileName(sourceLink)), L"%y", fmtFileName(targetLink)) +
+ throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)) +
L"\n\n" + getLastErrorFormatted());
//allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist
@@ -1996,15 +2004,8 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
//called after copy operation is finished - note: for 0-sized files this callback is invoked just ONCE!
//if (totalFileSize.QuadPart == totalBytesTransferred.QuadPart && dwStreamNumber == 1) {}
- if (cbd.userCallback)
- {
- //some odd check for some possible(?) error condition
- if (totalBytesTransferred.QuadPart < 0) //let's see if someone answers the call...
- ::MessageBox(nullptr, L"You've just discovered a bug in WIN32 API function \"CopyFileEx\"! \n\n\
- Please write a mail to the author of FreeFileSync at zenju@gmx.de and simply state that\n\
- \"totalBytesTransferred.HighPart can be below zero\"!\n\n\
- This will then be handled in future versions of FreeFileSync.\n\nThanks -Zenju",
- nullptr, 0);
+ if (cbd.userCallback &&
+ totalBytesTransferred.QuadPart >= 0) //should be always true, but let's still check
try
{
cbd.userCallback->updateCopyStatus(totalBytesTransferred.QuadPart - cbd.bytesReported); //throw X!
@@ -2017,13 +2018,12 @@ DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize,
cbd.errorHandler.reportUserException(*cbd.userCallback);
return PROGRESS_CANCEL;
}
- }
return PROGRESS_CONTINUE;
}
const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destination is not supported with Windows 2000
-//const bool supportUnbufferedCopy = vistaOrLater();
+//const bool supportUnbufferedCopy = vistaOrLater();
//caveat: function scope static initialization is not thread-safe in VS 2010!
@@ -2074,7 +2074,7 @@ void copyFileWindowsDefault(const Zstring& sourceFile,
throw ErrorShouldCopyAsSparse(L"sparse dummy value2");
//assemble error message...
- std::wstring errorMessage = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", fmtFileName(sourceFile)), L"%y", fmtFileName(targetFile)) +
+ std::wstring errorMessage = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile)) +
L"\n\n" + getLastErrorFormatted(lastError);
//if file is locked throw "ErrorFileLocked" instead!
@@ -2162,7 +2162,7 @@ void copyFileWindows(const Zstring& sourceFile, const Zstring& targetFile, Callb
//try to handle issues with already existing short 8.3 file names on Windows
if (have8dot3NameClash(targetFile))
{
- Fix8Dot3NameClash dummy(targetFile); //move clashing filename to the side
+ Fix8Dot3NameClash dummy(targetFile); //throw FileError; move clashing filename to the side
copyFileWindowsSelectRoutine(sourceFile, targetFile, callback, sourceAttr); //throw FileError; the short filename name clash is solved, this should work now
return;
}
diff --git a/zen/file_handling.h b/zen/file_handling.h
index 5739dc2a..a0bd9b5b 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -55,9 +55,13 @@ void renameFile(const Zstring& oldName, const Zstring& newName); //throw FileErr
bool supportsPermissions(const Zstring& dirname); //throw FileError, derefernces symlinks
-//creates superdirectories automatically:
-void makeDirectory(const Zstring& directory); //throw FileError; do nothing if directory already exists!
-void makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting
+//if parent directory not existing: create recursively:
+void makeDirectory(const Zstring& directory, bool failIfExists = false); //throw FileError, ErrorTargetExisting
+
+//fail if already existing or parent not existing:
+//directory should not end with path separator
+//templateDir may be empty
+void makeDirectoryPlain(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
struct FileAttrib
{
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index b39f8416..7093c44a 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -17,7 +17,11 @@
#include "dll.h"
#include "FindFilePlus/find_file_plus.h"
-#elif defined FFS_LINUX || defined FFS_MAC
+#elif defined FFS_MAC
+#include <zen/osx_string.h>
+#endif
+
+#if defined FFS_LINUX || defined FFS_MAC
#include <sys/stat.h>
#include <dirent.h>
#endif
@@ -284,7 +288,7 @@ struct FilePlusTraverser
*/
if (lastError == ERROR_NOT_SUPPORTED)
{
- fb(); //fallback should apply to whole directory sub-tree!
+ fb(); //fallback should apply to whole directory sub-tree! => client needs to handle duplicate file notifications!
return false;
}
@@ -555,11 +559,30 @@ private:
return;
//don't return "." and ".."
- const char* const shortName = dirEntry->d_name; //evaluate dirEntry *before* going into recursion => we use a single "buffer"!
+ const char* 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)))
continue;
+#ifdef FFS_MAC
+ //some file system abstraction layers fail to properly return decomposed UTF8: http://developer.apple.com/library/mac/#qa/qa1173/_index.html
+ //so we need to do it ourselves; perf: ~600 ns per conversion
+ //note: it's not sufficient to apply this in z_impl::compareFilenamesNoCase: if UTF8 forms differ, FFS assumes a rename in case sensitivity and
+ // will try to propagate the rename => this won't work if target drive reports a particular UTF8 form only!
+ if (CFStringRef cfStr = osx::createCFString(shortName))
+ {
+ ZEN_ON_SCOPE_EXIT(::CFRelease(cfStr));
+ CFIndex lenMax = ::CFStringGetMaximumSizeOfFileSystemRepresentation(cfStr); //"could be much larger than the actual space required" => don't store in Zstring
+ if (lenMax > 0)
+ {
+ bufferUtfDecomposed.resize(lenMax);
+ if (::CFStringGetFileSystemRepresentation(cfStr, &bufferUtfDecomposed[0], lenMax)) //get decomposed UTF form (verified!) despite ambiguous documentation
+ shortName = &bufferUtfDecomposed[0];
+ }
+ }
+ //const char* sampleDecomposed = "\x6f\xcc\x81.txt";
+ //const char* samplePrecomposed = "\xc3\xb3.txt";
+#endif
const Zstring& fullName = appendSeparator(directory) + shortName;
struct ::stat statData = {};
@@ -645,6 +668,9 @@ private:
}
std::vector<char> buffer;
+#ifdef FFS_MAC
+ std::vector<char> bufferUtfDecomposed;
+#endif
};
#endif
}
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index 7e566075..97fb0e9f 100644
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -62,12 +62,13 @@ struct DstHackCallback
virtual void requestUiRefresh(const Zstring& filename) = 0; //applying DST hack imposes significant one-time performance drawback => callback to inform user
};
#elif defined FFS_LINUX || defined FFS_MAC
-struct DstHackCallback; //DST hack not required on Linux
+struct DstHackCallback; //DST hack not required on Unix
#endif
//custom traverser with detail information about files
+//Win: client needs to handle duplicate file notifications! (FilePlusTraverser fallback)
//directory may end with PATH_SEPARATOR
-void traverseFolder(const Zstring& directory, //throw();
+void traverseFolder(const Zstring& directory, //throw()
TraverseCallback& sink,
DstHackCallback* dstCallback = nullptr); //apply DST hack if callback is supplied
}
diff --git a/zen/i18n.h b/zen/i18n.h
index 217d506c..66e5fa53 100644
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -22,7 +22,7 @@
namespace zen
{
-//implement handler to enable program wide localizations
+//implement handler to enable program wide localizations: implement THREAD-SAFE ACCESS!
struct TranslationHandler
{
virtual ~TranslationHandler() {}
diff --git a/zen/optional.h b/zen/optional.h
index 4d85e53a..a6a53103 100644
--- a/zen/optional.h
+++ b/zen/optional.h
@@ -10,7 +10,7 @@
namespace zen
{
/*
-Optional return value with static memory allocation!
+Optional return value without heap memory allocation!
-> interface like a pointer, performance like a value
Usage:
diff --git a/zen/osx_error.h b/zen/osx_error.h
new file mode 100644
index 00000000..4b0aeb3b
--- /dev/null
+++ b/zen/osx_error.h
@@ -0,0 +1,27 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef OSX_ERRROR_834270598342753425
+#define OSX_ERRROR_834270598342753425
+
+#include <string>
+
+namespace osx
+{
+class OsxError //Exception base class used to notify file/directory copy/delete errors
+{
+public:
+ explicit OsxError(const std::wstring& message) : msg(message) {}
+ virtual ~OsxError() {}
+
+ const std::wstring& toString() const { return msg; }
+
+private:
+ std::wstring msg;
+};
+}
+
+#endif //OSX_ERRROR_834270598342753425
diff --git a/zen/osx_string.h b/zen/osx_string.h
new file mode 100644
index 00000000..a5c0849e
--- /dev/null
+++ b/zen/osx_string.h
@@ -0,0 +1,82 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef OSX_STRING_1873641732143214324
+#define OSX_STRING_1873641732143214324
+
+#include <zen/zstring.h>
+#include <CoreFoundation/CoreFoundation.h> //CFString
+
+namespace osx
+{
+Zstring cfStringToZstring(const CFStringRef& cfStr);
+
+CFStringRef createCFString (const char* utf8Str); //returns nullptr on error
+CFMutableStringRef createMutableCFString(const char* utf8Str); //pass ownership! => ZEN_ON_SCOPE_EXIT(::CFRelease(utf8Str));
+
+
+
+
+
+
+
+
+
+
+
+
+
+//################# implementation #####################
+inline
+Zstring cfStringToZstring(const CFStringRef& cfStr)
+{
+ if (cfStr)
+ {
+ //perf: try to get away cheap:
+ if (const char* utf8Str = ::CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8))
+ return utf8Str;
+
+ CFIndex length = ::CFStringGetLength(cfStr);
+ if (length > 0)
+ {
+ CFIndex bufferSize = ::CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
+ Zstring buffer;
+ buffer.resize(bufferSize);
+
+ if (::CFStringGetCString(cfStr, &*buffer.begin(), bufferSize, kCFStringEncodingUTF8))
+ {
+ buffer.resize(zen::strLength(&*buffer.begin())); //caveat: memory consumption of returned string!
+ return buffer;
+ }
+ }
+ }
+ return Zstring();
+}
+
+
+inline
+CFStringRef createCFString(const char* utf8Str)
+{
+ //don't bother with CFStringCreateWithBytes: it's slightly slower, despite passing length info
+ return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc,
+ utf8Str, //const char *cStr,
+ kCFStringEncodingUTF8); //CFStringEncoding encoding
+}
+
+
+inline
+CFMutableStringRef createMutableCFString(const char* utf8Str)
+{
+ if (CFMutableStringRef strRef = ::CFStringCreateMutable(NULL, 0))
+ {
+ ::CFStringAppendCString(strRef, utf8Str, kCFStringEncodingUTF8);
+ return strRef;
+ }
+ return nullptr;
+}
+}
+
+#endif //OSX_STRING_1873641732143214324
diff --git a/zen/osx_throw_exception.h b/zen/osx_throw_exception.h
new file mode 100644
index 00000000..cb458974
--- /dev/null
+++ b/zen/osx_throw_exception.h
@@ -0,0 +1,56 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
+// **************************************************************************
+
+#ifndef OSX_EXCEPTION_89274305834255
+#define OSX_EXCEPTION_89274305834255
+
+#import <Cocoa/Cocoa.h>
+#include <zen/osx_error.h>
+#include <zen/utf.h>
+
+namespace osx
+{
+//for use in Objective C implementation files only!
+void throwOsxError(NSException* e); //throw OsxError
+
+#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw OsxError
+/*
+Example: ZEN_COM_ASSERT(obj);
+
+Equivalent to:
+ if (!obj)
+ throw OsxError(L"Assertion failed: \"obj\".");
+*/
+
+
+
+
+
+
+//######################## implmentation ############################
+inline
+void throwOsxError(NSException* e) //throw OsxError
+{
+ std::string msg;
+ if (const char* name = [[e name ] cStringUsingEncoding:NSUTF8StringEncoding]) //"const char*" NOT owned by us!
+ msg += name;
+ if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding])
+ {
+ msg += "\n";
+ msg += descr;
+ }
+ throw OsxError(zen::utfCvrtTo<std::wstring>(msg));
+ /*
+ e.g.
+ NSInvalidArgumentException
+ *** +[NSString stringWithCString:encoding:]: NULL cString
+ */
+}
+}
+
+#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw osx::OsxError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\".");
+
+#endif //OSX_EXCEPTION_89274305834255
diff --git a/zen/privilege.cpp b/zen/privilege.cpp
index 288a1480..b474958e 100644
--- a/zen/privilege.cpp
+++ b/zen/privilege.cpp
@@ -99,7 +99,7 @@ public:
private:
Privileges() {}
Privileges(Privileges&);
- void operator=(Privileges&);
+ Privileges& operator=(const Privileges&);
~Privileges() //clean up: deactivate all privileges that have been activated by this application
{
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 2a82cd24..c062a26c 100644
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -13,7 +13,6 @@
//#include <algorithm>
//#include <functional>
#include <zen/dll.h>
-#include <zen/win.h> //includes "windows.h"
#include <zen/assert_static.h>
#include <zen/win_ver.h>
#include <zen/long_path_prefix.h>
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index 81f47f87..ca05d39d 100644
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -15,7 +15,7 @@ namespace zen
{
//Scope Guard
/*
- zen::ScopeGuard lockAio = zen::makeGuard([&]() { ::CancelIo(hDir); });
+ zen::ScopeGuard lockAio = zen::makeGuard([&] { ::CancelIo(hDir); });
...
lockAio.dismiss();
*/
@@ -33,13 +33,13 @@ public:
protected:
ScopeGuardBase() : dismissed_(false) {}
ScopeGuardBase(ScopeGuardBase&& other) : dismissed_(other.dismissed_) { other.dismiss(); } //take over responsibility
- ~ScopeGuardBase() {}
+ ~ScopeGuardBase() {} //[!] protected non-virtual base class destructor
bool isDismissed() const { return dismissed_; }
private:
- ScopeGuardBase(const ScopeGuardBase&); //delete
- ScopeGuardBase& operator=(const ScopeGuardBase&); // = delete;
+ ScopeGuardBase (const ScopeGuardBase&); // = delete
+ ScopeGuardBase& operator=(const ScopeGuardBase&); //
bool dismissed_;
};
@@ -76,6 +76,6 @@ ScopeGuardImpl<typename std::decay<F>::type> makeGuard(F&& fun) { return ScopeGu
#define ZEN_CONCAT_SUB(X, Y) X ## Y
#define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y)
-#define ZEN_ON_SCOPE_EXIT(X) zen::ScopeGuard ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
+#define ZEN_ON_SCOPE_EXIT(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
#endif //ZEN_SCOPEGUARD_8971632487321434
diff --git a/zen/thread.h b/zen/thread.h
index ae865cc8..db9cf3a3 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -7,7 +7,7 @@
#ifndef BOOST_THREAD_WRAP_H
#define BOOST_THREAD_WRAP_H
-//temporary solution until C++11 thread becomes fully available
+//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>
//fix this pathetic boost thread warning mess
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index b371e598..262df49e 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -12,6 +12,7 @@
#include "win_ver.h"
#elif defined FFS_MAC
+//#include <zen/scope_guard.h>
#include <ctype.h> //toupper()
#endif
@@ -132,7 +133,7 @@ time per call | function
#ifdef FFS_WIN
namespace
{
-#ifndef LOCALE_INVARIANT //not known to MinGW
+#ifdef __MINGW32__ //MinGW is clueless...
#define LOCALE_INVARIANT 0x007f
#endif
@@ -169,7 +170,7 @@ int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_
}
else //fallback
{
- //do NOT use "CompareString"; this function is NOT accurate (even with LOCALE_INVARIANT and SORT_STRINGSORT): for example "weiß" == "weiss"!!!
+ //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));
@@ -229,9 +230,16 @@ void z_impl::makeFilenameUpperCase(wchar_t* str, size_t size)
}
#elif defined FFS_MAC
+int z_impl::compareFilenamesNoCase(const char* lhs, const char* rhs, size_t sizeLhs, size_t sizeRhs)
+{
+ return ::strcasecmp(lhs, rhs); //locale-dependent!
+}
+
+
void z_impl::makeFilenameUpperCase(char* str, size_t size)
{
std::for_each(str, str + size, [](char& c) { c = static_cast<char>(::toupper(static_cast<unsigned char>(c))); }); //locale-dependent!
//result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness!
+ //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves!
}
#endif
diff --git a/zen/zstring.h b/zen/zstring.h
index f4a79181..435f03a2 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -11,7 +11,7 @@
#ifdef FFS_LINUX
#include <cstring> //strcmp
#elif defined FFS_MAC
-#include <strings.h> //strcasecmp
+//#include <strings.h> //strcasecmp
#endif
@@ -101,10 +101,8 @@ Zstring appendSeparator(Zstring path) //support rvalue references!
//################################# inline implementation ########################################
namespace z_impl
{
-#if defined FFS_WIN
-int compareFilenamesNoCase(const Zchar* lhs, const Zchar* rhs, size_t sizeLhs, size_t sizeRhs);
-#endif
#if defined FFS_WIN || defined FFS_MAC
+int compareFilenamesNoCase(const Zchar* lhs, const Zchar* rhs, size_t sizeLhs, size_t sizeRhs);
void makeFilenameUpperCase(Zchar* str, size_t size);
#endif
}
@@ -113,12 +111,12 @@ void makeFilenameUpperCase(Zchar* str, size_t size);
template <template <class, class> class SP, class AP> inline
int cmpFileName(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs)
{
-#if defined FFS_WIN
+#if defined FFS_WIN || defined FFS_MAC
return z_impl::compareFilenamesNoCase(lhs.data(), rhs.data(), lhs.length(), rhs.length());
#elif defined FFS_LINUX
return std::strcmp(lhs.c_str(), rhs.c_str()); //POSIX filenames don't have embedded 0
-#elif defined FFS_MAC
- return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent!
+ //#elif defined FFS_MAC
+ // return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent!
#endif
}
bgstack15