summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/basic_math.h23
-rw-r--r--zen/build_info.h2
-rw-r--r--zen/com_error.h8
-rw-r--r--zen/debug_log.h7
-rw-r--r--zen/debug_minidump.cpp2
-rw-r--r--zen/dir_watcher.cpp31
-rw-r--r--zen/dll.h28
-rw-r--r--zen/file_handling.cpp131
-rw-r--r--zen/file_id.cpp6
-rw-r--r--zen/file_id_def.h4
-rw-r--r--zen/file_io.cpp22
-rw-r--r--zen/file_io.h9
-rw-r--r--zen/file_traverser.cpp7
-rw-r--r--zen/file_traverser.h2
-rw-r--r--zen/format_unit.cpp190
-rw-r--r--zen/format_unit.h6
-rw-r--r--zen/guid.h4
-rw-r--r--zen/int64.h4
-rw-r--r--zen/last_error.h51
-rw-r--r--zen/process_priority.cpp149
-rw-r--r--zen/process_priority.h68
-rw-r--r--zen/read_txt.cpp2
-rw-r--r--zen/recycler.cpp69
-rw-r--r--zen/symlink_target.h12
-rw-r--r--zen/thread.h19
-rw-r--r--zen/tick_count.h34
-rw-r--r--zen/warn_static.h7
-rw-r--r--zen/zstring.cpp196
-rw-r--r--zen/zstring.h168
29 files changed, 694 insertions, 567 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h
index d83a7f77..89d9d6c0 100644
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -10,6 +10,7 @@
#include <algorithm>
#include <iterator>
#include <limits>
+#include <cmath>
#include <functional>
#include <cassert>
@@ -40,6 +41,9 @@ std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, Input
template <class InputIterator, class Compare>
std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare comp);
+template <class T, class InputIterator> //precondition: range must be sorted!
+auto nearMatch(const T& val, InputIterator first, InputIterator last) -> typename std::iterator_traits<InputIterator>::value_type;
+
template <class T>
bool isNull(T value);
@@ -198,6 +202,25 @@ std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, Input
}
+template <class T, class InputIterator> inline
+auto nearMatch(const T& val, InputIterator first, InputIterator last) -> typename std::iterator_traits<InputIterator>::value_type
+{
+ if (first == last)
+ return 0;
+
+ assert(std::is_sorted(first, last));
+ InputIterator it = std::lower_bound(first, last, val);
+ if (it == last)
+ return *--last;
+ if (it == first)
+ return *first;
+
+ const auto nextVal = *it;
+ const auto prevVal = *--it;
+ return val - prevVal < nextVal - val ? prevVal : nextVal;
+}
+
+
template <class T> inline
bool isNull(T value)
{
diff --git a/zen/build_info.h b/zen/build_info.h
index d69a8c1c..406fef70 100644
--- a/zen/build_info.h
+++ b/zen/build_info.h
@@ -13,6 +13,8 @@ namespace zen
//safer than checking for _WIN64 (defined on windows for 64-bit compilations only) while _WIN32 is always defined (even for x64 compiler!)
static const bool is32BitBuild = sizeof(void*) == 4;
static const bool is64BitBuild = sizeof(void*) == 8;
+
+static_assert(is32BitBuild || is64BitBuild, "");
}
#endif //BUILDINFO_H_INCLUDED
diff --git a/zen/com_error.h b/zen/com_error.h
index cd643b49..e6f5b492 100644
--- a/zen/com_error.h
+++ b/zen/com_error.h
@@ -51,14 +51,6 @@ Equivalent to:
-
-
-
-
-
-
-
-
//################# implementation #####################
std::wstring formatWin32Msg(DWORD dwMessageId) //return empty string on error
{
diff --git a/zen/debug_log.h b/zen/debug_log.h
index 45283da5..6ee44ff5 100644
--- a/zen/debug_log.h
+++ b/zen/debug_log.h
@@ -25,15 +25,10 @@ namespace zen
{
#ifdef FFS_WIN
const char ZEN_FILE_NAME_SEPARATOR = '\\';
-
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
const char ZEN_FILE_NAME_SEPARATOR = '/';
-
-#else
-#error specify platform!
#endif
-
class DebugLog
{
public:
diff --git a/zen/debug_minidump.cpp b/zen/debug_minidump.cpp
index 011e1bf2..c229b4aa 100644
--- a/zen/debug_minidump.cpp
+++ b/zen/debug_minidump.cpp
@@ -90,7 +90,7 @@ public:
private:
template <class T>
- static std::string numberToString(const T& number) //convert number to string the C++ way
+ static std::string numberToString(const T& number) //convert number to string the (slow) C++ way
{
std::ostringstream ss;
ss << number;
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 7d5e4697..0b34f890 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -19,6 +19,9 @@
#include <sys/inotify.h>
#include <fcntl.h>
#include "file_traverser.h"
+
+#elif defined FFS_MAC
+//#include <CoreFoundation/FSEvents.h>
#endif
using namespace zen;
@@ -133,7 +136,7 @@ class ReadChangesAsync
{
public:
//constructed in main thread!
- ReadChangesAsync(const Zstring& directory, //make sure to not leak in thread-unsafe types!
+ ReadChangesAsync(const Zstring& directory, //make sure to not leak-in thread-unsafe types!
const std::shared_ptr<SharedData>& shared) :
shared_(shared),
dirnamePf(appendSeparator(directory)),
@@ -510,11 +513,35 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
output.push_back(Entry(ACTION_DELETE, fullname));
}
}
-
bytePos += sizeof(struct ::inotify_event) + evt.len;
}
return output;
}
+#elif defined FFS_MAC
+
+warn_static("finish")
+struct DirWatcher::Pimpl
+{
+
+};
+
+
+DirWatcher::DirWatcher(const Zstring& directory) //throw FileError
+{
+
+}
+
+
+DirWatcher::~DirWatcher()
+{
+}
+
+
+std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>&) //throw FileError
+{
+ std::vector<Entry> output;
+ return output;
+}
#endif
diff --git a/zen/dll.h b/zen/dll.h
index 3e7a0655..2c39ac44 100644
--- a/zen/dll.h
+++ b/zen/dll.h
@@ -8,10 +8,15 @@
#define DLLLOADER_H_INCLUDED
#include <memory>
+#ifdef FFS_WIN
#include <string>
#include "scope_guard.h"
#include "win.h" //includes "windows.h"
+#elif defined FFS_LINUX || defined FFS_MAC
+#include <dlfcn.h>
+#endif
+
namespace zen
{
/*
@@ -35,10 +40,15 @@ class DllFun
public:
DllFun() : fun(nullptr) {}
+#ifdef FFS_WIN
DllFun(const wchar_t* libraryName, const char* functionName) :
hLibRef(::LoadLibrary(libraryName), ::FreeLibrary),
fun(hLibRef ? reinterpret_cast<Func>(::GetProcAddress(static_cast<HMODULE>(hLibRef.get()), functionName)) : nullptr) {}
-
+#elif defined FFS_LINUX || defined FFS_MAC
+ DllFun(const char* libraryName, const char* functionName) :
+ hLibRef(::dlopen(libraryName, RTLD_LAZY), ::dlclose),
+ fun(hLibRef ? reinterpret_cast<Func>(::dlsym(hLibRef.get(), functionName)) : nullptr) {}
+#endif
operator Func() const { return fun; }
private:
@@ -46,6 +56,8 @@ private:
Func fun;
};
+
+#ifdef FFS_WIN
//if the dll is already part of the process space, e.g. "kernel32.dll" or "shell32.dll", we can use a faster variant:
//NOTE: since the lifetime of the referenced library is *not* controlled, this is safe to use only for permanently loaded libraries like these!
template <class Func>
@@ -66,7 +78,6 @@ private:
Func fun;
};
-
/*
extract binary resources from .exe/.dll:
@@ -77,12 +88,7 @@ extract binary resources from .exe/.dll:
MY_BINARY_RESOURCE RCDATA "filename.dat"
*/
std::string getResourceStream(const std::wstring& libraryName, size_t resourceId);
-
-
-
-
-
-
+#endif
@@ -94,6 +100,7 @@ std::string getResourceStream(const std::wstring& libraryName, size_t resourceId
//--------------- implementation---------------------------------------------------
+#ifdef FFS_WIN
inline
std::string getResourceStream(const wchar_t* libraryName, size_t resourceId)
{
@@ -102,16 +109,13 @@ std::string getResourceStream(const wchar_t* libraryName, size_t resourceId)
ZEN_ON_SCOPE_EXIT(::FreeLibrary(module));
if (HRSRC res = ::FindResource(module, MAKEINTRESOURCE(resourceId), RT_RCDATA))
- {
if (HGLOBAL resHandle = ::LoadResource(module, res))
- {
if (const char* stream = static_cast<const char*>(::LockResource(resHandle)))
return std::string(stream, static_cast<size_t>(::SizeofResource(module, res))); //size is 0 on error
- }
- }
}
return std::string();
}
+#endif
}
#endif // DLLLOADER_H_INCLUDED
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index 4f34814f..bf829010 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -27,14 +27,20 @@
#include "IFileOperation/file_op.h"
#elif defined FFS_LINUX
-#include <sys/stat.h>
-#include <utime.h>
-#include <sys/time.h> //futimes
-#include <sys/vfs.h>
-
+#include <sys/vfs.h> //statfs
+#include <fcntl.h> //AT_SYMLINK_NOFOLLOW, UTIME_OMIT
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
+
+#elif defined FFS_MAC
+#include <sys/mount.h> //statfs
+#include <utime.h>
+#endif
+
+#if defined FFS_LINUX || defined FFS_MAC
+#include <sys/stat.h>
+//#include <sys/time.h>
#endif
using namespace zen;
@@ -47,7 +53,7 @@ bool zen::fileExists(const Zstring& filename)
const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
struct stat fileInfo = {};
return ::lstat(filename.c_str(), &fileInfo) == 0 &&
(S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)); //in Linux a symbolic link is neither file nor directory
@@ -62,7 +68,7 @@ bool zen::dirExists(const Zstring& dirname)
const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str());
return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
struct stat dirInfo = {};
return ::lstat(dirname.c_str(), &dirInfo) == 0 &&
(S_ISLNK(dirInfo.st_mode) || S_ISDIR(dirInfo.st_mode)); //in Linux a symbolic link is neither file nor directory
@@ -82,7 +88,7 @@ bool zen::symlinkExists(const Zstring& linkname)
}
return isSymlink(fileInfo);
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
struct stat fileInfo = {};
return ::lstat(linkname.c_str(), &fileInfo) == 0 &&
S_ISLNK(fileInfo.st_mode); //symbolic link
@@ -96,7 +102,7 @@ bool zen::somethingExists(const Zstring& objname)
const DWORD rv = ::GetFileAttributes(applyLongPathPrefix(objname).c_str());
return rv != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys"
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
struct stat fileInfo = {};
return ::lstat(objname.c_str(), &fileInfo) == 0;
#endif
@@ -160,7 +166,7 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl
attr.modificationTime = toTimeT(fileInfoHnd.ftLastWriteTime);
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
struct stat fileInfo = {};
const int rv = procSl == SYMLINK_FOLLOW ?
@@ -204,7 +210,7 @@ UInt64 zen::getFreeDiskSpace(const Zstring& path) //throw FileError
return UInt64(bytesFree.LowPart, bytesFree.HighPart);
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
struct statfs info = {};
if (::statfs(path.c_str(), &info) != 0)
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)) + L"\n\n" + getLastErrorFormatted());
@@ -269,7 +275,7 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
return volumeSerial;
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
dev_t retrieveVolumeSerial(const Zstring& pathName) //return 0 on error!
{
Zstring volumePathName = pathName;
@@ -308,7 +314,7 @@ bool zen::removeFile(const Zstring& filename) //throw FileError
#ifdef FFS_WIN
const Zstring& filenameFmt = applyLongPathPrefix(filename);
if (!::DeleteFile(filenameFmt.c_str()))
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
if (::unlink(filename.c_str()) != 0)
#endif
{
@@ -419,7 +425,7 @@ void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw File
throw FileError(errorMessage);
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
if (::rename(oldName.c_str(), newName.c_str()) != 0)
{
const int lastError = errno;
@@ -619,7 +625,7 @@ void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback)
if (callback) callback->onBeforeDirDeletion(directory); //once per symlink
#ifdef FFS_WIN
if (!::RemoveDirectory(directoryFmt.c_str()))
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
if (::unlink(directory.c_str()) != 0)
#endif
throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
@@ -653,7 +659,7 @@ void removeDirectoryImpl(const Zstring& directory, CallbackRemoveDir* callback)
if (callback) callback->onBeforeDirDeletion(directory); //and once per folder
#ifdef FFS_WIN
if (!::RemoveDirectory(directoryFmt.c_str()))
-#else
+#elif defined FFS_LINUX || defined FFS_MAC
if (::rmdir(directory.c_str()) != 0)
#endif
throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
@@ -676,11 +682,11 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback)
-void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, ProcSymlink procSl) //throw FileError
+void zen::setFileTime(const Zstring& filename, const Int64& modTime, ProcSymlink procSl) //throw FileError
{
#ifdef FFS_WIN
FILETIME creationTime = {};
- FILETIME lastWriteTime = tofiletime(modificationTime);
+ FILETIME lastWriteTime = tofiletime(modTime);
//####################################### DST hack ###########################################
if (dst::isFatDrive(filename)) //throw()
@@ -930,25 +936,23 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
#endif
#elif defined FFS_LINUX
- if (procSl == SYMLINK_FOLLOW)
- {
- struct ::utimbuf newTimes = {};
- newTimes.actime = ::time(nullptr);
- newTimes.modtime = to<time_t>(modificationTime);
+ struct ::timespec newTimes[2] = {};
+ newTimes[0].tv_nsec = UTIME_OMIT; //omit access time
+ newTimes[1].tv_sec = to<time_t>(modTime); //modification time (seconds)
- // set new "last write time"
- if (::utime(filename.c_str(), &newTimes) != 0)
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
- }
- else
- {
- struct ::timeval newTimes[2] = {};
- newTimes[0].tv_sec = ::time(nullptr); //access time (seconds)
- newTimes[1].tv_sec = to<time_t>(modificationTime); //modification time (seconds)
+ if (::utimensat(AT_FDCWD, filename.c_str(), newTimes, procSl == SYMLINK_DIRECT ? AT_SYMLINK_NOFOLLOW : 0) != 0)
+ throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
- if (::lutimes(filename.c_str(), newTimes) != 0)
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
- }
+#elif defined FFS_MAC
+ struct ::timeval newTimes[2] = {};
+ newTimes[0].tv_sec = ::time(nullptr); //access time (seconds)
+ newTimes[1].tv_sec = to<time_t>(modTime); //modification time (seconds)
+
+ const int rv = procSl == SYMLINK_FOLLOW ?
+ :: utimes(filename.c_str(), newTimes) : //utimensat() not yet implemented on OS X
+ ::lutimes(filename.c_str(), newTimes);
+ if (rv != 0)
+ throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
#endif
}
@@ -976,7 +980,7 @@ bool zen::supportsPermissions(const Zstring& dirname) //throw FileError
return (fsFlags & FILE_PERSISTENT_ACLS) != 0;
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
return true;
#endif
}
@@ -1226,7 +1230,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
throw FileError
*/
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#ifdef HAVE_SELINUX //copy SELinux security context
copySecurityContext(source, target, procSl); //throw FileError
@@ -1277,7 +1281,7 @@ void createDirectoryStraight(const Zstring& directory, //throw FileError, ErrorT
throw FileError(msg);
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
if (::mkdir(directory.c_str(), 0755) != 0) //mode: drwxr-xr-x
{
const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted();
@@ -1474,7 +1478,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
if (!createSymbolicLink(targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, - seems no long path prefix is required...
linkPath.c_str(), //__in LPTSTR lpTargetFileName,
(isDirLink ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0))) //__in DWORD dwFlags
-#elif defined FFS_LINUX
+#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)) +
@@ -2166,14 +2170,13 @@ void copyFileWindows(const Zstring& sourceFile, const Zstring& targetFile, Callb
}
}
-#elif defined FFS_LINUX
-void copyFileLinux(const Zstring& sourceFile,
- const Zstring& targetFile,
- CallbackCopyFile* callback,
- FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
-{
- zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: place guard before lifetime of FileOutput
+#elif defined FFS_LINUX || defined FFS_MAC
+void copyFileLinuxMac(const Zstring& sourceFile,
+ const Zstring& targetFile,
+ CallbackCopyFile* callback,
+ FileAttrib* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+{
//open sourceFile for reading
FileInputUnbuffered fileIn(sourceFile); //throw FileError, ErrorNotExisting
@@ -2181,6 +2184,7 @@ void copyFileLinux(const Zstring& sourceFile,
if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)) + L"\n\n" + getLastErrorFormatted());
+ zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (...) {} }); //transactional behavior: place guard before lifetime of FileOutput
try
{
//create targetFile and open it for writing
@@ -2202,12 +2206,6 @@ void copyFileLinux(const Zstring& sourceFile,
//adapt target file modification time:
{
- struct ::timeval newTimes[2] = {};
- newTimes[0].tv_sec = sourceInfo.st_atime;
- newTimes[1].tv_sec = sourceInfo.st_mtime;
- if (::futimes(fileOut.getDescriptor(), newTimes) != 0) //by using the already open file handle, we avoid issues like: https://sourceforge.net/p/freefilesync/bugs/230/
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)) + L"\n\n" + getLastErrorFormatted());
-
//read and return file statistics
struct ::stat targetInfo = {};
if (::fstat(fileOut.getDescriptor(), &targetInfo) != 0)
@@ -2222,12 +2220,19 @@ void copyFileLinux(const Zstring& sourceFile,
}
}
}
- catch (ErrorTargetExisting&)
+ catch (const ErrorTargetExisting&)
{
guardTarget.dismiss(); //don't delete file that existed previously!
throw;
}
+ //we cannot set the target file times while the file descriptor is open and being written:
+ //this triggers bugs on samba shares where the modification time is set to current time instead.
+ //http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236
+ //http://comments.gmane.org/gmane.linux.file-systems.cifs/2854
+ //on the other hand we thereby have to reopen https://sourceforge.net/p/freefilesync/bugs/230/
+ setFileTime(targetFile, sourceInfo.st_mtime, SYMLINK_FOLLOW); //throw FileError
+
guardTarget.dismiss(); //target has been created successfully!
}
#endif
@@ -2246,14 +2251,14 @@ Zstring findUnusedTempName(const Zstring& filename)
/*
- File Copy Layers
- ================
-
- copyFile (setup transactional behavior)
- |
- copyFileSelectOs
- / \
-copyFileLinux copyFileWindows (solve 8.3 issue)
+ ------------------
+ |File Copy Layers|
+ ------------------
+ copyFile (setup transactional behavior)
+ |
+ copyFileSelectOs
+ / \
+copyFileLinuxMac copyFileWindows (solve 8.3 issue)
|
copyFileWindowsSelectRoutine
/ \
@@ -2266,8 +2271,8 @@ void copyFileSelectOs(const Zstring& sourceFile, const Zstring& targetFile, Call
#ifdef FFS_WIN
copyFileWindows(sourceFile, targetFile, callback, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked
-#elif defined FFS_LINUX
- copyFileLinux(sourceFile, targetFile, callback, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+#elif defined FFS_LINUX || defined FFS_MAC
+ copyFileLinuxMac(sourceFile, targetFile, callback, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
#endif
}
}
diff --git a/zen/file_id.cpp b/zen/file_id.cpp
index 3d04ce34..7e0fa81b 100644
--- a/zen/file_id.cpp
+++ b/zen/file_id.cpp
@@ -11,7 +11,7 @@
#include "long_path_prefix.h"
#include "scope_guard.h"
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#include <sys/stat.h>
#endif
@@ -40,9 +40,9 @@ zen::FileId zen::getFileID(const Zstring& filename)
return extractFileID(fileInfo);
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
struct ::stat fileInfo = {};
- if (::stat(filename.c_str(), &fileInfo) == 0) //stat() follows symlinks
+ if (::lstat(filename.c_str(), &fileInfo) == 0)
return extractFileID(fileInfo);
#endif
diff --git a/zen/file_id_def.h b/zen/file_id_def.h
index 5f485c68..54012b4f 100644
--- a/zen/file_id_def.h
+++ b/zen/file_id_def.h
@@ -13,7 +13,7 @@
#ifdef FFS_WIN
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#include <sys/stat.h>
#endif
@@ -49,7 +49,7 @@ assert_static(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFi
assert_static(sizeof(FileId().second) == sizeof(ULARGE_INTEGER));
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
namespace impl { typedef struct ::stat StatDummy; } //sigh...
typedef decltype(impl::StatDummy::st_dev) DeviceId;
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 788288ce..6581dfbe 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -12,7 +12,7 @@
#include "win_ver.h"
#include "dll.h"
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#include <fcntl.h> //open, close
#include <unistd.h> //read, write
#endif
@@ -45,7 +45,7 @@ Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string
return Zstring();
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
//"filename" could be a named pipe which *blocks* forever during "open()"! https://sourceforge.net/p/freefilesync/bugs/221/
void checkForUnsupportedType(const Zstring& filename) //throw FileError
{
@@ -64,7 +64,7 @@ void checkForUnsupportedType(const Zstring& filename) //throw FileError
S_ISBLK (m) ? L"block device" :
S_ISFIFO(m) ? L"FIFO, named pipe" :
S_ISSOCK(m) ? L"socket" : nullptr;
- const std::wstring numFmt = printNumber<std::wstring>(L"0%06o", m & __S_IFMT);
+ const std::wstring numFmt = printNumber<std::wstring>(L"0%06o", m & S_IFMT);
return name ? numFmt + L", " + name : numFmt;
};
throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtFileName(filename)) + L" " + getTypeName(fileInfo.st_mode));
@@ -113,7 +113,7 @@ FileInput::FileInput(const Zstring& filename) : //throw FileError, ErrorNotExis
*/
nullptr);
if (fileHandle == INVALID_HANDLE_VALUE)
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever!
fileHandle = ::fopen(filename.c_str(), "r,type=record,noseek"); //utilize UTF-8 filename
if (!fileHandle)
@@ -147,7 +147,7 @@ FileInput::~FileInput()
{
#ifdef FFS_WIN
::CloseHandle(fileHandle);
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
::fclose(fileHandle); //NEVER allow passing nullptr to fclose! -> crash!; fileHandle != nullptr in this context!
#endif
}
@@ -164,7 +164,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number
static_cast<DWORD>(bytesToRead), //__in DWORD nNumberOfBytesToRead,
&bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead,
nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle);
if (::ferror(fileHandle) != 0) //checks status of stream, not fread()!
#endif
@@ -174,7 +174,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number
if (bytesRead < bytesToRead) //verify only!
setEof();
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
if (::feof(fileHandle) != 0)
setEof();
@@ -257,7 +257,7 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
}
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
checkForUnsupportedType(filename); //throw FileError; writing a named pipe would block forever!
fileHandle = ::fopen(filename.c_str(),
//GNU extension: https://www.securecoding.cert.org/confluence/display/cplusplus/FIO03-CPP.+Do+not+make+assumptions+about+fopen()+and+file+creation
@@ -282,7 +282,7 @@ FileOutput::~FileOutput()
{
#ifdef FFS_WIN
::CloseHandle(fileHandle);
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
::fclose(fileHandle); //NEVER allow passing nullptr to fclose! -> crash!
#endif
}
@@ -297,7 +297,7 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
static_cast<DWORD>(bytesToWrite), //__in DWORD nNumberOfBytesToWrite,
&bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten,
nullptr)) //__inout_opt LPOVERLAPPED lpOverlapped
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle);
if (::ferror(fileHandle) != 0) //checks status of stream, not fwrite()!
#endif
@@ -308,7 +308,7 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
}
-#ifdef FFS_LINUX
+#if defined FFS_LINUX || defined FFS_MAC
//Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-5.0.tar.gz
FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBase(filename) //throw FileError, ErrorNotExisting
diff --git a/zen/file_io.h b/zen/file_io.h
index 31373857..8e501172 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -12,7 +12,7 @@
#ifdef FFS_WIN
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#include <cstdio>
#include <sys/stat.h>
#endif
@@ -24,13 +24,15 @@ namespace zen
static const char LINE_BREAK[] = "\r\n";
#elif defined FFS_LINUX
static const char LINE_BREAK[] = "\n";
+#elif defined FFS_MAC
+static const char LINE_BREAK[] = "\r";
#endif
//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks)
#ifdef FFS_WIN
typedef HANDLE FileHandle;
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
typedef FILE* FileHandle;
#endif
@@ -62,8 +64,7 @@ private:
FileHandle fileHandle;
};
-
-#ifdef FFS_LINUX
+#if defined FFS_LINUX || defined FFS_MAC
class FileInputUnbuffered : public FileInputBase
{
public:
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 9a02eb1e..b39f8416 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -17,7 +17,7 @@
#include "dll.h"
#include "FindFilePlus/find_file_plus.h"
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#include <sys/stat.h>
#include <dirent.h>
#endif
@@ -498,8 +498,7 @@ private:
const DWORD volumeSerial; //may be 0!
};
-
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
class DirTraverser
{
public:
@@ -515,7 +514,7 @@ public:
the buffer whose address is passed in entry as follows:
len = offsetof(struct dirent, d_name) + pathconf(dirpath, _PC_NAME_MAX) + 1
entryp = malloc(len); */
- const long maxPath = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return -1
+ const size_t maxPath = std::max<long>(::pathconf(directoryFormatted.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1)
buffer.resize(offsetof(struct ::dirent, d_name) + maxPath + 1);
traverse(directoryFormatted, sink, 0);
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index c8ef6550..7e566075 100644
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -61,7 +61,7 @@ struct DstHackCallback
virtual ~DstHackCallback() {}
virtual void requestUiRefresh(const Zstring& filename) = 0; //applying DST hack imposes significant one-time performance drawback => callback to inform user
};
-#else
+#elif defined FFS_LINUX || defined FFS_MAC
struct DstHackCallback; //DST hack not required on Linux
#endif
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 6455029f..7cc43e6b 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -16,7 +16,7 @@
#include <zen/win.h> //includes "windows.h"
#include <zen/win_ver.h>
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#include <clocale> //thousands separator
#include <zen/utf.h> //
#endif
@@ -26,53 +26,50 @@ using namespace zen;
std::wstring zen::filesizeToShortString(Int64 size)
{
- //if (size < 0) return _("Error"); -> really? there's at least one exceptional case: a failed rename operation falls-back to copy + delete, reducing "bytes transferred" to potentially < 0!
+ //if (size < 0) return _("Error"); -> really?
if (numeric::abs(size) <= 999)
- return replaceCpy(_P("1 Byte", "%x Bytes", to<int>(size)),
- L"%x",
- numberTo<std::wstring>(size));
- else
- {
- double filesize = to<double>(size);
+ return replaceCpy(_P("1 Byte", "%x Bytes", to<int>(size)), L"%x", numberTo<std::wstring>(size));
- filesize /= 1024;
- std::wstring output = _("%x KB");
- if (numeric::abs(filesize) > 999)
- {
- filesize /= 1024;
- output = _("%x MB");
- if (numeric::abs(filesize) > 999)
- {
- filesize /= 1024;
- output = _("%x GB");
- if (numeric::abs(filesize) > 999)
- {
- filesize /= 1024;
- output = _("%x TB");
- if (numeric::abs(filesize) > 999)
- {
- filesize /= 1024;
- output = _("%x PB");
- }
- }
- }
- }
+ auto formatUnitSize = [](double sizeInUnit, const std::wstring& unitTxt) -> std::wstring
+ {
//print just three significant digits: 0,01 | 0,11 | 1,11 | 11,1 | 111
- const size_t fullunits = static_cast<size_t>(numeric::abs(filesize));
+ const size_t fullunits = static_cast<size_t>(numeric::abs(sizeInUnit));
const int precisionDigits = fullunits < 10 ? 2 : fullunits < 100 ? 1 : 0; //sprintf requires "int"
wchar_t buffer[50];
#ifdef __MINGW32__
- int charsWritten = ::snwprintf(buffer, 50, L"%.*f", precisionDigits, filesize); //MinGW does not comply to the C standard here
+ int charsWritten = ::_snwprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit); //MinGW does not comply to the C standard here
#else
- int charsWritten = std::swprintf(buffer, 50, L"%.*f", precisionDigits, filesize);
+ int charsWritten = std::swprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit);
#endif
- return charsWritten > 0 ? replaceCpy(output, L"%x", std::wstring(buffer, charsWritten)) : _("Error");
- }
+ return charsWritten > 0 ? replaceCpy(unitTxt, L"%x", std::wstring(buffer, charsWritten)) : _("Error");
+ };
+
+ double sizeInUnit = to<double>(size);
+ sizeInUnit /= 1024;
+ if (numeric::abs(sizeInUnit) <= 999)
+ return formatUnitSize(sizeInUnit, _("%x KB"));
+
+ sizeInUnit /= 1024;
+ if (numeric::abs(sizeInUnit) <= 999)
+ return formatUnitSize(sizeInUnit, _("%x MB"));
+
+ sizeInUnit /= 1024;
+ if (numeric::abs(sizeInUnit) <= 999)
+ return formatUnitSize(sizeInUnit, _("%x GB"));
+
+ sizeInUnit /= 1024;
+ if (numeric::abs(sizeInUnit) <= 999)
+ return formatUnitSize(sizeInUnit, _("%x TB"));
+
+ sizeInUnit /= 1024;
+ return formatUnitSize(sizeInUnit, _("%x PB"));
}
+namespace
+{
enum UnitRemTime
{
URT_SEC,
@@ -81,56 +78,75 @@ enum UnitRemTime
URT_DAY
};
-std::wstring zen::remainingTimeToShortString(double timeInSec)
-{
- double remainingTime = timeInSec;
-
- //determine preferred unit
- UnitRemTime unit = URT_SEC;
- if (remainingTime > 59)
- {
- unit = URT_MIN;
- remainingTime /= 60;
- if (remainingTime > 59)
- {
- unit = URT_HOUR;
- remainingTime /= 60;
- if (remainingTime > 23)
- {
- unit = URT_DAY;
- remainingTime /= 24;
- }
- }
- }
-
- int formattedTime = numeric::round(remainingTime);
-
- //reduce precision to 5 seconds
- if (unit == URT_SEC)
- formattedTime = static_cast<int>(std::ceil(formattedTime / 5.0) * 5);
- //generate output message
- std::wstring output;
+std::wstring formatUnitTime(int val, UnitRemTime unit)
+{
+ auto subst = [&](const std::wstring& output) { return replaceCpy(output, L"%x", zen::numberTo<std::wstring>(val)); };
switch (unit)
{
case URT_SEC:
- output = _P("1 sec", "%x sec", formattedTime);
- break;
+ return subst(_P("1 sec", "%x sec", val));
case URT_MIN:
- output = _P("1 min", "%x min", formattedTime);
- break;
+ return subst(_P("1 min", "%x min", val));
case URT_HOUR:
- output = _P("1 hour", "%x hours", formattedTime);
- break;
+ return subst(_P("1 hour", "%x hours", val));
case URT_DAY:
- output = _P("1 day", "%x days", formattedTime);
- break;
+ return subst(_P("1 day", "%x days", val));
}
- return replaceCpy(output, L"%x", zen::numberTo<std::wstring>(formattedTime));
+ assert(false);
+ return _("Error");
+}
+
+
+template <int M, int N>
+std::wstring roundToBlock(double timeHigh,
+ UnitRemTime unitHigh, const int (&stepsHigh)[M],
+ int unitLowPerHigh,
+ UnitRemTime unitLow, const int (&stepsLow)[N])
+{
+ assert(unitLowPerHigh > 0);
+ const double granularity = 0.1;
+ const double timeLow = timeHigh * unitLowPerHigh;
+ const int blockSizeLow = granularity * timeHigh < 1 ?
+ numeric::nearMatch(granularity * timeLow, std::begin(stepsLow), std::end(stepsLow)):
+ numeric::nearMatch(granularity * timeHigh, std::begin(stepsHigh), std::end(stepsHigh)) * unitLowPerHigh;
+ const int roundedTimeLow = numeric::round(timeLow / blockSizeLow) * blockSizeLow;
+
+ std::wstring output = formatUnitTime(roundedTimeLow / unitLowPerHigh, unitHigh);
+ if (unitLowPerHigh > blockSizeLow)
+ output += L" " + formatUnitTime(roundedTimeLow % unitLowPerHigh, unitLow);
+ return output;
+};
}
-std::wstring zen::fractionToShortString(double fraction)
+std::wstring zen::remainingTimeToString(double timeInSec)
+{
+ const int steps10[] = { 1, 2, 5, 10 };
+ const int steps24[] = { 1, 2, 3, 4, 6, 8, 12, 24 };
+ const int steps60[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+
+ //determine preferred unit
+ double timeInUnit = timeInSec;
+ if (timeInUnit <= 60)
+ return roundToBlock(timeInUnit, URT_SEC, steps60, 1, URT_SEC, steps60);
+
+ timeInUnit /= 60;
+ if (timeInUnit <= 60)
+ return roundToBlock(timeInUnit, URT_MIN, steps60, 60, URT_SEC, steps60);
+
+ timeInUnit /= 60;
+ if (timeInUnit <= 24)
+ return roundToBlock(timeInUnit, URT_HOUR, steps24, 60, URT_MIN, steps60);
+
+ timeInUnit /= 24;
+ return roundToBlock(timeInUnit, URT_DAY, steps10, 24, URT_HOUR, steps24);
+ //note: for 10% granularity steps10 yields a valid blocksize only up to timeInUnit == 100!
+ //for larger time sizes this results in a finer granularity than expected: 10 days -> should not be a problem considering "usual" remaining time for synchronization
+}
+
+
+std::wstring zen::fractionToString(double fraction)
{
//return replaceCpy(_("%x%"), L"%x", printNumber<std::wstring>(L"%3.2f", fraction * 100.0), false);
return printNumber<std::wstring>(L"%3.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
@@ -236,7 +252,7 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
}
return number;
-#else
+#elif defined FFS_LINUX || defined FFS_MAC
//we have to include thousands separator ourselves; this doesn't work for all countries (e.g india), but is better than nothing
//::setlocale (LC_ALL, ""); -> implicitly called by wxLocale
@@ -261,28 +277,6 @@ std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
#endif
}
-/*
-#include <wx/scrolwin.h>
-
-void zen::scrollToBottom(wxScrolledWindow* scrWindow)
-{
- int height = 0;
- scrWindow->GetClientSize(nullptr, &height);
-
- int pixelPerLine = 0;
- scrWindow->GetScrollPixelsPerUnit(nullptr, &pixelPerLine);
-
- if (height > 0 && pixelPerLine > 0)
- {
- const int scrollLinesTotal = scrWindow->GetScrollLines(wxVERTICAL);
- const int scrollLinesOnScreen = height / pixelPerLine;
- const int scrollPosBottom = scrollLinesTotal - scrollLinesOnScreen;
-
- if (0 <= scrollPosBottom)
- scrWindow->Scroll(0, scrollPosBottom);
- }
-}
-*/
#ifdef FFS_WIN
namespace
@@ -333,7 +327,7 @@ std::wstring zen::utcToLocalTimeString(Int64 utcTime)
loc.minute = systemTimeLocal.wMinute;
loc.second = systemTimeLocal.wSecond;
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
zen::TimeComp loc = zen::localTime(to<time_t>(utcTime));
#endif
diff --git a/zen/format_unit.h b/zen/format_unit.h
index f634e5e5..86477999 100644
--- a/zen/format_unit.h
+++ b/zen/format_unit.h
@@ -8,14 +8,14 @@
#define FMT_UNIT_8702184019487324
#include <string>
-#include <zen/string_tools.h>
#include <zen/int64.h>
+#include <zen/string_tools.h>
namespace zen
{
std::wstring filesizeToShortString(Int64 filesize);
-std::wstring remainingTimeToShortString(double timeInSec);
-std::wstring fractionToShortString(double fraction); //within [0, 1]
+std::wstring remainingTimeToString(double timeInSec);
+std::wstring fractionToString(double fraction); //within [0, 1]
template <class NumberType>
std::wstring toGuiString(NumberType number); //format integer number including thousands separator
diff --git a/zen/guid.h b/zen/guid.h
index 0c03bd9f..b2dd90aa 100644
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -10,7 +10,7 @@
#include <string>
#include <boost/uuid/uuid.hpp>
-#ifdef __MINGW32__ //boost should start cleaning this mess up
+#ifdef __GNUC__ //boost should start cleaning this mess up
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow"
#pragma GCC diagnostic ignored "-Wuninitialized"
@@ -18,7 +18,7 @@
#include <boost/uuid/uuid_generators.hpp>
-#ifdef __MINGW32__
+#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
diff --git a/zen/int64.h b/zen/int64.h
index 238efb14..3e967df8 100644
--- a/zen/int64.h
+++ b/zen/int64.h
@@ -182,7 +182,7 @@ public:
inline friend bool operator>=(const UInt64& lhs, const UInt64& rhs) { return lhs.value >= rhs.value; }
//checked conversion to arbitrary target integer type
- template <class T> inline friend T to(UInt64 number) { checkRange<T>(number.value); return static_cast<T>(number.value); }
+ template <class T> friend T to(UInt64 number);
template <class T> inline friend std::basic_istream<T>& operator>>(std::basic_istream<T>& lhs, UInt64& rhs) { lhs >> rhs.value; return lhs; }
template <class T> inline friend std::basic_ostream<T>& operator<<(std::basic_ostream<T>& lhs, const UInt64& rhs) { lhs << rhs.value; return lhs; }
@@ -191,6 +191,8 @@ private:
std::uint64_t value;
};
+template <class T> inline T to(UInt64 number) { checkRange<T>(number.value); return static_cast<T>(number.value); } //Clang 3.2 doesn't properly handle inline-friends defined in class definition
+
inline UInt64 operator+ (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) += rhs; }
inline UInt64 operator- (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) -= rhs; }
inline UInt64 operator* (const UInt64& lhs, const UInt64& rhs) { return UInt64(lhs) *= rhs; }
diff --git a/zen/last_error.h b/zen/last_error.h
index 72d54d48..ddee552f 100644
--- a/zen/last_error.h
+++ b/zen/last_error.h
@@ -14,7 +14,7 @@
#ifdef FFS_WIN
#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#include <cstring>
#include <cerrno>
#endif
@@ -25,7 +25,7 @@ namespace zen
//evaluate GetLastError()/errno and assemble specific error message
#ifdef FFS_WIN
typedef DWORD ErrorCode;
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
typedef int ErrorCode;
#endif
@@ -42,40 +42,13 @@ bool errorCodeForNotExisting(ErrorCode lastError); //check for "not existing" al
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
//######################## implementation ########################
inline
ErrorCode getLastError()
{
#ifdef FFS_WIN
return ::GetLastError();
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
return errno; //don't use "::", errno is a macro!
#endif
}
@@ -87,17 +60,16 @@ std::wstring getLastErrorFormatted(ErrorCode lastError)
if (lastError == 0)
lastError = getLastError();
-#ifdef FFS_WIN
- std::wstring output = _("Windows Error Code %x:");
+ std::wstring output = _("Error Code %x:");
replace(output, L"%x", numberTo<std::wstring>(lastError));
-
+#ifdef FFS_WIN
LPWSTR buffer = nullptr;
if (::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_MAX_WIDTH_MASK |
FORMAT_MESSAGE_IGNORE_INSERTS | //important: without this flag ::FormatMessage() will fail if message contains placeholders
FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, lastError, 0, reinterpret_cast<LPWSTR>(&buffer), 0, nullptr) != 0)
{
- if (buffer) //just to be sure
+ if (buffer) //"don't trust nobody"
{
output += L" ";
output += buffer;
@@ -105,18 +77,14 @@ std::wstring getLastErrorFormatted(ErrorCode lastError)
}
}
::SetLastError(lastError); //restore last error
- return output;
-
-#elif defined FFS_LINUX
- std::wstring output = _("Linux Error Code %x:");
- replace(output, L"%x", numberTo<std::wstring>(lastError));
+#elif defined FFS_LINUX || defined FFS_MAC
output += L" ";
output += utfCvrtTo<std::wstring>(::strerror(lastError));
errno = lastError; //restore errno
- return output;
#endif
+ return output;
}
@@ -128,8 +96,7 @@ bool errorCodeForNotExisting(ErrorCode lastError)
lastError == ERROR_PATH_NOT_FOUND ||
lastError == ERROR_BAD_NETPATH ||
lastError == ERROR_NETNAME_DELETED;
-
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
return lastError == ENOENT;
#endif
}
diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp
new file mode 100644
index 00000000..96a6c2de
--- /dev/null
+++ b/zen/process_priority.cpp
@@ -0,0 +1,149 @@
+// **************************************************************************
+// * 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 *
+// **************************************************************************
+
+#include "process_priority.h"
+#include <zen/last_error.h>
+#include <zen/i18n.h>
+
+#ifdef FFS_WIN
+#include "win.h" //includes "windows.h"
+
+#elif defined FFS_LINUX
+//#include <sys/syscall.h>
+
+#elif defined FFS_MAC
+#include <sys/resource.h> //getiopolicy_np
+#include <IOKit/pwr_mgt/IOPMLib.h> //keep in .cpp file to not pollute global namespace! e.g. with UInt64
+#endif
+
+using namespace zen;
+
+
+#ifdef FFS_WIN
+struct PreventStandby::Pimpl {};
+
+PreventStandby::PreventStandby()
+{
+ if (::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) == 0)
+ throw FileError(_("Failed to suspend system sleep mode.")); //no GetLastError() support?
+}
+
+
+PreventStandby::~PreventStandby()
+{
+ ::SetThreadExecutionState(ES_CONTINUOUS);
+}
+
+
+#ifndef PROCESS_MODE_BACKGROUND_BEGIN
+#define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000 // Windows Server 2003 and Windows XP/2000: This value is not supported!
+#define PROCESS_MODE_BACKGROUND_END 0x00200000 //
+#endif
+
+struct ScheduleForBackgroundProcessing::Pimpl {};
+
+
+ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing()
+{
+ if (!::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN)) //this call lowers CPU priority, too!!
+ throw FileError(_("Cannot change process I/O priorities.") + L"\n\n" + getLastErrorFormatted());
+}
+
+
+ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing()
+{
+ ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END);
+}
+
+#elif defined FFS_LINUX
+struct PreventStandby::Pimpl {};
+PreventStandby::PreventStandby() {}
+PreventStandby::~PreventStandby() {}
+
+//solution for GNOME?: http://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Inhibit
+
+struct ScheduleForBackgroundProcessing::Pimpl {};
+ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() {};
+ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() {};
+
+/*
+struct ScheduleForBackgroundProcessing
+{
+ - required functions ioprio_get/ioprio_set are not part of glibc: http://linux.die.net/man/2/ioprio_set
+ - and probably never will: http://sourceware.org/bugzilla/show_bug.cgi?id=4464
+ - /usr/include/linux/ioprio.h not available on Ubuntu, so we can't use it instead
+
+ ScheduleForBackgroundProcessing() : oldIoPrio(getIoPriority(IOPRIO_WHO_PROCESS, ::getpid()))
+ {
+ if (oldIoPrio != -1)
+ setIoPriority(::getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
+ }
+ ~ScheduleForBackgroundProcessing()
+ {
+ if (oldIoPrio != -1)
+ setIoPriority(::getpid(), oldIoPrio);
+ }
+
+private:
+ static int getIoPriority(pid_t pid)
+ {
+ return ::syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid);
+ }
+ static int setIoPriority(pid_t pid, int ioprio)
+ {
+ return ::syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio);
+ }
+
+ const int oldIoPrio;
+};
+*/
+
+#elif defined FFS_MAC
+//https://developer.apple.com/library/mac/#qa/qa1340
+struct PreventStandby::Pimpl
+{
+ Pimpl() : assertionID() {}
+ IOPMAssertionID assertionID;
+};
+
+PreventStandby::PreventStandby() : pimpl(make_unique<Pimpl>())
+{
+ if (::IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep,
+ kIOPMAssertionLevelOn,
+ CFSTR("FreeFileSync"),
+ &pimpl->assertionID) != kIOReturnSuccess)
+ throw FileError(_("Failed to suspend system sleep mode."));
+}
+
+PreventStandby::~PreventStandby()
+{
+ ::IOPMAssertionRelease(pimpl->assertionID);
+}
+
+
+struct ScheduleForBackgroundProcessing::Pimpl
+{
+ Pimpl() : oldIoPrio() {}
+ int oldIoPrio;
+};
+
+
+ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() : pimpl(make_unique<Pimpl>())
+{
+ pimpl->oldIoPrio = ::getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS);
+ if (pimpl->oldIoPrio == -1)
+ throw FileError(_("Cannot change process I/O priorities.") + L" (r)" + L"\n\n" + getLastErrorFormatted());
+
+ if (::setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE) != 0)
+ throw FileError(_("Cannot change process I/O priorities.") + L" (w)" + L"\n\n" + getLastErrorFormatted());
+}
+
+
+ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing()
+{
+ ::setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, pimpl->oldIoPrio);
+}
+#endif \ No newline at end of file
diff --git a/zen/process_priority.h b/zen/process_priority.h
index c0bae667..78e49cd0 100644
--- a/zen/process_priority.h
+++ b/zen/process_priority.h
@@ -1,58 +1,36 @@
+// **************************************************************************
+// * 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 PREVENTSTANDBY_H_INCLUDED
#define PREVENTSTANDBY_H_INCLUDED
-#ifdef FFS_WIN
-#include "win.h" //includes "windows.h"
-
-#elif defined FFS_LINUX
-#endif
+#include <memory>
+#include <zen/file_error.h>
namespace zen
{
-struct PreventStandby //signal a "busy" state to the operating system
+//signal a "busy" state to the operating system
+class PreventStandby
{
-#ifdef FFS_WIN
- PreventStandby () { ::SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED /* | ES_AWAYMODE_REQUIRED*/ ); }
- ~PreventStandby() { ::SetThreadExecutionState(ES_CONTINUOUS); }
-#endif
+public:
+ PreventStandby(); //throw FileError
+ ~PreventStandby();
+private:
+ struct Pimpl;
+ std::unique_ptr<Pimpl> pimpl;
};
-
-struct ScheduleForBackgroundProcessing //lower CPU and file I/O priorities
+//lower CPU and file I/O priorities
+class ScheduleForBackgroundProcessing
{
-#ifdef FFS_WIN
-#ifndef PROCESS_MODE_BACKGROUND_BEGIN
-#define PROCESS_MODE_BACKGROUND_BEGIN 0x00100000 // Windows Server 2003 and Windows XP/2000: This value is not supported!
-#define PROCESS_MODE_BACKGROUND_END 0x00200000 //
-#endif
-
- ScheduleForBackgroundProcessing () { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_BEGIN); } //this call lowers CPU priority, too!!
- ~ScheduleForBackgroundProcessing() { ::SetPriorityClass(::GetCurrentProcess(), PROCESS_MODE_BACKGROUND_END ); }
-
-#elif defined FFS_LINUX
- /*
- CPU prio:
- int getpriority(PRIO_PROCESS, 0); - errno caveat!
- int ::setpriority(PRIO_PROCESS, 0, int prio); //a zero value for "who" denotes the calling process
-
- priority can be decreased, but NOT increased :(
-
- file I/O prio:
- ScheduleForBackgroundProcessing() : oldIoPrio(::ioprio_get(IOPRIO_WHO_PROCESS, ::getpid()))
- {
- if (oldIoPrio != -1)
- ::ioprio_set(IOPRIO_WHO_PROCESS, ::getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
- }
- ~ScheduleForBackgroundProcessing()
- {
- if (oldIoPrio != -1)
- ::ioprio_set(IOPRIO_WHO_PROCESS, ::getpid(), oldIoPrio);
- }
-
- private:
- const int oldIoPrio;
- */
-#endif
+public:
+ ScheduleForBackgroundProcessing(); //throw FileError
+ ~ScheduleForBackgroundProcessing();
+private:
+ struct Pimpl;
+ std::unique_ptr<Pimpl> pimpl;
};
}
diff --git a/zen/read_txt.cpp b/zen/read_txt.cpp
index 0fd310c2..7566ff14 100644
--- a/zen/read_txt.cpp
+++ b/zen/read_txt.cpp
@@ -5,6 +5,7 @@ using namespace zen;
namespace
{
+warn_static("superfluous method")
std::string detectLineBreak(const Zstring& filename) //throw FileError
{
//read a (hopefully) significant portion of data
@@ -51,6 +52,7 @@ ExtractLines::ExtractLines(const Zstring& filename, const std::string& lineBreak
bool ExtractLines::getLine(std::string& output) //throw FileError
{
+ warn_static("don't use lineBreak, but support any of r, n, rn!!!")
for (;;)
{
//check if full line is in buffer
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 07803e50..2a82cd24 100644
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -5,14 +5,13 @@
// **************************************************************************
#include "recycler.h"
-#include <stdexcept>
-#include <iterator>
+//#include <stdexcept>
+//#include <iterator>
#include <zen/file_handling.h>
#ifdef FFS_WIN
-#include <algorithm>
-#include <functional>
-#include <vector>
+//#include <algorithm>
+//#include <functional>
#include <zen/dll.h>
#include <zen/win.h> //includes "windows.h"
#include <zen/assert_static.h>
@@ -24,6 +23,9 @@
#include <zen/scope_guard.h>
#include <sys/stat.h>
#include <gio/gio.h>
+
+#elif defined FFS_MAC
+#include <CoreServices/CoreServices.h>
#endif
using namespace zen;
@@ -142,7 +144,7 @@ void zen::recycleOrDelete(const std::vector<Zstring>& filenames, CallbackRecycli
bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
{
- if (!somethingExists(filename))
+ if (!somethingExists(filename)) //[!] do not optimize away, OS X needs this for reliable detection of "recycle bin missing"
return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it!
#ifdef FFS_WIN
@@ -151,13 +153,13 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
recycleOrDelete(filenames, nullptr); //throw FileError
#elif defined FFS_LINUX
- GFile* file = g_file_new_for_path(filename.c_str()); //never fails according to docu
+ GFile* file = ::g_file_new_for_path(filename.c_str()); //never fails according to docu
ZEN_ON_SCOPE_EXIT(g_object_unref(file);)
GError* error = nullptr;
- ZEN_ON_SCOPE_EXIT(if (error) g_error_free(error););
+ ZEN_ON_SCOPE_EXIT(if (error) ::g_error_free(error););
- if (!g_file_trash(file, nullptr, &error))
+ if (!::g_file_trash(file, nullptr, &error))
{
const std::wstring shortMsg = replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filename));
@@ -181,6 +183,53 @@ bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
throw FileError(shortMsg + L"\n\n" + L"Glib Error Code " + numberTo<std::wstring>(error->code) + /* L", " +
g_quark_to_string(error->domain) + */ L": " + utfCvrtTo<std::wstring>(error->message));
}
+
+#elif defined FFS_MAC
+ //we cannot use FSPathMoveObjectToTrashSync directly since it follows symlinks!
+
+ assert_static(sizeof(Zchar) == sizeof(char));
+ const UInt8* filenameUtf8 = reinterpret_cast<const UInt8*>(filename.c_str());
+
+ auto throwFileError = [&](OSStatus oss)
+ {
+ std::wstring msg = replaceCpy(_("Unable to move %x to the Recycle Bin!"), L"%x", fmtFileName(filename)) + L"\n\n"
+ + L"Result Code " + numberTo<std::wstring>(oss);
+ const char* description = GetMacOSStatusCommentString(oss);
+ if (description) //found no documentation for proper use of GetMacOSStatusCommentString
+ msg += L": " + utfCvrtTo<std::wstring>(description);
+ throw FileError(msg);
+ };
+
+ FSRef objectRef;
+ OSStatus rv = ::FSPathMakeRefWithOptions(filenameUtf8, //const UInt8 *path,
+ kFSPathMakeRefDoNotFollowLeafSymlink, //OptionBits options,
+ &objectRef, //FSRef *ref,
+ nullptr); //Boolean *isDirectory
+ if (rv != noErr)
+ throwFileError(rv);
+
+ //deprecated since OS X 10.8!!! "trashItemAtURL" should be used instead
+ OSStatus rv2 = ::FSMoveObjectToTrashSync(&objectRef, //const FSRef *source,
+ nullptr, //FSRef *target,
+ kFSFileOperationDefaultOptions); //OptionBits options
+ if (rv2 != noErr)
+ {
+ //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 = {};
+ if (::lstat(filename.c_str(), &fileInfo) != 0)
+ return false;
+
+ if (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode))
+ removeFile(filename); //throw FileError
+ else if (S_ISDIR(fileInfo.st_mode))
+ removeDirectory(filename); //throw FileError
+ return true;
+ }
+
+ throwFileError(rv2);
+ }
#endif
return true;
}
@@ -275,7 +324,7 @@ StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
*/
}
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
/*
We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not!
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 1a616559..95aa84fb 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -16,7 +16,7 @@
#include "privilege.h"
#include "long_path_prefix.h"
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
#include <unistd.h>
#endif
@@ -39,14 +39,6 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath); //throw FileError
-
-
-
-
-
-
-
-
//################################ implementation ################################
#ifdef _MSC_VER //I don't have Windows Driver Kit at hands right now, so unfortunately we need to redefine this structures and cross fingers...
typedef struct _REPARSE_DATA_BUFFER //from ntifs.h
@@ -145,7 +137,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
return output;
-#elif defined FFS_LINUX
+#elif defined FFS_LINUX || defined FFS_MAC
const int BUFFER_SIZE = 10000;
std::vector<char> buffer(BUFFER_SIZE);
diff --git a/zen/thread.h b/zen/thread.h
index 43917d13..ae865cc8 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -11,7 +11,7 @@
#include <memory>
//fix this pathetic boost thread warning mess
-#ifdef __MINGW32__
+#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
@@ -24,7 +24,7 @@
#include <boost/thread.hpp>
-#ifdef __MINGW32__
+#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#ifdef _MSC_VER
@@ -34,8 +34,8 @@
namespace zen
{
/*
-std::async replacement without crappy semantics:
- 1. guaranteed to run asynchronous
+std::async replacement without crappy semantics:
+ 1. guaranteed to run asynchronous
2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor
Example:
@@ -79,15 +79,6 @@ private:
-
-
-
-
-
-
-
-
-
//###################### implementation ######################
#ifndef BOOST_HAS_THREADS
#error just some paranoia check...
@@ -99,7 +90,7 @@ auto async2(Function fun) -> boost::unique_future<T> //support for workaround of
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK //mirror "boost/thread/future.hpp", hopefully they know what they're doing
boost::packaged_task<T()> pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/
#else
- boost::packaged_task<T> pt(std::move(fun));
+ boost::packaged_task<T> pt(std::move(fun));
#endif
auto fut = pt.get_future();
boost::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
diff --git a/zen/tick_count.h b/zen/tick_count.h
index 04ac0902..be4839ca 100644
--- a/zen/tick_count.h
+++ b/zen/tick_count.h
@@ -8,9 +8,18 @@
#define ZEN_TICK_COUNT_HEADER_3807326
#include <cstdint>
-//#include <algorithm>
#include "type_traits.h"
#include "basic_math.h"
+#ifdef FFS_WIN
+#include "win.h" //includes "windows.h"
+
+#elif defined FFS_LINUX
+#include <time.h> //Posix ::clock_gettime()
+
+#elif defined FFS_MAC
+#include <mach/mach_time.h>
+#endif
+//#include <algorithm>
//#include "assert_static.h"
//#include <cmath>
//template <class T> inline
@@ -20,11 +29,6 @@
//}
-#ifdef FFS_WIN
-#include "win.h" //includes "windows.h"
-#elif defined FFS_LINUX
-#include <time.h> //Posix ::clock_gettime()
-#endif
namespace zen
{
@@ -57,6 +61,8 @@ public:
typedef LARGE_INTEGER NativeVal;
#elif defined FFS_LINUX
typedef timespec NativeVal;
+#elif defined FFS_MAC
+ typedef uint64_t NativeVal;
#endif
TickVal() : val_() {}
@@ -67,7 +73,6 @@ public:
{
#ifdef FFS_WIN
return numeric::dist(lhs.val_.QuadPart, rhs.val_.QuadPart); //std::abs(a - b) can lead to overflow!
-
#elif defined FFS_LINUX
const auto distSec = numeric::dist(lhs.val_.tv_sec, rhs.val_.tv_sec);
const auto distNsec = numeric::dist(lhs.val_.tv_nsec, rhs.val_.tv_nsec);
@@ -75,11 +80,13 @@ public:
if (distSec > (std::numeric_limits<std::int64_t>::max() - distNsec) / 1000000000) //truncate instead of overflow!
return std::numeric_limits<std::int64_t>::max();
return distSec * 1000000000 + distNsec;
+#elif defined FFS_MAC
+ return numeric::dist(lhs.val_, rhs.val_);
#endif
}
inline friend
- bool operator<(const TickVal& lhs, const TickVal& rhs) //evaluate directly rather than reuse operator-
+ bool operator<(const TickVal& lhs, const TickVal& rhs)
{
#ifdef FFS_WIN
return lhs.val_.QuadPart < rhs.val_.QuadPart;
@@ -87,6 +94,8 @@ public:
if (lhs.val_.tv_sec != rhs.val_.tv_sec)
return lhs.val_.tv_sec < rhs.val_.tv_sec;
return lhs.val_.tv_nsec < rhs.val_.tv_nsec;
+#elif defined FFS_MAC
+ return lhs.val_ < rhs.val_;
#endif
}
@@ -109,6 +118,12 @@ std::int64_t ticksPerSec() //return 0 on error
#elif defined FFS_LINUX
return 1000000000; //precision: nanoseconds
+
+#elif defined FFS_MAC
+ mach_timebase_info_data_t tbi = {};
+ if (::mach_timebase_info(&tbi) != KERN_SUCCESS)
+ return 0;
+ return 1000000000 * tbi.denom / tbi.numer;
#endif
}
@@ -126,6 +141,9 @@ TickVal getTicks() //return 0 on error
timespec now = {};
if (::clock_gettime(CLOCK_MONOTONIC_RAW, &now) != 0) //CLOCK_MONOTONIC measures time reliably across processors!
return TickVal();
+
+#elif defined FFS_MAC
+ uint64_t now = ::mach_absolute_time(); //can this call fail???
#endif
return TickVal(now);
}
diff --git a/zen/warn_static.h b/zen/warn_static.h
index 70679a12..737a56fb 100644
--- a/zen/warn_static.h
+++ b/zen/warn_static.h
@@ -26,9 +26,8 @@ Usage:
#define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y)
#define warn_static(TXT) \
- typedef int STATIC_WARNING __attribute__ ((deprecated)); \
- enum { ZEN_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING) };
+ typedef int STATIC_WARNING_87903124 __attribute__ ((deprecated)); \
+ enum { ZEN_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) };
#endif
-
-#endif //WARN_STATIC_HEADER_08724567834560832745 \ No newline at end of file
+#endif //WARN_STATIC_HEADER_08724567834560832745
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index 7f4e79db..b371e598 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -6,93 +6,141 @@
#include "zstring.h"
#include <stdexcept>
-#include <zen/stl_tools.h>
#ifdef FFS_WIN
#include "dll.h"
#include "win_ver.h"
+
+#elif defined FFS_MAC
+#include <ctype.h> //toupper()
#endif
#ifndef NDEBUG
+#include "thread.h" //includes <boost/thread.hpp>
#include <iostream>
-#include <cstdlib>
#endif
using namespace zen;
#ifndef NDEBUG
-LeakChecker::~LeakChecker()
+namespace
{
- if (!activeStrings.empty())
+class LeakChecker //small test for memory leaks
+{
+public:
+ void insert(const void* ptr, size_t size)
{
- std::string leakingStrings;
+ boost::lock_guard<boost::mutex> dummy(lockActStrings);
+ if (activeStrings.find(ptr) != activeStrings.end())
+ reportProblem("Fatal Error: New memory points into occupied space: " + rawMemToString(ptr, size));
- int items = 0;
- for (auto it = activeStrings.begin(); it != activeStrings.end() && items < 20; ++it, ++items)
- leakingStrings += "\"" + rawMemToString(it->first, it->second) + "\"\n";
+ activeStrings[ptr] = size;
+ }
- const std::string message = std::string("Memory leak detected!") + "\n\n"
- + "Candidates:\n" + leakingStrings;
-#ifdef FFS_WIN
- MessageBoxA(nullptr, message.c_str(), "Error", 0);
-#else
- std::cerr << message;
- std::abort();
-#endif
+ void remove(const void* ptr)
+ {
+ boost::lock_guard<boost::mutex> dummy(lockActStrings);
+ if (activeStrings.find(ptr) == activeStrings.end())
+ reportProblem("Fatal Error: No memory available for deallocation at this location!");
+
+ activeStrings.erase(ptr);
}
-}
+ static LeakChecker& instance() { static LeakChecker inst; return inst; }
-LeakChecker& LeakChecker::instance()
-{
- static LeakChecker inst;
- return inst;
-}
+private:
+ LeakChecker() {}
+ ~LeakChecker()
+ {
+ if (!activeStrings.empty())
+ {
+ std::string leakingStrings;
-//caveat: function scope static initialization is not thread-safe in VS 2010! => make sure to call at app start!
-namespace
-{
-const LeakChecker& dummy = LeakChecker::instance();
-}
+ int items = 0;
+ for (auto it = activeStrings.begin(); it != activeStrings.end() && items < 20; ++it, ++items)
+ leakingStrings += "\"" + rawMemToString(it->first, it->second) + "\"\n";
+ const std::string message = std::string("Memory leak detected!") + "\n\n"
+ + "Candidates:\n" + leakingStrings;
+#ifdef FFS_WIN
+ MessageBoxA(nullptr, message.c_str(), "Error", 0);
+#else
+ std::cerr << message;
+ std::abort();
+#endif
+ }
+ }
-std::string LeakChecker::rawMemToString(const void* ptr, size_t size)
-{
- std::string output = std::string(reinterpret_cast<const char*>(ptr), size);
- vector_remove_if(output, [](char& c) { return c == 0; }); //remove intermediate 0-termination
- if (output.size() > 100)
- output.resize(100);
- return output;
-}
+ LeakChecker(const LeakChecker&);
+ LeakChecker& operator=(const LeakChecker&);
+ static std::string rawMemToString(const void* ptr, size_t size)
+ {
+ std::string output(reinterpret_cast<const char*>(ptr), size);
+ vector_remove_if(output, [](char& c) { return c == 0; }); //remove intermediate 0-termination
+ if (output.size() > 100)
+ output.resize(100);
+ return output;
+ }
-void LeakChecker::reportProblem(const std::string& message) //throw std::logic_error
-{
+ void reportProblem(const std::string& message) //throw std::logic_error
+ {
#ifdef FFS_WIN
- ::MessageBoxA(nullptr, message.c_str(), "Error", 0);
+ ::MessageBoxA(nullptr, message.c_str(), "Error", 0);
#else
- std::cerr << message;
+ std::cerr << message;
#endif
- throw std::logic_error("Memory leak! " + message);
+ throw std::logic_error("Memory leak! " + message);
+ }
+
+ boost::mutex lockActStrings;
+ 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();
}
+
+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); }
#endif //NDEBUG
+/*
+Perf test: compare strings 10 mio times; 64 bit build
+-----------------------------------------------------
+ string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-"
+ string b = "fjK84$%kgfj$%T\\\\gfFg\\gsdgf\\fgSy----------dfdf"
+
+Windows (UTF16 wchar_t)
+ 4 ns | wcscmp
+ 67 ns | CompareStringOrdinalFunc+ + bIgnoreCase
+314 ns | LCMapString + wmemcmp
+
+OS X (UTF8 char)
+ 6 ns | strcmp
+ 98 ns | strcasecmp
+ 120 ns | strncasecmp + std::min(sizeLhs, sizeRhs);
+ 856 ns | CFStringCreateWithCString + CFStringCompare(kCFCompareCaseInsensitive)
+1110 ns | CFStringCreateWithCStringNoCopy + CFStringCompare(kCFCompareCaseInsensitive)
+________________________
+time per call | function
+*/
+
+
#ifdef FFS_WIN
namespace
{
-#ifndef LOCALE_INVARIANT //invariant locale has been introduced with XP
-#define LOCALE_INVARIANT 0x007f
+#ifndef LOCALE_INVARIANT //not known to MinGW
+#define LOCALE_INVARIANT 0x007f
#endif
-
//warning: LOCALE_INVARIANT is NOT available with Windows 2000, so we have to make yet another distinction...
const LCID ZSTRING_INVARIANT_LOCALE = zen::winXpOrLater() ?
LOCALE_INVARIANT :
MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); //see: http://msdn.microsoft.com/en-us/goglobal/bb688122.aspx
-
//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,
@@ -104,7 +152,7 @@ const SysDllFun<CompareStringOrdinalFunc> compareStringOrdinal = SysDllFun<Compa
}
-int z_impl::compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t sizeLhs, size_t sizeRhs)
+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
@@ -165,26 +213,12 @@ int z_impl::compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t s
return rv;
}
}
-
return static_cast<int>(sizeLhs) - static_cast<int>(sizeRhs);
}
-
- // const int rv = CompareString(
- // invariantLocale, //locale independent
- // NORM_IGNORECASE | SORT_STRINGSORT, //comparison-style options
- // a, //pointer to first string
- // aCount, //size, in bytes or characters, of first string
- // b, //pointer to second string
- // bCount); //size, in bytes or characters, of second string
- //
- // if (rv == 0)
- // throw std::runtime_error("Error comparing strings!");
- // else
- // return rv - 2; //convert to C-style string compare result
}
-void z_impl::makeUpperCaseWin(wchar_t* str, size_t size)
+void z_impl::makeFilenameUpperCase(wchar_t* str, size_t size)
{
if (size == 0) //LCMapString does not allow input sizes of 0!
return;
@@ -194,38 +228,10 @@ void z_impl::makeUpperCaseWin(wchar_t* str, size_t size)
throw std::runtime_error("Error converting to upper case! (LCMapString)");
}
-
-/*
-#include <fstream>
- extern std::wofstream statShared;
-extern std::wofstream statLowCapacity;
-extern std::wofstream statOverwrite;
-
-std::wstring p(ptr);
-p.erase(std::remove(p.begin(), p.end(), L'\n'), p.end());
-p.erase(std::remove(p.begin(), p.end(), L','), p.end());
-
- if (descr(ptr)->refCount > 1)
- statShared <<
- minCapacity << L"," <<
- descr(ptr)->refCount << L"," <<
- descr(ptr)->length << L"," <<
- descr(ptr)->capacity << L"," <<
- p << L"\n";
-else if (minCapacity > descr(ptr)->capacity)
- statLowCapacity <<
- minCapacity << L"," <<
- descr(ptr)->refCount << L"," <<
- descr(ptr)->length << L"," <<
- descr(ptr)->capacity << L"," <<
- p << L"\n";
-else
- statOverwrite <<
- minCapacity << L"," <<
- descr(ptr)->refCount << L"," <<
- descr(ptr)->length << L"," <<
- descr(ptr)->capacity << L"," <<
- p << L"\n";
-*/
-
-#endif //FFS_WIN
+#elif defined FFS_MAC
+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!
+}
+#endif
diff --git a/zen/zstring.h b/zen/zstring.h
index df96df7b..f4a79181 100644
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -7,72 +7,38 @@
#ifndef ZSTRING_H_INCLUDED
#define ZSTRING_H_INCLUDED
-#include <cstring> //strcmp()
#include "string_base.h"
-
-#ifndef NDEBUG
-#include "thread.h" //includes <boost/thread.hpp>
-#include <map>
+#ifdef FFS_LINUX
+#include <cstring> //strcmp
+#elif defined FFS_MAC
+#include <strings.h> //strcasecmp
#endif
#ifndef NDEBUG
-class LeakChecker //small test for memory leaks
+namespace z_impl
{
-public:
- void insert(const void* ptr, size_t size)
- {
- boost::lock_guard<boost::mutex> dummy(lockActStrings);
- if (activeStrings.find(ptr) != activeStrings.end())
- reportProblem("Fatal Error: New memory points into occupied space: " + rawMemToString(ptr, size));
-
- activeStrings[ptr] = size;
- }
-
- void remove(const void* ptr)
- {
- boost::lock_guard<boost::mutex> dummy(lockActStrings);
- if (activeStrings.find(ptr) == activeStrings.end())
- reportProblem("Fatal Error: No memory available for deallocation at this location!");
-
- activeStrings.erase(ptr);
- }
-
- static LeakChecker& instance();
-
-private:
- LeakChecker() {}
- LeakChecker(const LeakChecker&);
- LeakChecker& operator=(const LeakChecker&);
- ~LeakChecker();
-
- static std::string rawMemToString(const void* ptr, size_t size);
- void reportProblem(const std::string& message); //throw std::logic_error
-
- boost::mutex lockActStrings;
- zen::hash_map<const void*, size_t> activeStrings;
-};
+void leakCheckerInsert(const void* ptr, size_t size);
+void leakCheckerRemove(const void* ptr);
+}
#endif //NDEBUG
-
class AllocatorFreeStoreChecked
{
public:
static void* allocate(size_t size) //throw std::bad_alloc
{
-#ifndef NDEBUG
void* newMem = ::operator new(size);
- LeakChecker::instance().insert(newMem, size); //test Zbase for memory leaks
- return newMem;
-#else
- return ::operator new(size);
+#ifndef NDEBUG
+ z_impl::leakCheckerInsert(newMem, size); //test Zbase for memory leaks
#endif
+ return newMem;
}
static void deallocate(void* ptr)
{
#ifndef NDEBUG
- LeakChecker::instance().remove(ptr); //check for memory leaks
+ z_impl::leakCheckerRemove(ptr); //check for memory leaks
#endif
::operator delete(ptr);
}
@@ -82,66 +48,50 @@ public:
//############################## helper functions #############################################
-#if defined(FFS_WIN) || defined(FFS_LINUX)
-//Compare filenames: Windows does NOT distinguish between upper/lower-case, while Linux DOES
-template <class T, template <class, class> class SP, class AP> int cmpFileName(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs);
-
-struct LessFilename //case-insensitive on Windows, case-sensitive on Linux
-{
- template <class T, template <class, class> class SP, class AP>
- bool operator()(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) const;
-};
-
-struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux
-{
- template <class T, template <class, class> class SP, class AP>
- bool operator()(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) const;
-};
-#endif
-
-#ifdef FFS_WIN
-template <template <class, class> class SP, class AP>
-void makeUpper(zen::Zbase<wchar_t, SP, AP>& str);
-#endif
#ifdef FFS_WIN //Windows encodes Unicode as UTF-16 wchar_t
typedef wchar_t Zchar;
#define Zstr(x) L ## x
const Zchar FILE_NAME_SEPARATOR = L'\\';
-#elif defined FFS_LINUX //Linux uses UTF-8
+#elif defined FFS_LINUX || defined FFS_MAC //Linux uses UTF-8
typedef char Zchar;
#define Zstr(x) x
const Zchar FILE_NAME_SEPARATOR = '/';
-
-#else
-#error define your platform: FFS_WIN or FFS_LINUX
#endif
//"The reason for all the fuss above" - Loki/SmartPtr
-//a high-performance string for use as file name in multithreaded contexts
+//a high-performance string for interfacing with native OS APIs and multithreaded contexts
typedef zen::Zbase<Zchar, zen::StorageRefCountThreadSafe, AllocatorFreeStoreChecked> Zstring;
-inline
-Zstring appendSeparator(Zstring path) //support rvalue references!
-{
- return endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR);
-}
-
-
-
-
-
-
-
-
-
+//Compare filenames: Windows does NOT distinguish between upper/lower-case, while Linux DOES
+template <template <class, class> class SP, class AP>
+int cmpFileName(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs);
+struct LessFilename //case-insensitive on Windows, case-sensitive on Linux
+{
+ template <template <class, class> class SP, class AP>
+ bool operator()(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) const { return cmpFileName(lhs, rhs) < 0; }
+};
+struct EqualFilename //case-insensitive on Windows, case-sensitive on Linux
+{
+ template <template <class, class> class SP, class AP>
+ bool operator()(const zen::Zbase<Zchar, SP, AP>& lhs, const zen::Zbase<Zchar, SP, AP>& rhs) const { return cmpFileName(lhs, rhs) == 0; }
+};
+#if defined FFS_WIN || defined FFS_MAC
+template <template <class, class> class SP, class AP>
+void makeUpper(zen::Zbase<Zchar, SP, AP>& str);
+#endif
+inline
+Zstring appendSeparator(Zstring path) //support rvalue references!
+{
+ return endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR);
+}
@@ -149,53 +99,35 @@ Zstring appendSeparator(Zstring path) //support rvalue references!
//################################# inline implementation ########################################
-#if defined(FFS_WIN) || defined(FFS_LINUX)
namespace z_impl
{
-int compareFilenamesWin(const wchar_t* lhs, const wchar_t* rhs, size_t sizeLhs, size_t sizeRhs);
-void makeUpperCaseWin(wchar_t* str, size_t size);
-}
-
-
-template <class T, template <class, class> class SP, class AP> inline
-int cmpFileName(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs)
-{
-#ifdef FFS_WIN
- return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length());
-#elif defined FFS_LINUX
- return ::strcmp(lhs.c_str(), rhs.c_str()); //POSIX filenames don't have embedded 0
+#if defined FFS_WIN
+int compareFilenamesNoCase(const Zchar* lhs, const Zchar* rhs, size_t sizeLhs, size_t sizeRhs);
#endif
-}
-
-
-template <class T, template <class, class> class SP, class AP> inline
-bool LessFilename::operator()(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) const
-{
-#ifdef FFS_WIN
- return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length()) < 0;
-#elif defined FFS_LINUX
- return ::strcmp(lhs.c_str(), rhs.c_str()) < 0; //POSIX filenames don't have embedded 0
+#if defined FFS_WIN || defined FFS_MAC
+void makeFilenameUpperCase(Zchar* str, size_t size);
#endif
}
-template <class T, template <class, class> class SP, class AP> inline
-bool EqualFilename::operator()(const zen::Zbase<T, SP, AP>& lhs, const zen::Zbase<T, SP, AP>& rhs) const
+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)
{
-#ifdef FFS_WIN
- return z_impl::compareFilenamesWin(lhs.data(), rhs.data(), lhs.length(), rhs.length()) == 0;
+#if defined FFS_WIN
+ return z_impl::compareFilenamesNoCase(lhs.data(), rhs.data(), lhs.length(), rhs.length());
#elif defined FFS_LINUX
- return ::strcmp(lhs.c_str(), rhs.c_str()) == 0; //POSIX filenames don't have embedded 0
+ 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!
#endif
}
-#endif //defined(FFS_WIN) || defined(FFS_LINUX)
-#ifdef FFS_WIN
+#if defined FFS_WIN || defined FFS_MAC
template <template <class, class> class SP, class AP> inline
-void makeUpper(zen::Zbase<wchar_t, SP, AP>& str)
+void makeUpper(zen::Zbase<Zchar, SP, AP>& str)
{
- z_impl::makeUpperCaseWin(str.begin(), str.length());
+ z_impl::makeFilenameUpperCase(str.begin(), str.length());
}
#endif
bgstack15