summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:20:29 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:20:29 +0200
commitb8f13e45be884dc12884ebe8f3dcd9eecb23a106 (patch)
tree22a6d8b96815d626061ff3e2d432c13078fca5c4 /zen
parent5.4 (diff)
downloadFreeFileSync-b8f13e45be884dc12884ebe8f3dcd9eecb23a106.tar.gz
FreeFileSync-b8f13e45be884dc12884ebe8f3dcd9eecb23a106.tar.bz2
FreeFileSync-b8f13e45be884dc12884ebe8f3dcd9eecb23a106.zip
5.5
Diffstat (limited to 'zen')
-rw-r--r--zen/FindFilePlus/find_file_plus.cpp2
-rw-r--r--zen/error_log.h10
-rw-r--r--zen/file_handling.cpp238
-rw-r--r--zen/file_handling.h10
-rw-r--r--zen/file_io.cpp2
-rw-r--r--zen/file_io.h10
-rw-r--r--zen/file_traverser.cpp71
-rw-r--r--zen/file_update_handle.h70
-rw-r--r--zen/fixed_list.h53
-rw-r--r--zen/long_path_prefix.h2
-rw-r--r--zen/optional.h15
-rw-r--r--zen/read_txt.cpp12
-rw-r--r--zen/recycler.cpp68
-rw-r--r--zen/recycler.h4
-rw-r--r--zen/stl_tools.h17
-rw-r--r--zen/string_base.h8
-rw-r--r--zen/string_tools.h6
-rw-r--r--zen/string_traits.h5
-rw-r--r--zen/symlink_target.h2
-rw-r--r--zen/thread.h15
20 files changed, 334 insertions, 286 deletions
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp
index 9fba5f1a..19f43998 100644
--- a/zen/FindFilePlus/find_file_plus.cpp
+++ b/zen/FindFilePlus/find_file_plus.cpp
@@ -296,7 +296,7 @@ void FileSearcher::readDirImpl(FileInformation& output) //throw FileError
rv == STATUS_INVALID_INFO_CLASS ||
rv == STATUS_UNSUCCESSFUL ||
rv == STATUS_ACCESS_VIOLATION ||
- rv == STATUS_NO_SUCH_FILE)
+ rv == STATUS_NO_SUCH_FILE) //[!]
rv = STATUS_NOT_SUPPORTED;
throw NtFileError(rv); //throws STATUS_NO_MORE_FILES when finished
diff --git a/zen/error_log.h b/zen/error_log.h
index f3f67233..bbb36f00 100644
--- a/zen/error_log.h
+++ b/zen/error_log.h
@@ -17,10 +17,10 @@ namespace zen
{
enum MessageType
{
- TYPE_INFO = 1,
- TYPE_WARNING = 2,
- TYPE_ERROR = 4,
- TYPE_FATAL_ERROR = 8,
+ TYPE_INFO = 0x1,
+ TYPE_WARNING = 0x2,
+ TYPE_ERROR = 0x4,
+ TYPE_FATAL_ERROR = 0x8,
};
struct LogEntry
@@ -130,4 +130,4 @@ inline std::wstring formatMessage(const LogEntry& entry) { return formatMessageI
}
-#endif // ERRORLOGGING_H_INCLUDED
+#endif //ERRORLOGGING_H_INCLUDED
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index f004e09c..ebfe4d19 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -23,7 +23,6 @@
#include "long_path_prefix.h"
#include <Aclapi.h>
#include "dst_hack.h"
-#include "file_update_handle.h"
#include "win_ver.h"
#include "IFileOperation/file_op.h"
@@ -703,18 +702,25 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, Callb
}
//if moving failed treat as error (except when it tried to move to a different volume: in this case we will copy the directory)
catch (const ErrorDifferentVolume&) {}
- catch (const ErrorTargetExisting&) {}
+ catch (const ErrorTargetExisting& ) {}
//create target
if (symlinkExists(sourceDir))
{
- if (!dirExists(targetDir))
+ if (!symlinkExists(targetDir))
copySymlink(sourceDir, targetDir, false); //throw FileError -> don't copy permissions
}
else
{
- if (!dirExists(targetDir)) //check even if ErrorTargetExisting: me may have clashed with a file of the same name!!!
- createDirectory(targetDir, sourceDir, false); //throw FileError
+ try
+ {
+ makeNewDirectory(targetDir, sourceDir, false); //FileError, ErrorTargetExisting
+ }
+ catch (const ErrorTargetExisting&)
+ {
+ if (!dirExists(targetDir))
+ throw; //clashed with a file or symlink of the same name!!!
+ }
//move files/folders recursively
TraverseOneLevel::NameList fileList; //list of names: 1. short 2.long
@@ -888,23 +894,6 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
{
*/
- //may need to remove the readonly-attribute (e.g. FAT usb drives)
- FileUpdateHandle targetHandle(filename, [=]
- {
- return ::CreateFile(applyLongPathPrefix(filename).c_str(),
- FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
- //avoids mysterious "access denied" when using "GENERIC_READ | GENERIC_WRITE" on a read-only file, even after read-only was removed right before:
- //https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS | //needed to open a directory
- (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //process symlinks
- nullptr);
- });
-
- if (targetHandle.get() == INVALID_HANDLE_VALUE)
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
/*
if (hTarget == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_SHARING_VIOLATION)
::Sleep(retryInterval); //wait then retry
@@ -913,12 +902,87 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
}
*/
- auto isNullTime = [](const FILETIME & ft) { return ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0; };
+ //temporarily reset read-only flag if required
+ DWORD attribs = INVALID_FILE_ATTRIBUTES;
+ ZEN_ON_SCOPE_EXIT(
+ if (attribs != INVALID_FILE_ATTRIBUTES)
+ ::SetFileAttributes(applyLongPathPrefix(filename).c_str(), attribs);
+ );
+
+ auto removeReadonly = [&]() -> bool //may need to remove the readonly-attribute (e.g. on FAT usb drives)
+ {
+ if (attribs == INVALID_FILE_ATTRIBUTES)
+ {
+ const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
+ if (tmpAttr == INVALID_FILE_ATTRIBUTES)
+ throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+
+ if (tmpAttr & FILE_ATTRIBUTE_READONLY)
+ {
+ if (!::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_NORMAL))
+ throw FileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+
+ attribs = tmpAttr; //reapplied on scope exit
+ return true;
+ }
+ }
+ return false;
+ };
+
+ auto openFile = [&](bool conservativeApproach)
+ {
+ return ::CreateFile(applyLongPathPrefix(filename).c_str(),
+ (conservativeApproach ?
+ //some NAS seem to have issues with FILE_WRITE_ATTRIBUTES, even worse, they may fail silently!
+ //http://sourceforge.net/tracker/?func=detail&atid=1093081&aid=3536680&group_id=234430
+ //Citrix shares seem to have this issue, too, but at least fail with "access denied" => try generic access first:
+ GENERIC_READ | GENERIC_WRITE :
+ //avoids mysterious "access denied" when using "GENERIC_READ | GENERIC_WRITE" on a read-only file, even *after* read-only was removed directly before the call!
+ //http://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430
+ //since former gives an error notification we may very well try FILE_WRITE_ATTRIBUTES second.
+ FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES),
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | //needed to open a directory
+ (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //process symlinks
+ nullptr);
+ };
+
+ HANDLE hFile = INVALID_HANDLE_VALUE;
+ for (int i = 0; i < 2; ++i) //we will get this handle, no matter what! :)
+ {
+ //1. be conservative
+ hFile = openFile(true);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ if (::GetLastError() == ERROR_ACCESS_DENIED) //fails if file is read-only (or for "other" reasons)
+ if (removeReadonly())
+ continue;
- if (!::SetFileTime(targetHandle.get(), //__in HANDLE hFile,
+ //2. be a *little* fancy
+ hFile = openFile(false);
+ if (hFile == INVALID_HANDLE_VALUE)
+ {
+ if (::GetLastError() == ERROR_ACCESS_DENIED)
+ if (removeReadonly())
+ continue;
+
+ //3. after these herculean stunts we give up...
+ throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+ }
+ }
+ break;
+ }
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
+
+
+ auto isNullTime = [](const FILETIME& ft) { return ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0; };
+
+ if (!::SetFileTime(hFile, //__in HANDLE hFile,
!isNullTime(creationTime) ? &creationTime : nullptr, //__in_opt const FILETIME *lpCreationTime,
- nullptr, //__in_opt const FILETIME *lpLastAccessTime,
- &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime
+ nullptr, //__in_opt const FILETIME *lpLastAccessTime,
+ &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime
{
auto lastErr = ::GetLastError();
@@ -933,7 +997,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
{
auto setFileInfo = [&](FILE_BASIC_INFO basicInfo) //throw FileError; no const& since SetFileInformationByHandle() requires non-const parameter!
{
- if (!setFileInformationByHandle(targetHandle.get(), //__in HANDLE hFile,
+ if (!setFileInformationByHandle(hFile, //__in HANDLE hFile,
FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
&basicInfo, //__in LPVOID lpFileInformation,
sizeof(basicInfo))) //__in DWORD dwBufferSize
@@ -950,7 +1014,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
//---------------------------------------------------------------------------
BY_HANDLE_FILE_INFORMATION fileInfo = {};
- if (::GetFileInformationByHandle(targetHandle.get(), &fileInfo))
+ if (::GetFileInformationByHandle(hFile, &fileInfo))
if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
{
FILE_BASIC_INFO basicInfo = {}; //undocumented: file times of "0" stand for "don't change"
@@ -959,6 +1023,7 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
if (!isNullTime(creationTime))
basicInfo.CreationTime = toLargeInteger(creationTime);
+ //set file time + attributes
setFileInfo(basicInfo); //throw FileError
try //... to restore original file attributes
@@ -972,10 +1037,10 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
lastErr = ERROR_SUCCESS;
}
}
-
- if (lastErr != ERROR_SUCCESS)
- throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastErr));
}
+
+ if (lastErr != ERROR_SUCCESS)
+ throw FileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted(lastErr));
}
#ifndef NDEBUG //dst hack: verify data written
@@ -1005,8 +1070,8 @@ void zen::setFileTime(const Zstring& filename, const Int64& modificationTime, Pr
else
{
struct timeval newTimes[2] = {};
- newTimes[0].tv_sec = ::time(nullptr); /* seconds */
- newTimes[0].tv_usec = 0; /* microseconds */
+ newTimes[0].tv_sec = ::time(nullptr); //seconds
+ newTimes[0].tv_usec = 0; //microseconds
newTimes[1].tv_sec = to<time_t>(modificationTime);
newTimes[1].tv_usec = 0;
@@ -1140,7 +1205,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymli
//copy permissions for files, directories or symbolic links: requires admin rights
-void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError;
+void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError
{
#ifdef FFS_WIN
//setting privileges requires admin rights!
@@ -1314,22 +1379,41 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
}
-void createDirectory_straight(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions, int level)
+void createDirectoryStraight(const Zstring& directory, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
+ const Zstring& templateDir,
+ bool copyFilePermissions)
{
- //default directory creation
#ifdef FFS_WIN
//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)!
//- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!)
- if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), nullptr))
+ 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();
+
+ 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
- if (::mkdir(directory.c_str(), 0755) != 0)
-#endif
+ if (::mkdir(directory.c_str(), 0755) != 0) //mode: drwxr-xr-x
{
- if (level != 0) return;
- throw FileError(replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
+ const std::wstring msg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted();
+ const ErrorCode lastError = getLastError();
+
+ if (lastError == EEXIST)
+ throw ErrorTargetExisting(msg);
+ else if (lastError == ENOENT)
+ throw ErrorTargetPathMissing(msg);
+ throw FileError(msg);
}
+#endif
if (!templateDir.empty())
{
@@ -1355,11 +1439,12 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD
const DWORD sourceAttr = ::GetFileAttributes(applyLongPathPrefix(sourcePath).c_str());
if (sourceAttr != INVALID_FILE_ATTRIBUTES)
{
+ ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), sourceAttr);
+ //copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx
+
const bool isCompressed = (sourceAttr & FILE_ATTRIBUTE_COMPRESSED) != 0;
const bool isEncrypted = (sourceAttr & FILE_ATTRIBUTE_ENCRYPTED) != 0;
- ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), sourceAttr);
-
if (isEncrypted)
::EncryptFile(directory.c_str()); //seems no long path is required (check passed!)
@@ -1391,6 +1476,7 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD
}
}
#endif
+
zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(directory); } catch (...) {} }); //ensure cleanup:
//enforce copying file permissions: it's advertized on GUI...
@@ -1402,47 +1488,48 @@ void createDirectory_straight(const Zstring& directory, const Zstring& templateD
}
-void createDirectoryRecursively(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions, int level)
+void createDirectoryRecursively(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions) //FileError, ErrorTargetExisting
{
- if (level == 100) //catch endless recursion
- return;
-
-#ifdef FFS_WIN
- std::unique_ptr<Fix8Dot3NameClash> fnc;
- if (somethingExists(directory))
+ try
{
- //handle issues with already existing short 8.3 file names on Windows
- if (have8dot3NameClash(directory))
- fnc.reset(new Fix8Dot3NameClash(directory)); //move clashing object to the side
- else if (dirExists(directory))
- return;
+ createDirectoryStraight(directory, templateDir, copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
}
-#elif defined FFS_LINUX
- if (somethingExists(directory))
+ catch (const ErrorTargetExisting&)
{
- if (dirExists(directory))
+#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
- else //if "not somethingExists" we need to create the parent directory
+ throw;
+ }
+ catch (const ErrorTargetPathMissing&)
{
- //try to create parent folders first
+ //we need to create parent directories first
const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR);
- if (!dirParent.empty() && !dirExists(dirParent))
+ 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, level + 1);
+ createDirectoryRecursively(dirParent, templateParent, copyFilePermissions); //throw
+
+ //now try again...
+ createDirectoryStraight(directory, templateDir, copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing
+ return;
}
+ throw;
}
-
- //now creation should be possible
- createDirectory_straight(directory, templateDir, copyFilePermissions, level); //throw FileError
}
}
-void zen::createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions)
+void zen::makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions) //FileError, ErrorTargetExisting
{
//remove trailing separator
const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ?
@@ -1453,13 +1540,22 @@ void zen::createDirectory(const Zstring& directory, const Zstring& templateDir,
beforeLast(templateDir, FILE_NAME_SEPARATOR) :
templateDir;
- createDirectoryRecursively(dirFormatted, templateFormatted, copyFilePermissions, 0);
+ createDirectoryRecursively(dirFormatted, templateFormatted, copyFilePermissions); //FileError, ErrorTargetExisting
}
-void zen::createDirectory(const Zstring& directory)
+void zen::makeDirectory(const Zstring& directory)
{
- zen::createDirectory(directory, Zstring(), false);
+ try
+ {
+ makeNewDirectory(directory, Zstring(), false); //FileError, ErrorTargetExisting
+ }
+ catch (const ErrorTargetExisting&)
+ {
+ if (dirExists(directory))
+ return;
+ throw; //clash with file (dir symlink is okay)
+ }
}
@@ -2254,7 +2350,7 @@ Zstring findUnusedTempName(const Zstring& filename)
{
Zstring output = filename + zen::TEMP_FILE_ENDING;
- //ensure uniqueness (+ minor race condition)
+ //ensure uniqueness (+ minor file system race condition!)
for (int i = 1; somethingExists(output); ++i)
output = filename + Zchar('_') + numberTo<Zstring>(i) + zen::TEMP_FILE_ENDING;
diff --git a/zen/file_handling.h b/zen/file_handling.h
index 8848fbc2..d6444da3 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -53,18 +53,18 @@ void removeDirectory(const Zstring& directory, CallbackRemoveDir* callback = nul
//rename file or directory: no copying!!!
-void renameFile(const Zstring& oldName, const Zstring& newName); //throw FileError;
+void renameFile(const Zstring& oldName, const Zstring& newName); //throw FileError
//move source to target across volumes; prerequisite: all super-directories of target exist
//if target already contains some files/dirs they are seen as remnants of a previous incomplete move - see comment in moveDirectoryImpl
-void moveFile(const Zstring& sourceFile, const Zstring& targetFile, CallbackMoveFile* callback); //throw FileError
-void moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, CallbackMoveFile* callback); //throw FileError
+void moveFile (const Zstring& sourceFile, const Zstring& targetFile, CallbackMoveFile* callback); //throw FileError
+void moveDirectory(const Zstring& sourceDir, const Zstring& targetDir, CallbackMoveFile* callback); //throw FileError
bool supportsPermissions(const Zstring& dirname); //throw FileError, derefernces symlinks
//creates superdirectories automatically:
-void createDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError;
-void createDirectory(const Zstring& directory); //throw FileError; -> function overload avoids default parameter ambiguity issues!
+void makeDirectory(const Zstring& directory); //throw FileError; do nothing if directory already exists!
+void makeNewDirectory(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //FileError, ErrorTargetExisting
struct FileAttrib
{
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 3b3c244d..77fcb691 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -81,7 +81,7 @@ FileInput::~FileInput()
}
-size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number of bytes read; throw (FileError)
+size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number of bytes read; throw FileError
{
#ifdef FFS_WIN
DWORD bytesRead = 0;
diff --git a/zen/file_io.h b/zen/file_io.h
index 33074d7e..b134e47a 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -19,6 +19,12 @@
namespace zen
{
+#ifdef FFS_WIN
+static const char LINE_BREAK[] = "\r\n";
+#elif defined FFS_LINUX
+static const char LINE_BREAK[] = "\n";
+#endif
+
//file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks)
#ifdef FFS_WIN
@@ -37,6 +43,8 @@ public:
size_t read(void* buffer, size_t bytesToRead); //throw FileError; returns actual number of bytes read
bool eof() { return eofReached; } //end of file reached
+ const Zstring& getFilename() const { return filename_; }
+
private:
FileInput(const FileInput&);
FileInput& operator=(const FileInput&);
@@ -61,6 +69,8 @@ public:
void write(const void* buffer, size_t bytesToWrite); //throw FileError
+ const Zstring& getFilename() const { return filename_; }
+
private:
FileOutput(const FileOutput&);
FileOutput& operator=(const FileOutput&);
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 3acb0edf..aa46d4f0 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -10,13 +10,12 @@
#include "symlink_target.h"
#ifdef FFS_WIN
-#include "win.h" //includes "windows.h"
+#include <zen/win_ver.h>
#include "long_path_prefix.h"
#include "dst_hack.h"
-#include "file_update_handle.h"
+#include "file_handling.h" //remove this huge dependency when getting rid of DST hack!! until then we need "setFileTime"
#include "dll.h"
#include "FindFilePlus/find_file_plus.h"
-#include <zen/win_ver.h>
#elif defined FFS_LINUX
#include <sys/stat.h>
@@ -179,11 +178,12 @@ struct Win32Traverser
{
struct DirHandle
{
- DirHandle() : searchHandle(nullptr), firstRead(true), firstData() {}
+ DirHandle() : searchHandle(nullptr), haveData(true), data() {}
HANDLE searchHandle;
- bool firstRead;
- WIN32_FIND_DATA firstData;
+
+ bool haveData;
+ WIN32_FIND_DATA data;
};
typedef WIN32_FIND_DATA FindData;
@@ -192,7 +192,7 @@ struct Win32Traverser
{
const Zstring& directoryPf = appendSeparator(directory);
- hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.firstData);
+ hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.data);
//no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
if (hnd.searchHandle == INVALID_HANDLE_VALUE)
throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
@@ -208,10 +208,10 @@ struct Win32Traverser
template <class FallbackFun>
static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo, FallbackFun) //throw FileError
{
- if (hnd.firstRead)
+ if (hnd.haveData)
{
- hnd.firstRead = false;
- ::memcpy(&fileInfo, &hnd.firstData, sizeof(fileInfo));
+ hnd.haveData = false;
+ ::memcpy(&fileInfo, &hnd.data, sizeof(fileInfo));
return true;
}
@@ -451,7 +451,7 @@ private:
if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error
fileInfo.lastWriteTimeRaw = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error)
else
- markForDstHack.push_back(std::make_pair(fullName, Trav::getModTimeRaw(findData)));
+ markForDstHack.push_back(std::make_pair(fullName, toTimeT(rawTime.writeTimeRaw)));
}
//####################################### DST hack ###########################################
@@ -467,52 +467,33 @@ private:
int failedAttempts = 0;
int filesToValidate = 50; //don't let data verification become a performance issue
- for (FilenameTimeList::const_iterator i = markForDstHack.begin(); i != markForDstHack.end(); ++i)
+ for (auto iter = markForDstHack.begin(); iter != markForDstHack.end(); ++iter)
{
if (failedAttempts >= 10) //some cloud storages don't support changing creation/modification times => don't waste (a lot of) time trying to
return;
- dstCallback.requestUiRefresh(i->first);
+ dstCallback.requestUiRefresh(iter->first);
- const dst::RawTime encodedTime = dst::fatEncodeUtcTime(i->second); //throw std::runtime_error
+ try
{
- //may need to remove the readonly-attribute (e.g. FAT usb drives)
- FileUpdateHandle updateHandle(i->first, [=]
- {
- return ::CreateFile(zen::applyLongPathPrefix(i->first).c_str(),
- FILE_WRITE_ATTRIBUTES,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory
- nullptr);
- });
- if (updateHandle.get() == INVALID_HANDLE_VALUE)
- {
- ++failedAttempts;
- assert(false); //don't throw exceptions due to dst hack here
- continue;
- }
-
- if (!::SetFileTime(updateHandle.get(),
- &encodedTime.createTimeRaw,
- nullptr,
- &encodedTime.writeTimeRaw))
- {
- ++failedAttempts;
- assert(false); //don't throw exceptions due to dst hack here
- continue;
- }
+ //set modification time including DST hack: this function is too clever to not introduce this dependency
+ setFileTime(iter->first, iter->second, SYMLINK_FOLLOW); //throw FileError
+ }
+ catch (FileError&)
+ {
+ ++failedAttempts;
+ assert(false); //don't throw exceptions due to dst hack here
+ continue;
}
//even at this point it's not sure whether data was written correctly, again cloud storages tend to lie about success status
- if (filesToValidate > 0)
+ if (filesToValidate-- > 0)
{
- --filesToValidate; //don't change during check!
+ const dst::RawTime encodedTime = dst::fatEncodeUtcTime(tofiletime(iter->second)); //throw std::runtime_error
//dst hack: verify data written; attention: this check may fail for "sync.ffs_lock"
WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {};
- ::GetFileAttributesEx(zen::applyLongPathPrefix(i->first).c_str(), //__in LPCTSTR lpFileName,
+ ::GetFileAttributesEx(zen::applyLongPathPrefix(iter->first).c_str(), //__in LPCTSTR lpFileName,
GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId,
&debugeAttr); //__out LPVOID lpFileInformation
@@ -528,7 +509,7 @@ private:
}
const bool isFatFileSystem;
- typedef std::vector<std::pair<Zstring, FILETIME> > FilenameTimeList;
+ typedef std::vector<std::pair<Zstring, Int64> > FilenameTimeList;
FilenameTimeList markForDstHack;
//####################################### DST hack ###########################################
diff --git a/zen/file_update_handle.h b/zen/file_update_handle.h
deleted file mode 100644
index 3df69f10..00000000
--- a/zen/file_update_handle.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#ifndef FILE_UPDATE_HANDLE_H_INCLUDED
-#define FILE_UPDATE_HANDLE_H_INCLUDED
-
-#include "win.h" //includes "windows.h"
-#include "long_path_prefix.h"
-
-namespace
-{
-//manage file handle to update existing files (temporarily resetting read-only if necessary)
-//CreateFileCmd: lambda directly returning non-owned file handle from ::CreateFile()
-class FileUpdateHandle
-{
-public:
- template <class CreateFileCmd>
- FileUpdateHandle(const Zstring& filename, CreateFileCmd cmd) :
- filenameFmt(zen::applyLongPathPrefix(filename)),
- hFile(INVALID_HANDLE_VALUE),
- attr(INVALID_FILE_ATTRIBUTES)
- {
- hFile = cmd();
- if (hFile == INVALID_HANDLE_VALUE)
- {
- //try to recover
- if (::GetLastError() == ERROR_ACCESS_DENIED) //function fails if file is read-only
- {
- //read-only file attribute may cause trouble: temporarily reset it
- const DWORD tmpAttr = ::GetFileAttributes(filenameFmt.c_str());
- if (tmpAttr != INVALID_FILE_ATTRIBUTES)
- {
- if (tmpAttr & FILE_ATTRIBUTE_READONLY)
- {
- if (::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL))
- {
- //guardErrorCode.dismiss();
- attr = tmpAttr; //"create" guard on read-only attribute
-
- //now try again
- hFile = cmd();
- }
- }
- else
- ::SetLastError(ERROR_ACCESS_DENIED);
- }
- }
- }
- }
-
- ~FileUpdateHandle()
- {
- if (hFile != INVALID_HANDLE_VALUE)
- ::CloseHandle(hFile);
-
- if (attr != INVALID_FILE_ATTRIBUTES)
- ::SetFileAttributes(filenameFmt.c_str(), attr);
- }
-
- //may return INVALID_FILE_ATTRIBUTES, in which case ::GetLastError() may be called directly after FileUpdateHandle()
- HANDLE get() const { return hFile; }
-
-private:
- FileUpdateHandle(const FileUpdateHandle&);
- FileUpdateHandle& operator=(const FileUpdateHandle&);
-
- Zstring filenameFmt;
- HANDLE hFile;
- DWORD attr;
-};
-}
-
-#endif // FILE_UPDATE_HANDLE_H_INCLUDED
diff --git a/zen/fixed_list.h b/zen/fixed_list.h
index eedbfab9..04a680ad 100644
--- a/zen/fixed_list.h
+++ b/zen/fixed_list.h
@@ -4,8 +4,8 @@
// * Copyright (C) ZenJu (zhnmju123 AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef PTR_WRAP_012384670856841394535
-#define PTR_WRAP_012384670856841394535
+#ifndef FIXED_LIST_01238467085684139453534
+#define FIXED_LIST_01238467085684139453534
#include <iterator>
@@ -19,12 +19,13 @@ class FixedList
struct Node
{
Node() : next(nullptr), val() {}
- template <class A> Node(A&& a) : next(nullptr), val(a) {}
- template <class A, class B> Node(A&& a, B&& b) : next(nullptr), val(a, b) {}
- template <class A, class B, class C> Node(A&& a, B&& b, C&& c) : next(nullptr), val(a, b, c) {}
- template <class A, class B, class C, class D> Node(A&& a, B&& b, C&& c, D&& d) : next(nullptr), val(a, b, c, d) {}
- template <class A, class B, class C, class D, class E> Node(A&& a, B&& b, C&& c, D&& d, E&& e) : next(nullptr), val(a, b, c, d, e) {}
- template <class A, class B, class C, class D, class E, class F> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) : next(nullptr), val(a, b, c, d, e, f) {}
+ //no variadic templates on VC2010... :(
+ template <class A> Node(A&& a) : next(nullptr), val(std::forward<A>(a)) {}
+ template <class A, class B> Node(A&& a, B&& b) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b)) {}
+ template <class A, class B, class C> Node(A&& a, B&& b, C&& c) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c)) {}
+ template <class A, class B, class C, class D> Node(A&& a, B&& b, C&& c, D&& d) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d)) {}
+ template <class A, class B, class C, class D, class E> Node(A&& a, B&& b, C&& c, D&& d, E&& e) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e)) {}
+ template <class A, class B, class C, class D, class E, class F> Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) : next(nullptr), val(std::forward<A>(a), std::forward<B>(b), std::forward<C>(c), std::forward<D>(d), std::forward<E>(e), std::forward<F>(f)) {}
Node* next; //singly linked list is sufficient
T val;
@@ -36,16 +37,7 @@ public:
lastInsert(nullptr),
sz(0) {}
- ~FixedList()
- {
- Node* ptr = first;
- while (ptr)
- {
- Node* tmp = ptr;
- ptr = ptr->next;
- delete tmp;
- }
- }
+ ~FixedList() { clear(); }
template <class NodeT, class U>
class ListIterator : public std::iterator<std::forward_iterator_tag, U>
@@ -73,6 +65,9 @@ public:
const_iterator begin() const { return first; }
const_iterator end () const { return const_iterator(); }
+ const_iterator cbegin() const { return first; }
+ const_iterator cend () const { return const_iterator(); }
+
reference front() { return first->val; }
const_reference front() const { return first->val; }
@@ -112,7 +107,20 @@ public:
}
}
- void clear() { remove_if([](T&) { return true; }); }
+ void clear()
+ {
+ Node* ptr = first;
+ while (ptr)
+ {
+ Node* tmp = ptr;
+ ptr = ptr->next;
+ delete tmp;
+ }
+
+ first = lastInsert = nullptr;
+ sz = 0;
+ }
+
bool empty() const { return first == nullptr; }
size_t size() const { return sz; }
@@ -120,7 +128,7 @@ private:
FixedList(const FixedList&);
FixedList& operator=(const FixedList&);
- void pushNode(Node* newNode)
+ void pushNode(Node* newNode) //throw()
{
++sz;
if (lastInsert == nullptr)
@@ -144,10 +152,9 @@ private:
}
Node* first;
- Node* lastInsert; //point to last insertion; required by emplace_back()
+ Node* lastInsert; //point to last insertion; required by efficient emplace_back()
size_t sz;
};
}
-
-#endif //PTR_WRAP_012384670856841394535
+#endif //FIXED_LIST_01238467085684139453534
diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h
index ed6308dc..1476e87d 100644
--- a/zen/long_path_prefix.h
+++ b/zen/long_path_prefix.h
@@ -19,7 +19,7 @@ namespace zen
3. path may already contain \\?\-prefix
*/
Zstring applyLongPathPrefix(const Zstring& path); //throw()
-Zstring applyLongPathPrefixCreateDir(const Zstring& path); //throw() -> special rule for ::CreateDirectoryEx(): MAX_PATH - 12(=^ 8.3 filename) is threshold
+Zstring applyLongPathPrefixCreateDir(const Zstring& path); //throw() -> special rule for ::CreateDirectory()/::CreateDirectoryEx(): MAX_PATH - 12(=^ 8.3 filename) is threshold
Zstring removeLongPathPrefix(const Zstring& path); //throw()
}
diff --git a/zen/optional.h b/zen/optional.h
index 2bd272ae..34e3d21d 100644
--- a/zen/optional.h
+++ b/zen/optional.h
@@ -38,9 +38,17 @@ public:
Opt(NoValue) : value() , valid(false) {}
Opt(const T& val) : value(val), valid(true ) {}
- //rvalue optimization: only basic exception safety:
- Opt(Opt&& tmp) : value(std::move(tmp.value)), valid(tmp.valid) {}
- Opt& operator=(const Opt& tmp) { value = std::move(tmp.value); valid = tmp.valid; }
+ Opt(const Opt& tmp) : value(tmp.valid ? tmp.value : T()), valid(tmp.valid) {}
+
+ Opt& operator=(const Opt& tmp)
+ {
+ if (tmp.valid)
+ value = tmp.value;
+ valid = tmp.valid;
+ }
+
+ ////rvalue optimization: only basic exception safety:
+ // Opt(Opt&& tmp) : value(std::move(tmp.value)), valid(tmp.valid) {}
#ifdef _MSC_VER
private:
@@ -58,6 +66,7 @@ public:
/**/ T* operator->() { return &value; }
void reset() { valid = false; }
+
private:
T value;
bool valid;
diff --git a/zen/read_txt.cpp b/zen/read_txt.cpp
index fd92a10c..0fd310c2 100644
--- a/zen/read_txt.cpp
+++ b/zen/read_txt.cpp
@@ -5,13 +5,13 @@ using namespace zen;
namespace
{
-std::string detectLineBreak(const Zstring& filename) //throw (FileError)
+std::string detectLineBreak(const Zstring& filename) //throw FileError
{
//read a (hopefully) significant portion of data
zen::FileInput input(filename);
std::vector<char> buffer(64 * 1024);
- size_t bytesRead = input.read(&buffer[0], buffer.size()); //throw (FileError);
+ size_t bytesRead = input.read(&buffer[0], buffer.size()); //throw FileError
buffer.resize(bytesRead);
//detect line break
@@ -41,15 +41,15 @@ std::string detectLineBreak(const Zstring& filename) //throw (FileError)
}
-ExtractLines::ExtractLines(const Zstring& filename, const std::string& lineBreak) : //throw (FileError)
+ExtractLines::ExtractLines(const Zstring& filename, const std::string& lineBreak) : //throw FileError
inputStream(filename), bufferLogBegin(buffer.begin()), lineBreak_(lineBreak)
{
if (lineBreak.empty())
- lineBreak_ = detectLineBreak(filename); //throw (FileError)
+ lineBreak_ = detectLineBreak(filename); //throw FileError
}
-bool ExtractLines::getLine(std::string& output) //throw (FileError)
+bool ExtractLines::getLine(std::string& output) //throw FileError
{
for (;;)
{
@@ -80,7 +80,7 @@ bool ExtractLines::getLine(std::string& output) //throw (FileError)
const size_t BLOCK_SIZE = 512 * 1024;
buffer.resize(buffer.size() + BLOCK_SIZE);
- size_t bytesRead = inputStream.read(&buffer[0] + buffer.size() - BLOCK_SIZE, BLOCK_SIZE); //throw (FileError);
+ size_t bytesRead = inputStream.read(&buffer[0] + buffer.size() - BLOCK_SIZE, BLOCK_SIZE); //throw FileError
assert(bytesRead <= BLOCK_SIZE); //promised by FileInput()
if (bytesRead < BLOCK_SIZE)
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 5e5e56d4..3de795a5 100644
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -71,7 +71,7 @@ Zstring getLockingProcessNames(const Zstring& filename) //throw(), empty string
}
-bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError
+bool zen::recycleOrDelete(const Zstring& filename) //throw FileError
{
if (!somethingExists(filename))
return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it!
@@ -165,12 +165,37 @@ bool zen::moveToRecycleBin(const Zstring& filename) //throw FileError
#ifdef FFS_WIN
-zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
+StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
{
- warn_static("fix XP not working + finish");
+ const DWORD bufferSize = MAX_PATH + 1;
+ std::vector<wchar_t> buffer(bufferSize);
+ if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
+ &buffer[0], //__out LPTSTR lpszVolumePathName,
+ bufferSize)) //__in DWORD cchBufferLength
+ return STATUS_REC_UNKNOWN;
+
+ const Zstring rootPathPf = appendSeparator(&buffer[0]);
+
+ SHQUERYRBINFO recInfo = {};
+ recInfo.cbSize = sizeof(recInfo);
+ HRESULT rv = ::SHQueryRecycleBin(rootPathPf.c_str(), //__in_opt LPCTSTR pszRootPath,
+ &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo
+
+ return rv == S_OK ? STATUS_REC_EXISTS : STATUS_REC_MISSING;
+ //1. excessive: traverses whole C:\$Recycle.Bin directory tree each time!!!! But it's safe and correct.
+
+ //2. we would prefer to use CLSID_RecycleBinManager beginning with Vista... if only this interface were documented!!!
+
+ //3. check directory existence of "C:\$Recycle.Bin, C:\RECYCLER, C:\RECYCLED"
+ // -> not upward-compatible, wrong result for subst-alias: recycler assumed existing, although it is not!
+
+ //4. alternative approach a'la Raymond Chen: http://blogs.msdn.com/b/oldnewthing/archive/2008/09/18/8956382.aspx
+ //caveat: might not be reliable, e.g. "subst"-alias of volume contains "$Recycle.Bin" although it is not available!
/*
- const bool canUseFastCheckForRecycler = winXpOrLater();
+ Zstring rootPathPf = appendSeparator(&buffer[0]);
+
+ const bool canUseFastCheckForRecycler = winXpOrLater();
if (!canUseFastCheckForRecycler) //== "checkForRecycleBin"
return STATUS_REC_UNKNOWN;
@@ -180,20 +205,7 @@ zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
if (!checkRecycler)
return STATUS_REC_UNKNOWN; //actually an error since we're >= XP
- const DWORD bufferSize = MAX_PATH + 1;
- std::vector<wchar_t> buffer(bufferSize);
- if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
- &buffer[0], //__out LPTSTR lpszVolumePathName,
- bufferSize)) //__in DWORD cchBufferLength
- return STATUS_REC_UNKNOWN;
-
- Zstring rootPathPf = appendSeparator(&buffer[0]);
-
//search root directories for recycle bin folder...
- //we would prefer to use CLSID_RecycleBinManager beginning with Vista... if this interface were documented
-
- //caveat: "subst"-alias of volume contains "$Recycle.Bin" although it is not available!!!
- warn_static("check")
WIN32_FIND_DATA dataRoot = {};
HANDLE hFindRoot = ::FindFirstFile(applyLongPathPrefix(rootPathPf + L'*').c_str(), &dataRoot);
@@ -207,8 +219,8 @@ zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
{
if (!shouldSkip(dataRoot.cFileName) &&
(dataRoot.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
- (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) != 0 && //maybe a little risky to rely on these attributes, there may be a recycler
- (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) != 0) //(in obscure cases) we don't find, but that's better than the other way round
+ (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) != 0 && //risky to rely on these attributes!!!
+ (dataRoot.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) != 0) //
{
WIN32_FIND_DATA dataChild = {};
const Zstring childDirPf = rootPathPf + dataRoot.cFileName + L"\\";
@@ -238,25 +250,7 @@ zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName)
while (::FindNextFile(hFindRoot, &dataRoot)); //
return STATUS_REC_MISSING;
-
*/
-
- const DWORD bufferSize = MAX_PATH + 1;
- std::vector<wchar_t> buffer(bufferSize);
- if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
- &buffer[0], //__out LPTSTR lpszVolumePathName,
- bufferSize)) //__in DWORD cchBufferLength
- return STATUS_REC_UNKNOWN;
-
- const Zstring rootPathPf = appendSeparator(&buffer[0]);
-
- SHQUERYRBINFO recInfo = {};
- recInfo.cbSize = sizeof(recInfo);
- HRESULT rv = ::SHQueryRecycleBin(rootPathPf.c_str(), //__in_opt LPCTSTR pszRootPath,
- &recInfo); //__inout LPSHQUERYRBINFO pSHQueryRBInfo
- //traverses whole C:\$Recycle.Bin directory each time!!!!
-
- return rv == S_OK ? STATUS_REC_EXISTS : STATUS_REC_MISSING;
}
#elif defined FFS_LINUX
diff --git a/zen/recycler.h b/zen/recycler.h
index 952deec2..1778eb2e 100644
--- a/zen/recycler.h
+++ b/zen/recycler.h
@@ -29,8 +29,8 @@ Linker flags: `pkg-config --libs gio-2.0`
Already included in package "gtk+-2.0"!
*/
-//move a file or folder to Recycle Bin (deletes permanently if recycle is not available)
-bool moveToRecycleBin(const Zstring& filename); //throw FileError, return "true" if file/dir was actually deleted
+//move a file or folder to Recycle Bin (deletes permanently if recycle is not available) -> crappy semantics, but we have no choice thanks to Windows' design
+bool recycleOrDelete(const Zstring& filename); //throw FileError, return "true" if file/dir was actually deleted
#ifdef FFS_WIN
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 78d99832..ace6ebaa 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -33,6 +33,9 @@ void set_remove_if(S& set, Predicate p);
template <class M, class Predicate>
void map_remove_if(M& map, Predicate p);
+template <class M, class K, class V>
+V& map_add_or_update(M& map, const K& key, const V& value); //efficient add or update without "default-constructible" requirement (Effective STL, item 24)
+
//binary search returning an iterator
template <class ForwardIterator, class T, typename Compare>
ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp);
@@ -98,6 +101,20 @@ template <class M, class Predicate> inline
void map_remove_if(M& map, Predicate p) { set_remove_if(map, p); }
+template <class M, class K, class V> inline
+V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or update without "default-constructible" requirement (Effective STL, item 24)
+{
+ auto iter = map.lower_bound(key);
+ if (iter != map.end() && !(map.key_comp()(key, iter->first)))
+ {
+ iter->second = value;
+ return iter->second;
+ }
+ else
+ return map.insert(iter, typename M::value_type(key, value))->second;
+}
+
+
template <class ForwardIterator, class T, typename Compare> inline
ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp)
{
diff --git a/zen/string_base.h b/zen/string_base.h
index 16731089..4c339d56 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -31,7 +31,8 @@ public:
//::operator new/ ::operator delete show same performance characterisics like malloc()/free()!
static void* allocate(size_t size) { return ::operator new(size); } //throw std::bad_alloc
static void deallocate(void* ptr) { ::operator delete(ptr); }
- static size_t calcCapacity(size_t length) { return std::max<size_t>(16, length + length / 2); } //any growth rate should not exceed golden ratio: 1.618033989
+ static size_t calcCapacity(size_t length) { return std::max<size_t>(std::max<size_t>(16, length), length + length / 2); } //size_t might overflow!
+ //any growth rate should not exceed golden ratio: 1.618033989
};
@@ -68,9 +69,9 @@ protected:
static Char* create(size_t size) { return create(size, size); }
static Char* create(size_t size, size_t minCapacity)
{
+ assert(size <= minCapacity);
const size_t newCapacity = AP::calcCapacity(minCapacity);
assert(newCapacity >= minCapacity);
- assert(minCapacity >= size);
Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char)));
@@ -121,9 +122,10 @@ protected:
static Char* create(size_t size) { return create(size, size); }
static Char* create(size_t size, size_t minCapacity)
{
+ assert(size <= minCapacity);
+
const size_t newCapacity = AP::calcCapacity(minCapacity);
assert(newCapacity >= minCapacity);
- assert(minCapacity >= size);
Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char)));
new (newDescr) Descriptor(1, size, newCapacity);
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 04ba1eef..00eb50ee 100644
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -90,7 +90,8 @@ bool isWhiteSpace(char ch)
std::isspace(static_cast<unsigned char>(ch)) != 0;
}
-template <> inline bool isWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; }
+template <> inline
+bool isWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; }
template <class Char> inline
@@ -458,7 +459,7 @@ S formatInteger(Num n, bool hasMinus)
assert(n >= 0);
typedef typename GetCharType<S>::Type CharType;
- const size_t bufferSize = 100; //sufficient for signed 256-bit numbers
+ const size_t bufferSize = 64; //sufficient for signed 128-bit numbers
CharType buffer[bufferSize]; //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency
assert_static(2 + 5 * sizeof(n) / 2 <= bufferSize);
//minimum required chars (+ sign char): 1 + ceil(ln_10 (256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.5)
@@ -492,7 +493,6 @@ S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>)
//--------------------------------------------------------------------------------
-
template <class Num, class S> inline
Num stringTo(const S& str, Int2Type<NUM_TYPE_OTHER>) //default string to number conversion using streams: convenient, but SLOW
{
diff --git a/zen/string_traits.h b/zen/string_traits.h
index cfcf78bb..eea9ae02 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -35,7 +35,7 @@ strLength():
strLength(array); //equals cStringLength(array)
*/
-//wrap a sub-string or a char* as an intermediate string class when the length is already known
+//reference a sub-string or a char* as an intermediate string class when the length is already known
template <class Char>
class StringProxy
{
@@ -126,8 +126,7 @@ public:
enum
{
- isStringLike =
- IsSameType<CharType, char >::value ||
+ isStringLike = IsSameType<CharType, char>::value ||
IsSameType<CharType, wchar_t>::value
};
};
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index dfd5ebfa..17ae1916 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -84,7 +84,7 @@ Zstring getSymlinkRawTargetString(const Zstring& linkPath) //throw FileError
const DWORD bufferSize = REPARSE_DATA_BUFFER_HEADER_SIZE + MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
std::vector<char> buffer(bufferSize);
- DWORD bytesReturned; //dummy value required by FSCTL_GET_REPARSE_POINT!
+ DWORD bytesReturned = 0; //dummy value required by FSCTL_GET_REPARSE_POINT!
if (!::DeviceIoControl(hLink, //__in HANDLE hDevice,
FSCTL_GET_REPARSE_POINT, //__in DWORD dwIoControlCode,
nullptr, //__in_opt LPVOID lpInBuffer,
diff --git a/zen/thread.h b/zen/thread.h
index 3fb73e70..1fda016d 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -11,15 +11,15 @@
#include <memory>
#include "fixed_list.h"
+//fix this pathetic boost thread warning mess
#ifdef __MINGW32__
#pragma GCC diagnostic push
-
#pragma GCC diagnostic ignored "-Wswitch-enum"
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
-//#pragma GCC diagnostic ignored "-Wno-attributes"
-//#pragma GCC diagnostic ignored "-Wredundant-decls"
-//#pragma GCC diagnostic ignored "-Wcast-align"
-//#pragma GCC diagnostic ignored "-Wunused-value"
+#pragma GCC diagnostic ignored "-Wshadow"
+#endif
+#ifdef _MSC_VER
+#pragma warning(disable : 4702) //unreachable code
#endif
#include <boost/thread.hpp>
@@ -27,6 +27,9 @@
#ifdef __MINGW32__
#pragma GCC diagnostic pop
#endif
+#ifdef _MSC_VER
+#pragma warning(default : 4702) //unreachable code
+#endif
namespace zen
{
@@ -89,7 +92,7 @@ auto async2(Function fun) -> boost::unique_future<T> //workaround VS2010 bug: bo
boost::packaged_task<T> pt([=] { return fun(); });
auto fut = pt.get_future();
boost::thread(std::move(pt));
- return std::move(fut);
+ return std::move(fut); //compiler error without "move", why needed???
}
bgstack15