summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rw-r--r--zen/async_task.h4
-rw-r--r--zen/dir_watcher.cpp3
-rw-r--r--zen/file_handling.cpp186
-rw-r--r--zen/file_handling.h8
-rw-r--r--zen/file_io.cpp13
-rw-r--r--zen/file_io.h9
-rw-r--r--zen/file_io_base.h4
-rw-r--r--zen/file_traverser.cpp257
-rw-r--r--zen/file_traverser.h15
-rw-r--r--zen/format_unit.cpp3
-rw-r--r--zen/int64.h1
-rw-r--r--zen/long_path_prefix.h4
-rw-r--r--zen/serialize.h6
-rw-r--r--zen/stl_tools.h13
-rw-r--r--zen/string_base.h17
-rw-r--r--zen/string_traits.h49
-rw-r--r--zen/symlink_target.h73
-rw-r--r--zen/thread.h3
-rw-r--r--zen/tick_count.h2
-rw-r--r--zen/time.h2
-rw-r--r--zen/utf.h199
-rw-r--r--zen/zstring.cpp6
22 files changed, 458 insertions, 419 deletions
diff --git a/zen/async_task.h b/zen/async_task.h
index b4123b5d..c5e5857a 100644
--- a/zen/async_task.h
+++ b/zen/async_task.h
@@ -11,6 +11,7 @@
#include <functional>
#include <zen/thread.h>
#include <zen/scope_guard.h>
+//#include "type_tools.h"
namespace zen
{
@@ -34,7 +35,7 @@ public:
}
template <class Fun, class Fun2>
- void add2(Fun doAsync, Fun2 evalOnGui) //for doAsync returning void
+ void add2(Fun doAsync, Fun2 evalOnGui) //for evalOnGui taking no parameters
{
tasks.push_back(zen::async([=]() -> std::function<void()> { doAsync(); return [=]{ evalOnGui(); }; }));
}
@@ -64,7 +65,6 @@ private:
bool inRecursion;
std::list<boost::unique_future<std::function<void()>>> tasks;
};
-
}
#endif //ASYNC_JOB_839147839170432143214321
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 6c6c0929..6402dce7 100644
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -386,7 +386,8 @@ public:
dirs_.push_back(fullName);
return otherMe_;
}
- virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); }
+ virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); }
+ virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); }
private:
const std::shared_ptr<TraverseCallback>& otherMe_; //lifetime management, two options: 1. use std::weak_ptr 2. ref to shared_ptr
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index b529720f..8dc3e72d 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -50,11 +50,11 @@ bool zen::fileExists(const Zstring& filename)
{
//symbolic links (broken or not) are also treated as existing files!
#ifdef FFS_WIN
- const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
- return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
+ return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also
#elif defined FFS_LINUX || defined FFS_MAC
- struct stat fileInfo = {};
+ 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
#endif
@@ -65,11 +65,11 @@ bool zen::dirExists(const Zstring& dirname)
{
//symbolic links (broken or not) are also treated as existing directories!
#ifdef FFS_WIN
- const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str());
- return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirname).c_str());
+ return attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also
#elif defined FFS_LINUX || defined FFS_MAC
- struct stat dirInfo = {};
+ 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
#endif
@@ -89,7 +89,7 @@ bool zen::symlinkExists(const Zstring& linkname)
return isSymlink(fileInfo);
#elif defined FFS_LINUX || defined FFS_MAC
- struct stat fileInfo = {};
+ struct ::stat fileInfo = {};
return ::lstat(linkname.c_str(), &fileInfo) == 0 &&
S_ISLNK(fileInfo.st_mode); //symbolic link
#endif
@@ -99,16 +99,35 @@ bool zen::symlinkExists(const Zstring& linkname)
bool zen::somethingExists(const Zstring& objname)
{
#ifdef FFS_WIN
- const DWORD rv = ::GetFileAttributes(applyLongPathPrefix(objname).c_str());
- return rv != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys"
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str());
+ return attr != INVALID_FILE_ATTRIBUTES || ::GetLastError() == ERROR_SHARING_VIOLATION; //"C:\pagefile.sys"
#elif defined FFS_LINUX || defined FFS_MAC
- struct stat fileInfo = {};
+ struct ::stat fileInfo = {};
return ::lstat(objname.c_str(), &fileInfo) == 0;
#endif
}
+SymLinkType zen::getSymlinkType(const Zstring& linkname) //throw()
+{
+ assert(symlinkExists(linkname));
+#ifdef FFS_WIN
+ const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(linkname).c_str());
+ if (attr == INVALID_FILE_ATTRIBUTES)
+ return SYMLINK_TYPE_UNKNOWN;
+ return (attr & FILE_ATTRIBUTE_DIRECTORY) ? SYMLINK_TYPE_DIR : SYMLINK_TYPE_FILE;
+
+#elif defined FFS_LINUX || defined FFS_MAC
+ //S_ISDIR and S_ISLNK are mutually exclusive on Linux => explicitly need to follow link
+ struct ::stat fileInfo = {};
+ if (::stat(linkname.c_str(), &fileInfo) != 0)
+ return SYMLINK_TYPE_UNKNOWN;
+ return S_ISDIR(fileInfo.st_mode) ? SYMLINK_TYPE_DIR : SYMLINK_TYPE_FILE;
+#endif
+}
+
+
namespace
{
void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl) //throw FileError
@@ -155,7 +174,6 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl
nullptr);
if (hFile == INVALID_HANDLE_VALUE)
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
-
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
BY_HANDLE_FILE_INFORMATION fileInfoHnd = {};
@@ -167,7 +185,7 @@ void getFileAttrib(const Zstring& filename, FileAttrib& attr, ProcSymlink procSl
}
#elif defined FFS_LINUX || defined FFS_MAC
- struct stat fileInfo = {};
+ struct ::stat fileInfo = {};
const int rv = procSl == SYMLINK_FOLLOW ?
:: stat(filename.c_str(), &fileInfo) :
@@ -575,34 +593,42 @@ class CollectFilesFlat : public zen::TraverseCallback
{
public:
CollectFilesFlat(std::vector<Zstring>& files, std::vector<Zstring>& dirs) :
- m_files(files),
- m_dirs(dirs) {}
+ files_(files),
+ dirs_(dirs) {}
virtual void onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details)
{
- m_files.push_back(fullName);
+ files_.push_back(fullName);
}
virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details)
{
- if (details.dirLink)
- m_dirs.push_back(fullName);
- else
- m_files.push_back(fullName);
+ switch (getSymlinkType(fullName))
+ {
+ case SYMLINK_TYPE_DIR:
+ dirs_.push_back(shortName);
+ break;
+
+ case SYMLINK_TYPE_FILE:
+ case SYMLINK_TYPE_UNKNOWN:
+ files_.push_back(shortName);
+ break;
+ }
return LINK_SKIP;
}
virtual std::shared_ptr<TraverseCallback> onDir(const Zchar* shortName, const Zstring& fullName)
{
- m_dirs.push_back(fullName);
+ dirs_.push_back(fullName);
return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively!
}
- virtual HandleError onError(const std::wstring& msg) { throw FileError(msg); }
+ virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); }
+ virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); }
private:
CollectFilesFlat(const CollectFilesFlat&);
CollectFilesFlat& operator=(const CollectFilesFlat&);
- std::vector<Zstring>& m_files;
- std::vector<Zstring>& m_dirs;
+ std::vector<Zstring>& files_;
+ std::vector<Zstring>& dirs_;
};
@@ -989,49 +1015,6 @@ bool zen::supportsPermissions(const Zstring& dirname) //throw FileError
namespace
{
-#ifdef FFS_WIN
-Zstring getSymlinkTargetPath(const Zstring& symlink) //throw FileError
-{
- //open handle to target of symbolic link
- const HANDLE hDir = ::CreateFile(applyLongPathPrefix(symlink).c_str(),
- 0,
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory
- nullptr);
- if (hDir == INVALID_HANDLE_VALUE)
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(symlink)) + L"\n\n" + getLastErrorFormatted());
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
-
- //dynamically load windows API function
- typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile,
- LPTSTR lpszFilePath,
- DWORD cchFilePath,
- DWORD dwFlags);
- const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW");
- if (!getFinalPathNameByHandle)
- throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
-
- const DWORD BUFFER_SIZE = 10000;
- std::vector<wchar_t> targetPath(BUFFER_SIZE);
- const DWORD charsWritten = getFinalPathNameByHandle(hDir, //__in HANDLE hFile,
- &targetPath[0], //__out LPTSTR lpszFilePath,
- BUFFER_SIZE, //__in DWORD cchFilePath,
- FILE_NAME_NORMALIZED); //__in DWORD dwFlags
- if (charsWritten >= BUFFER_SIZE || charsWritten == 0)
- {
- std::wstring errorMessage = replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(symlink));
- if (charsWritten == 0)
- errorMessage += L"\n\n" + getLastErrorFormatted();
- throw FileError(errorMessage);
- }
-
- return Zstring(&targetPath[0], charsWritten);
-}
-#endif
-
-
#ifdef HAVE_SELINUX
//copy SELinux security context
void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError
@@ -1086,9 +1069,9 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSym
//in contrast to ::SetSecurityInfo(), ::SetFileSecurity() seems to honor the "inherit DACL/SACL" flags
//CAVEAT: if a file system does not support ACLs, GetFileSecurity() will return successfully with a *valid* security descriptor containing *no* ACL entries!
- //NOTE: ::GetFileSecurity()/::SetFileSecurity() do NOT follow Symlinks!
- const Zstring sourceResolved = procSl == SYMLINK_FOLLOW && symlinkExists(source) ? getSymlinkTargetPath(source) : source;
- const Zstring targetResolved = procSl == SYMLINK_FOLLOW && symlinkExists(target) ? getSymlinkTargetPath(target) : target;
+ //NOTE: ::GetFileSecurity()/::SetFileSecurity() do NOT follow Symlinks! getResolvedFilePath() requires Vista or later!
+ const Zstring sourceResolved = procSl == SYMLINK_FOLLOW && symlinkExists(source) ? getResolvedFilePath(source) : source; //throw FileError
+ const Zstring targetResolved = procSl == SYMLINK_FOLLOW && symlinkExists(target) ? getResolvedFilePath(target) : target; //
//setting privileges requires admin rights!
try
@@ -1400,50 +1383,46 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
if (!templateDir.empty())
{
#ifdef FFS_WIN
- //try to copy file attributes
- Zstring sourcePath;
-
- if (symlinkExists(templateDir))
- try
- {
- //get target directory of symbolic link
- sourcePath = getSymlinkTargetPath(templateDir); //throw FileError
- }
- catch (FileError&) {} //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error...
- else
- sourcePath = templateDir;
-
- //*try* to copy file attributes
- if (!sourcePath.empty())
+ //try to copy file attributes (dereference symlinks and junctions)
+ const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS /*needed to open a directory*/ /*| FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks!*/ ,
+ nullptr);
+ if (hDirSrc != INVALID_HANDLE_VALUE) //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error...
{
- const DWORD sourceAttr = ::GetFileAttributes(applyLongPathPrefix(sourcePath).c_str());
- if (sourceAttr != INVALID_FILE_ATTRIBUTES)
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirSrc));
+
+ BY_HANDLE_FILE_INFORMATION dirInfo = {};
+ if (::GetFileInformationByHandle(hDirSrc, &dirInfo))
{
- ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), sourceAttr);
+ ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), dirInfo.dwFileAttributes);
//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;
+ const bool isCompressed = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0;
+ const bool isEncrypted = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0;
if (isEncrypted)
::EncryptFile(directory.c_str()); //seems no long path is required (check passed!)
if (isCompressed)
{
- HANDLE hDir = ::CreateFile(applyLongPathPrefix(directory).c_str(),
- GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION
- FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
- nullptr,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- nullptr);
- if (hDir != INVALID_HANDLE_VALUE)
+ HANDLE hDirTrg = ::CreateFile(applyLongPathPrefix(directory).c_str(),
+ GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ nullptr);
+ if (hDirTrg != INVALID_HANDLE_VALUE)
{
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirTrg));
USHORT cmpState = COMPRESSION_FORMAT_DEFAULT;
DWORD bytesReturned = 0;
- ::DeviceIoControl(hDir, //handle to file or directory
+ ::DeviceIoControl(hDirTrg, //handle to file or directory
FSCTL_SET_COMPRESSION, //dwIoControlCode
&cmpState, //input buffer
sizeof(cmpState), //size of input buffer
@@ -1470,19 +1449,19 @@ void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorT
void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions) //throw FileError
{
- const Zstring linkPath = getSymlinkRawTargetString(sourceLink); //accept broken symlinks; throw FileError
+ const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks
#ifdef FFS_WIN
const bool isDirLink = [&]() -> bool
{
const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(sourceLink).c_str());
- return ret != INVALID_FILE_ATTRIBUTES && (ret& FILE_ATTRIBUTE_DIRECTORY);
+ return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY);
}();
//dynamically load windows API function
typedef BOOLEAN (WINAPI* CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags);
-
const SysDllFun<CreateSymbolicLinkFunc> createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW");
+
if (!createSymbolicLink)
throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\""));
@@ -1492,8 +1471,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool
#elif defined FFS_LINUX || defined FFS_MAC
if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0)
#endif
- throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)) +
- L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)) + L"\n\n" + getLastErrorFormatted());
//allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist
zen::ScopeGuard guardNewDir = zen::makeGuard([&]
diff --git a/zen/file_handling.h b/zen/file_handling.h
index a0bd9b5b..c18a68ac 100644
--- a/zen/file_handling.h
+++ b/zen/file_handling.h
@@ -33,6 +33,14 @@ enum ResponseSame
};
ResponseSame onSameVolume(const Zstring& folderLeft, const Zstring& folderRight); //throw()
+enum SymLinkType
+{
+ SYMLINK_TYPE_DIR, //Windows: may be broken
+ SYMLINK_TYPE_FILE, //Windows: may be broken
+ SYMLINK_TYPE_UNKNOWN, //Windows: unable to determine type; Linux: broken Symlink
+};
+SymLinkType getSymlinkType(const Zstring& linkname); //throw()
+
enum ProcSymlink
{
SYMLINK_DIRECT,
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 4961cac9..cda48e36 100644
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -168,7 +168,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number
const size_t bytesRead = ::fread(buffer, 1, bytesToRead, fileHandle);
if (::ferror(fileHandle) != 0) //checks status of stream, not fread()!
#endif
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (read)");
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (ReadFile)");
#ifdef FFS_WIN
if (bytesRead < bytesToRead) //verify only!
@@ -227,7 +227,7 @@ FileOutput::FileOutput(const Zstring& filename, AccessFlag access) : //throw Fil
const DWORD attrib = ::GetFileAttributes(applyLongPathPrefix(filename).c_str());
if (attrib != INVALID_FILE_ATTRIBUTES)
{
- fileHandle = getHandle(attrib); //retry
+ fileHandle = getHandle(attrib); //retry: alas this may still fail for hidden file, e.g. accessing shared folder in XP as Virtual Box guest!
lastError = ::GetLastError();
}
}
@@ -301,7 +301,7 @@ void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileErro
const size_t bytesWritten = ::fwrite(buffer, 1, bytesToWrite, fileHandle);
if (::ferror(fileHandle) != 0) //checks status of stream, not fwrite()!
#endif
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (w)"); //w -> distinguish from fopen error message!
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (WriteFile)");
if (bytesWritten != bytesToWrite) //must be fulfilled for synchronous writes!
throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"Incomplete write!");
@@ -316,7 +316,7 @@ FileInputUnbuffered::FileInputUnbuffered(const Zstring& filename) : FileInputBas
checkForUnsupportedType(filename); //throw FileError; reading a named pipe would block forever!
fdFile = ::open(filename.c_str(), O_RDONLY);
- if (fdFile < 0)
+ if (fdFile == -1)
{
const ErrorCode lastError = getLastError();
@@ -361,7 +361,7 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filename, mode_t mode)
//overwrite is: O_CREAT | O_WRONLY | O_TRUNC
fdFile = ::open(filename.c_str(), O_CREAT | O_WRONLY | O_EXCL, mode & (S_IRWXU | S_IRWXG | S_IRWXO));
- if (fdFile < 0)
+ if (fdFile == -1)
{
const int lastError = errno;
const std::wstring errorMessage = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(filename)) + L"\n\n" + zen::getLastErrorFormatted(lastError);
@@ -375,6 +375,7 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filename, mode_t mode)
}
}
+FileOutputUnbuffered::FileOutputUnbuffered(int fd, const Zstring& filename) : FileOutputBase(filename), fdFile(fd) {}
FileOutputUnbuffered::~FileOutputUnbuffered() { ::close(fdFile); }
@@ -395,7 +396,7 @@ void FileOutputUnbuffered::write(const void* buffer, size_t bytesToWrite) //thro
if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers
errno = ENOSPC;
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (w)");
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + getLastErrorFormatted() + L" (write)");
}
if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry
throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(getFilename())) + L"\n\n" + L"buffer overflow");
diff --git a/zen/file_io.h b/zen/file_io.h
index 8e501172..407109f7 100644
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef FILEIO_H_INCLUDED
-#define FILEIO_H_INCLUDED
+#ifndef FILEIO_89578342758342572345
+#define FILEIO_89578342758342572345
#include "file_io_base.h"
#include "file_error.h"
@@ -73,7 +73,7 @@ public:
//considering safe-read.c it seems buffer size should be a multiple of 8192
virtual size_t read(void* buffer, size_t bytesToRead); //throw FileError; returns actual number of bytes read
- //we should not rely on buffer being filled completely!
+ //do NOT rely on partially filled buffer meaning EOF!
int getDescriptor() { return fdFile;}
@@ -86,6 +86,7 @@ class FileOutputUnbuffered : public FileOutputBase
public:
//creates a new file (no overwrite allowed!)
FileOutputUnbuffered(const Zstring& filename, mode_t mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
+ FileOutputUnbuffered(int fd, const Zstring& filename); //takes ownership!
~FileOutputUnbuffered();
virtual void write(const void* buffer, size_t bytesToWrite); //throw FileError
@@ -97,4 +98,4 @@ private:
#endif
}
-#endif // FILEIO_H_INCLUDED
+#endif //FILEIO_89578342758342572345
diff --git a/zen/file_io_base.h b/zen/file_io_base.h
index f26cd8c2..8e9bf12e 100644
--- a/zen/file_io_base.h
+++ b/zen/file_io_base.h
@@ -21,8 +21,8 @@ protected:
~FileBase() {}
private:
- FileBase(const FileBase&);
- FileBase& operator=(const FileBase&);
+ FileBase(const FileBase&); //=delete
+ FileBase& operator=(const FileBase&); //
const Zstring filename_;
};
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 84bd6c64..53049c21 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -34,7 +34,7 @@ namespace
//implement "retry" in a generic way:
template <class Command> inline //function object expecting to throw FileError if operation fails
-bool tryReportingError(Command cmd, zen::TraverseCallback& callback) //return "true" on success, "false" if error was ignored
+bool tryReportingDirError(Command cmd, zen::TraverseCallback& callback) //return "true" on success, "false" if error was ignored
{
for (;;)
try
@@ -44,23 +44,40 @@ bool tryReportingError(Command cmd, zen::TraverseCallback& callback) //return "t
}
catch (const FileError& e)
{
- switch (callback.onError(e.toString()))
+ switch (callback.reportDirError(e.toString()))
+ {
+ case TraverseCallback::ON_ERROR_RETRY:
+ break;
+ case TraverseCallback::ON_ERROR_IGNORE:
+ return false;
+ }
+ }
+}
+
+template <class Command> inline //function object expecting to throw FileError if operation fails
+bool tryReportingItemError(Command cmd, zen::TraverseCallback& callback, const Zchar* shortName) //return "true" on success, "false" if error was ignored
+{
+ for (;;)
+ try
+ {
+ cmd(); //throw FileError
+ return true;
+ }
+ catch (const FileError& e)
+ {
+ switch (callback.reportItemError(e.toString(), shortName))
{
case TraverseCallback::ON_ERROR_RETRY:
break;
case TraverseCallback::ON_ERROR_IGNORE:
return false;
- //default:
- // assert(false);
- //break;
}
}
}
#ifdef FFS_WIN
-inline
-bool getTargetInfoFromSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output)
+void getInfoFromFileSymlink(const Zstring& linkName, zen::TraverseCallback::FileInfo& output) //throw FileError
{
//open handle to target of symbolic link
HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(linkName).c_str(),
@@ -68,21 +85,27 @@ bool getTargetInfoFromSymlink(const Zstring& linkName, zen::TraverseCallback::Fi
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory
+ FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory -> keep it even if we expect to open a file! See comment below
nullptr);
if (hFile == INVALID_HANDLE_VALUE)
- return false;
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted() + L" (CreateFile)");
ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile));
- BY_HANDLE_FILE_INFORMATION fileInfoByHandle = {};
- if (!::GetFileInformationByHandle(hFile, &fileInfoByHandle))
- return false;
+ BY_HANDLE_FILE_INFORMATION fileInfo = {};
+ if (!::GetFileInformationByHandle(hFile, &fileInfo))
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted() + L" (GetFileInformationByHandle)");
+
+ //a file symlink may incorrectly point to a directory, but both CreateFile() and GetFileInformationByHandle() will succeed and return garbage!
+ //- if we did not use FILE_FLAG_BACKUP_SEMANTICS above, CreateFile() would error out with an even less helpful ERROR_ACCESS_DENIED!
+ //- reinterpreting the link as a directory symlink would still fail during traversal, so just show an error here
+ //- OTOH a directory symlink that points to a file fails immediately in ::FindFirstFile() with ERROR_DIRECTORY! -> nothing to do in this case
+ if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkName)) + L"\n\n" + getLastErrorFormatted(ERROR_FILE_INVALID) + L" (GetFileInformationByHandle)");
//write output
- output.fileSize = UInt64(fileInfoByHandle.nFileSizeLow, fileInfoByHandle.nFileSizeHigh);
- output.lastWriteTimeRaw = toTimeT(fileInfoByHandle.ftLastWriteTime);
- output.id = FileId(); //= extractFileID(fileInfoByHandle); -> id from dereferenced symlink is problematic, since renaming will consider the link, not the target!
- return true;
+ output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
+ output.lastWriteTime = toTimeT(fileInfo.ftLastWriteTime);
+ output.id = extractFileID(fileInfo); //consider detection of moved files: allow for duplicate file ids, renaming affects symlink, not target, ...
}
@@ -92,8 +115,7 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if s
//- root paths "C:\", "D:\"
//- network shares: \\share\dirname
//- indirection: subst S: %USERPROFILE%
- // -> GetVolumePathName() on the other hand resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try...
-
+ // -> GetVolumePathName()+GetVolumeInformation() OTOH incorrectly resolves "S:\Desktop\somedir" to "S:\Desktop\" - nice try...
const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(pathName).c_str(),
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
@@ -113,43 +135,12 @@ DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error or if s
}
-/*
-DWORD retrieveVolumeSerial(const Zstring& pathName) //returns 0 on error!
-{
- //note: this works for network shares: \\share\dirname, but not "subst"!
-
- const DWORD BUFFER_SIZE = 10000;
- std::vector<wchar_t> buffer(BUFFER_SIZE);
-
- //full pathName need not yet exist!
- if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName,
- &buffer[0], //__out LPTSTR lpszVolumePathName,
- BUFFER_SIZE)) //__in DWORD cchBufferLength
- return 0;
-
- Zstring volumePath = appendSeparator(&buffer[0]);
-
- DWORD volumeSerial = 0;
- if (!::GetVolumeInformation(volumePath.c_str(), //__in_opt LPCTSTR lpRootPathName,
- nullptr, //__out LPTSTR lpVolumeNameBuffer,
- 0, //__in DWORD nVolumeNameSize,
- &volumeSerial, //__out_opt LPDWORD lpVolumeSerialNumber,
- nullptr, //__out_opt LPDWORD lpMaximumComponentLength,
- nullptr, //__out_opt LPDWORD lpFileSystemFlags,
- nullptr, //__out LPTSTR lpFileSystemNameBuffer,
- 0)) //__in DWORD nFileSystemNameSize
- return 0;
-
- return volumeSerial;
-}
-*/
-
-
const bool isXpOrLater = winXpOrLater(); //VS2010 compiled DLLs are not supported on Win 2000: Popup dialog "DecodePointer not found"
-const auto openDir = isXpOrLater ? DllFun<findplus::FunType_openDir >(findplus::getDllName(), findplus::funName_openDir ) : DllFun<findplus::FunType_openDir >(); //
-const auto readDir = isXpOrLater ? DllFun<findplus::FunType_readDir >(findplus::getDllName(), findplus::funName_readDir ) : DllFun<findplus::FunType_readDir >(); //load at startup: avoid pre C++11 static initialization MT issues
-const auto closeDir= isXpOrLater ? DllFun<findplus::FunType_closeDir>(findplus::getDllName(), findplus::funName_closeDir) : DllFun<findplus::FunType_closeDir>(); //
+#define DEF_DLL_FUN(name) const auto name = isXpOrLater ? DllFun<findplus::FunType_##name>(findplus::getDllName(), findplus::funName_##name) : DllFun<findplus::FunType_##name>();
+DEF_DLL_FUN(openDir); //
+DEF_DLL_FUN(readDir); //load at startup: avoid pre C++11 static initialization MT issues
+DEF_DLL_FUN(closeDir); //
/*
Common C-style interface for Win32 FindFirstFile(), FindNextFile() and FileFilePlus openDir(), closeDir():
@@ -192,11 +183,11 @@ struct Win32Traverser
typedef WIN32_FIND_DATA FindData;
- static void create(const Zstring& directory, DirHandle& hnd) //throw FileError
+ static void create(const Zstring& dirname, DirHandle& hnd) //throw FileError
{
- const Zstring& directoryPf = appendSeparator(directory);
+ const Zstring& dirnamePf = appendSeparator(dirname);
- hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.data);
+ hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(dirnamePf + 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)
{
@@ -205,17 +196,17 @@ struct Win32Traverser
{
//1. directory may not exist *or* 2. it is completely empty: not all directories contain "., .." entries, e.g. a drive's root directory; NetDrive
// -> FindFirstFile() is a nice example of violation of API design principle of single responsibility
- if (dirExists(directory)) //yes, a race-condition, still the best we can do
+ if (dirExists(dirname)) //yes, a race-condition, still the best we can do
return;
}
- throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
}
}
static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw()
template <class FallbackFun>
- static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo, FallbackFun) //throw FileError
+ static bool getEntry(DirHandle& hnd, const Zstring& dirname, FindData& fileInfo, FallbackFun) //throw FileError
{
if (hnd.searchHandle == INVALID_HANDLE_VALUE) //handle special case of "truly empty directories"
return false;
@@ -232,15 +223,15 @@ struct Win32Traverser
if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
return false;
//else we have a problem... report it:
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
}
return true;
}
static void extractFileInfo(const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output)
{
- output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
- output.lastWriteTimeRaw = getModTime(fileInfo);
+ output.fileSize = UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh);
+ output.lastWriteTime = getModTime(fileInfo);
output.id = FileId();
}
@@ -264,17 +255,17 @@ struct FilePlusTraverser
typedef findplus::FileInformation FindData;
- static void create(const Zstring& directory, DirHandle& hnd) //throw FileError
+ static void create(const Zstring& dirname, DirHandle& hnd) //throw FileError
{
- hnd.searchHandle = ::openDir(applyLongPathPrefix(directory).c_str());
+ hnd.searchHandle = ::openDir(applyLongPathPrefix(dirname).c_str());
if (hnd.searchHandle == nullptr)
- throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted() + L" (+)");
+ throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted() + L" (+)");
}
static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw()
template <class FallbackFun>
- static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo, FallbackFun fb) //throw FileError
+ static bool getEntry(DirHandle hnd, const Zstring& dirname, FindData& fileInfo, FallbackFun fb) //throw FileError
{
if (!::readDir(hnd.searchHandle, fileInfo))
{
@@ -293,7 +284,7 @@ struct FilePlusTraverser
}
//else we have a problem... report it:
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (+)");
+ throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted(lastError) + L" (+)");
}
return true;
@@ -301,9 +292,9 @@ struct FilePlusTraverser
static void extractFileInfo(const FindData& fileInfo, DWORD volumeSerial, TraverseCallback::FileInfo& output)
{
- output.fileSize = fileInfo.fileSize.QuadPart;
- output.lastWriteTimeRaw = getModTime(fileInfo);
- output.id = extractFileID(volumeSerial, fileInfo.fileId);
+ output.fileSize = fileInfo.fileSize.QuadPart;
+ output.lastWriteTime = getModTime(fileInfo);
+ output.id = extractFileID(volumeSerial, fileInfo.fileId);
}
static Int64 getModTime (const FindData& fileInfo) { return toTimeT(fileInfo.lastWriteTime); }
@@ -319,9 +310,10 @@ class DirTraverser
{
public:
DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) :
- isFatFileSystem(dst::isFatDrive(baseDirectory)),
- volumeSerial(retrieveVolumeSerial(baseDirectory)) //return 0 on error
+ isFatFileSystem(dst::isFatDrive(baseDirectory))
{
+ warn_static("ineffizient, wenn kein dst hack/file ids gebraucht werden???")
+
try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail)
{
activatePrivilege(SE_BACKUP_NAME); //throw FileError
@@ -329,7 +321,7 @@ public:
catch (FileError&) {} //don't cause issues in user mode
if (::openDir && ::readDir && ::closeDir)
- traverse<FilePlusTraverser>(baseDirectory, sink, 0);
+ traverse<FilePlusTraverser>(baseDirectory, sink, retrieveVolumeSerial(baseDirectory)); //retrieveVolumeSerial returns 0 on error
else //fallback
traverse<Win32Traverser>(baseDirectory, sink, 0);
@@ -343,32 +335,28 @@ private:
DirTraverser& operator=(const DirTraverser&);
template <class Trav>
- void traverse(const Zstring& directory, zen::TraverseCallback& sink, int level)
+ void traverse(const Zstring& dirname, zen::TraverseCallback& sink, DWORD volumeSerial /*may be 0!*/)
{
- tryReportingError([&]
- {
- if (level == 100) //notify endless recursion
- throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + _("Detected endless directory recursion."));
- }, sink);
+ //no need to check for endless recursion: Windows seems to have an internal path limit of about 700 chars
typename Trav::DirHandle searchHandle;
- if (!tryReportingError([&]
+ if (!tryReportingDirError([&]
{
typedef Trav Trav; //f u VS!
- Trav::create(directory, searchHandle); //throw FileError
+ Trav::create(dirname, searchHandle); //throw FileError
}, sink))
return; //ignored error
ZEN_ON_SCOPE_EXIT(typedef Trav Trav; Trav::destroy(searchHandle));
typename Trav::FindData findData = {};
- auto fallback = [&] { this->traverse<Win32Traverser>(directory, sink, level); }; //help VS2010 a little by avoiding too deeply nested lambdas
+ auto fallback = [&] { this->traverse<Win32Traverser>(dirname, sink, volumeSerial); }; //help VS2010 a little by avoiding too deeply nested lambdas
for (;;)
{
bool gotEntry = false;
- tryReportingError([&] { typedef Trav Trav; /*VS 2010 bug*/ gotEntry = Trav::getEntry(searchHandle, directory, findData, fallback); }, sink); //throw FileError
+ tryReportingDirError([&] { typedef Trav Trav; /*VS 2010 bug*/ gotEntry = Trav::getEntry(searchHandle, dirname, findData, fallback); }, sink); //throw FileError
if (!gotEntry) //no more items or ignored error
return;
@@ -378,18 +366,12 @@ private:
(shortName[1] == 0 || (shortName[1] == L'.' && shortName[2] == 0)))
continue;
- const Zstring& fullName = appendSeparator(directory) + shortName;
+ const Zstring& fullName = appendSeparator(dirname) + shortName;
if (Trav::isSymlink(findData)) //check first!
{
TraverseCallback::SymlinkInfo linkInfo;
- try
- {
- linkInfo.targetPath = getSymlinkRawTargetString(fullName); //throw FileError
- }
- catch (FileError&) { assert(false); }
- linkInfo.lastWriteTimeRaw = Trav::getModTime (findData);
- linkInfo.dirLink = Trav::isDirectory(findData); //directory symlinks have this flag on Windows
+ linkInfo.lastWriteTime = Trav::getModTime (findData);
switch (sink.onSymlink(shortName, fullName, linkInfo))
{
@@ -397,21 +379,21 @@ private:
if (Trav::isDirectory(findData))
{
if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
- traverse<Trav>(fullName, *rv, level + 1);
+ traverse<Trav>(fullName, *rv, retrieveVolumeSerial(fullName)); //symlink may link to different volume => redetermine volume serial!
}
else //a file
{
TraverseCallback::FileInfo targetInfo;
- const bool validLink = tryReportingError([&] //try to resolve symlink (and report error on failure!!!)
+ const bool validLink = tryReportingItemError([&] //try to resolve symlink (and report error on failure!!!)
{
- if (!getTargetInfoFromSymlink(fullName, targetInfo))
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted());
- }, sink);
+ getInfoFromFileSymlink(fullName, targetInfo); //throw FileError
+ }, sink, shortName);
if (validLink)
sink.onFile(shortName, fullName, targetInfo);
- else //broken symlink
- sink.onFile(shortName, fullName, TraverseCallback::FileInfo());
+ // else //broken symlink
+ // sink.onFile(shortName, fullName, TraverseCallback::FileInfo());
+ warn_static("impact of ignored broken file/incomplete dir read on two-way variant!?")
}
break;
@@ -422,7 +404,7 @@ private:
else if (Trav::isDirectory(findData))
{
if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
- traverse<Trav>(fullName, *rv, level + 1);
+ traverse<Trav>(fullName, *rv, volumeSerial);
}
else //a file
{
@@ -435,7 +417,7 @@ private:
const dst::RawTime rawTime(Trav::getCreateTimeRaw(findData), Trav::getModTimeRaw(findData));
if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error
- fileInfo.lastWriteTimeRaw = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error)
+ fileInfo.lastWriteTime = toTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw (std::runtime_error)
else
markForDstHack.push_back(std::make_pair(fullName, toTimeT(rawTime.writeTimeRaw)));
}
@@ -498,8 +480,6 @@ private:
typedef std::vector<std::pair<Zstring, Int64> > FilenameTimeList;
FilenameTimeList markForDstHack;
//####################################### DST hack ###########################################
-
- const DWORD volumeSerial; //may be 0!
};
#elif defined FFS_LINUX || defined FFS_MAC
@@ -521,28 +501,23 @@ public:
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);
+ traverse(directoryFormatted, sink);
}
private:
DirTraverser(const DirTraverser&);
DirTraverser& operator=(const DirTraverser&);
- void traverse(const Zstring& directory, zen::TraverseCallback& sink, int level)
+ void traverse(const Zstring& dirname, zen::TraverseCallback& sink)
{
- tryReportingError([&]
- {
- if (level == 100) //notify endless recursion
- throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + _("Detected endless directory recursion."));
- }, sink);
-
+ //no need to check for endless recursion: Linux has a fixed limit on the number of symbolic links in a path
DIR* dirObj = nullptr;
- if (!tryReportingError([&]
+ if (!tryReportingDirError([&]
{
- dirObj = ::opendir(directory.c_str()); //directory must NOT end with path separator, except "/"
+ dirObj = ::opendir(dirname.c_str()); //directory must NOT end with path separator, except "/"
if (!dirObj)
- throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
}, sink))
return; //ignored error
ZEN_ON_SCOPE_EXIT(::closedir(dirObj)); //never close nullptr handles! -> crash
@@ -550,10 +525,10 @@ private:
for (;;)
{
struct ::dirent* dirEntry = nullptr;
- tryReportingError([&]
+ tryReportingDirError([&]
{
if (::readdir_r(dirObj, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
- throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(directory)) + L"\n\n" + getLastErrorFormatted());
+ throw FileError(replaceCpy(_("Cannot enumerate directory %x."), L"%x", fmtFileName(dirname)) + L"\n\n" + getLastErrorFormatted());
}, sink);
if (!dirEntry) //no more items or ignored error
return;
@@ -583,61 +558,53 @@ private:
//const char* sampleDecomposed = "\x6f\xcc\x81.txt";
//const char* samplePrecomposed = "\xc3\xb3.txt";
#endif
- const Zstring& fullName = appendSeparator(directory) + shortName;
+ const Zstring& fullName = appendSeparator(dirname) + shortName;
struct ::stat statData = {};
- if (!tryReportingError([&]
+ if (!tryReportingItemError([&]
{
if (::lstat(fullName.c_str(), &statData) != 0) //lstat() does not resolve symlinks
throw FileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted());
- }, sink))
+ }, sink, shortName))
continue; //ignore error: skip file
if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks!
{
- struct ::stat statDataTrg = {};
- bool validLink = ::stat(fullName.c_str(), &statDataTrg) == 0; //if "LINK_SKIP", a broken link is no error!
-
TraverseCallback::SymlinkInfo linkInfo;
- try
- {
- linkInfo.targetPath = getSymlinkRawTargetString(fullName); //throw FileError
- }
- catch (FileError&) { assert(false); }
- linkInfo.lastWriteTimeRaw = statData.st_mtime; //UTC time (ANSI C format); unit: 1 second
- linkInfo.dirLink = validLink && S_ISDIR(statDataTrg.st_mode);
- //S_ISDIR and S_ISLNK are mutually exclusive on Linux => explicitly need to follow link
+ linkInfo.lastWriteTime = statData.st_mtime; //UTC time (ANSI C format); unit: 1 second
switch (sink.onSymlink(shortName, fullName, linkInfo))
{
case TraverseCallback::LINK_FOLLOW:
+ {
//try to resolve symlink (and report error on failure!!!)
- validLink = tryReportingError([&]
+ struct ::stat statDataTrg = {};
+ bool validLink = tryReportingItemError([&]
{
- if (validLink) return; //no need to check twice
if (::stat(fullName.c_str(), &statDataTrg) != 0)
throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(fullName)) + L"\n\n" + getLastErrorFormatted());
- }, sink);
+ }, sink, shortName);
if (validLink)
{
if (S_ISDIR(statDataTrg.st_mode)) //a directory
{
if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
- traverse(fullName, *rv, level + 1);
+ traverse(fullName, *rv);
}
else //a file or named pipe, ect.
{
TraverseCallback::FileInfo fileInfo;
- fileInfo.fileSize = zen::UInt64(statDataTrg.st_size);
- fileInfo.lastWriteTimeRaw = statDataTrg.st_mtime; //UTC time (time_t format); unit: 1 second
- //fileInfo.id = extractFileID(statDataTrg); -> id from dereferenced symlink is problematic, since renaming would consider the link, not the target!
+ fileInfo.fileSize = zen::UInt64(statDataTrg.st_size);
+ fileInfo.lastWriteTime = statDataTrg.st_mtime; //UTC time (time_t format); unit: 1 second
+ fileInfo.id = extractFileID(statDataTrg);
sink.onFile(shortName, fullName, fileInfo);
}
}
- else //report broken symlink as file!
- sink.onFile(shortName, fullName, TraverseCallback::FileInfo());
- break;
+ // else //report broken symlink as file!
+ // sink.onFile(shortName, fullName, TraverseCallback::FileInfo());
+ }
+ break;
case TraverseCallback::LINK_SKIP:
break;
@@ -646,14 +613,14 @@ private:
else if (S_ISDIR(statData.st_mode)) //a directory
{
if (const std::shared_ptr<TraverseCallback>& rv = sink.onDir(shortName, fullName))
- traverse(fullName, *rv, level + 1);
+ traverse(fullName, *rv);
}
else //a file or named pipe, ect.
{
TraverseCallback::FileInfo fileInfo;
- fileInfo.fileSize = zen::UInt64(statData.st_size);
- fileInfo.lastWriteTimeRaw = statData.st_mtime; //UTC time (time_t format); unit: 1 second
- fileInfo.id = extractFileID(statData);
+ fileInfo.fileSize = zen::UInt64(statData.st_size);
+ fileInfo.lastWriteTime = statData.st_mtime; //UTC time (time_t format); unit: 1 second
+ fileInfo.id = extractFileID(statData);
sink.onFile(shortName, fullName, fileInfo);
}
@@ -676,7 +643,7 @@ private:
}
-void zen::traverseFolder(const Zstring& directory, TraverseCallback& sink, DstHackCallback* dstCallback)
+void zen::traverseFolder(const Zstring& dirname, TraverseCallback& sink, DstHackCallback* dstCallback)
{
- DirTraverser(directory, sink, dstCallback);
+ DirTraverser(dirname, sink, dstCallback);
}
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index 97fb0e9f..13b76966 100644
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -22,17 +22,15 @@ struct TraverseCallback
struct FileInfo
{
- UInt64 fileSize; //unit: bytes!
- Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC
- FileId id; //optional: initial if not supported!
+ UInt64 fileSize; //unit: bytes!
+ Int64 lastWriteTime; //number of seconds since Jan. 1st 1970 UTC
+ FileId id; //optional: initial if not supported!
//std::unique_ptr<SymlinkInfo> symlinkInfo; //only filled if file is dereferenced symlink
};
struct SymlinkInfo
{
- Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC
- Zstring targetPath; //optional: empty if something goes wrong
- bool dirLink; //"true": point to dir; "false": point to file (or broken Link on Linux)
+ Int64 lastWriteTime; //number of seconds since Jan. 1st 1970 UTC
};
enum HandleLink
@@ -51,7 +49,8 @@ struct TraverseCallback
/**/ onDir (const Zchar* shortName, const Zstring& fullName) = 0;
virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details) = 0;
virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) = 0;
- virtual HandleError onError (const std::wstring& msg) = 0;
+ virtual HandleError reportDirError (const std::wstring& msg) = 0; //failed directory traversal -> consider directory data as incomplete!
+ virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) = 0; //failed to get single file/dir/symlink only!
};
@@ -68,7 +67,7 @@ struct DstHackCallback; //DST hack not required on Unix
//custom traverser with detail information about files
//Win: client needs to handle duplicate file notifications! (FilePlusTraverser fallback)
//directory may end with PATH_SEPARATOR
-void traverseFolder(const Zstring& directory, //throw()
+void traverseFolder(const Zstring& dirname, //throw()
TraverseCallback& sink,
DstHackCallback* dstCallback = nullptr); //apply DST hack if callback is supplied
}
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index 7cc43e6b..60eb6869 100644
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -39,7 +39,7 @@ std::wstring zen::filesizeToShortString(Int64 size)
wchar_t buffer[50];
#ifdef __MINGW32__
- int charsWritten = ::_snwprintf(buffer, 50, L"%.*f", precisionDigits, sizeInUnit); //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, sizeInUnit);
#endif
@@ -148,7 +148,6 @@ std::wstring zen::remainingTimeToString(double timeInSec)
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!?
}
diff --git a/zen/int64.h b/zen/int64.h
index a760d5ab..4c63a24f 100644
--- a/zen/int64.h
+++ b/zen/int64.h
@@ -14,7 +14,6 @@
#include <ostream>
#include "assert_static.h"
#include "type_tools.h"
-#include "type_traits.h"
#ifdef FFS_WIN
#include "win.h"
diff --git a/zen/long_path_prefix.h b/zen/long_path_prefix.h
index c3d705e2..db1fbdca 100644
--- a/zen/long_path_prefix.h
+++ b/zen/long_path_prefix.h
@@ -58,8 +58,8 @@ Zstring applyLongPathPrefixImpl(const Zstring& path)
assert(!zen::isWhiteSpace(path[0]));
if (path.length() >= maxPath || //maximum allowed path length without prefix is (MAX_PATH - 1)
- endsWith(path, L' ') || //by default all Win32 APIs trim trailing spaces and period, unless long path prefix is supplied!
- endsWith(path, L'.')) //
+ endsWith(path, L' ') || //by default all Win32 APIs trim trailing spaces and period, unless long path prefix is supplied!
+ endsWith(path, L'.')) //note: adding long path prefix might screw up relative paths "." and ".."!
if (!startsWith(path, LONG_PATH_PREFIX))
{
if (startsWith(path, L"\\\\")) //UNC-name, e.g. \\zenju-pc\Users
diff --git a/zen/serialize.h b/zen/serialize.h
index a9238359..982165f9 100644
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -86,6 +86,7 @@ struct BinStreamIn //throw UnexpectedEndOfStreamError
const void* requestRead(size_t len) //throw UnexpectedEndOfStreamError
{
+ if (len == 0) return nullptr; //don't allow for possibility to access empty buffer
if (pos + len > buffer.size())
throw UnexpectedEndOfStreamError();
size_t oldPos = pos;
@@ -102,8 +103,9 @@ struct BinStreamOut
{
void* requestWrite(size_t len)
{
- size_t oldSize = buffer.size();
- buffer.resize(buffer.size() + len);
+ if (len == 0) return nullptr; //don't allow for possibility to access empty buffer
+ const size_t oldSize = buffer.size();
+ buffer.resize(oldSize + len);
return &*buffer.begin() + oldSize;
}
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 07fc31d7..b394b128 100644
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -18,7 +18,6 @@
#endif
-
//enhancements for <algorithm>
namespace zen
{
@@ -47,6 +46,10 @@ template <class BidirectionalIterator1, class BidirectionalIterator2>
BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1,
BidirectionalIterator2 first2, BidirectionalIterator2 last2);
+template <class InputIterator1, class InputIterator2>
+bool equal(InputIterator1 first1, InputIterator1 last1,
+ InputIterator2 first2, InputIterator2 last2);
+
//hash container: proper name + mitigate MSVC performance bug
template <class T> class hash_set;
template <class K, class V> class hash_map;
@@ -166,6 +169,14 @@ BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, Bidirect
}
+template <class InputIterator1, class InputIterator2> inline
+bool equal(InputIterator1 first1, InputIterator1 last1,
+ InputIterator2 first2, InputIterator2 last2)
+{
+ return last1 - first1 == last2 - first2 && std::equal(first1, last1, first2);
+}
+
+
#if defined _MSC_VER && _MSC_VER <= 1600 //VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11
template <class T> class hash_set : public std::set<T> {};
template <class K, class V> class hash_map : public std::map<K, V> {};
diff --git a/zen/string_base.h b/zen/string_base.h
index e38fab94..591ed62b 100644
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -13,7 +13,7 @@
#include "string_tools.h"
#include <boost/detail/atomic_count.hpp>
-//Zbase - a policy based string class optimizing performance and genericity
+//Zbase - a policy based string class optimizing performance and flexibility
namespace zen
{
@@ -183,7 +183,7 @@ private:
//################################################################################################################################################################
-//perf note: interstingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison
+//perf note: interestingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison
template <class Char, //Character Type
template <class, class> class SP = StorageRefCountThreadSafe, //Storage Policy
@@ -231,7 +231,7 @@ public:
size_t find (Char ch, size_t pos = 0) const; //returns "npos" if not found
size_t rfind(Char ch, size_t pos = npos) const; //
size_t rfind(const Char* str, size_t pos = npos) const; //
- Zbase& replace(size_t pos1, size_t n1, const Zbase& str);
+ //Zbase& replace(size_t pos1, size_t n1, const Zbase& str);
void reserve(size_t minCapacity);
Zbase& assign(const Char* source, size_t len);
Zbase& append(const Char* source, size_t len);
@@ -361,11 +361,11 @@ Zbase<Char, SP, AP>::Zbase(const Zbase<Char, SP, AP>& source)
template <class Char, template <class, class> class SP, class AP> inline
Zbase<Char, SP, AP>::Zbase(Zbase<Char, SP, AP>&& tmp)
{
- //rawStr = this->clone(tmp.rawStr); NO! do not increment ref-count of a potentially unshared string! We'd lose optimization opportunity of reusing it!
- //instead create a dummy string and swap:
- if (this->canWrite(tmp.rawStr, 0)) //perf: this check saves about 4%
+ if (this->canWrite(tmp.rawStr, 0)) //perf: following optimization saves about 4%
{
- rawStr = this->create(0); //no perf issue! see comment in default constructor
+ //do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory!
+ //instead create a dummy string and swap:
+ rawStr = this->create(0); //no perf issue! see comment in default constructor
rawStr[0] = 0;
swap(tmp);
}
@@ -450,6 +450,7 @@ size_t Zbase<Char, SP, AP>::rfind(const Char* str, size_t pos) const
}
+/* -> dead code ahead: better use zen::replace template instead!
template <class Char, template <class, class> class SP, class AP>
Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::replace(size_t pos1, size_t n1, const Zbase& str)
{
@@ -493,7 +494,7 @@ Zbase<Char, SP, AP>& Zbase<Char, SP, AP>::replace(size_t pos1, size_t n1, const
}
return *this;
}
-
+*/
template <class Char, template <class, class> class SP, class AP> inline
void Zbase<Char, SP, AP>::resize(size_t newSize, Char fillChar)
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 93e8c510..22aa2ffc 100644
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -23,31 +23,31 @@ GetCharType<>::Type:
GetCharType<std::wstring>::Type //equals wchar_t
GetCharType<wchar_t[5]> ::Type //equals wchar_t
-strBegin():
+strLength():
+ strLength(str); //equals str.length()
+ strLength(array); //equals cStringLength(array)
+
+strBegin(): -> not null-terminated! -> may be nullptr if length is 0!
std::wstring str(L"dummy");
char array[] = "dummy";
strBegin(str); //returns str.c_str()
strBegin(array); //returns array
-
-strLength():
- strLength(str); //equals str.length()
- strLength(array); //equals cStringLength(array)
*/
-//reference a sub-string or a char* as an intermediate string class when the length is already known
+//reference a sub-string for consumption by zen string_tools
template <class Char>
-class StringProxy
+class StringRef
{
public:
- StringProxy(const Char* cstr, size_t len ) : cstr_(cstr), length_(len) {}
- StringProxy(const Char* cstrBegin, const Char* cstrEnd) : cstr_(cstrBegin), length_(cstrEnd - cstrBegin) {}
+ template <class Iterator>
+ StringRef(Iterator first, Iterator last) : length_(last - first), data_(first != last ? &*first : nullptr) {}
- const Char* c_str() const { return cstr_; }
size_t length() const { return length_; }
+ const Char* data() const { return data_; } //1. no null-termination! 2. may be nullptr!
private:
- const Char* cstr_;
size_t length_;
+ const Char* data_;
};
@@ -61,7 +61,6 @@ private:
-
//---------------------- implementation ----------------------
namespace implementation
{
@@ -129,6 +128,28 @@ public:
IsSameType<CharType, wchar_t>::value
};
};
+
+
+template <> class StringTraits<StringRef<char>>
+{
+public:
+ enum
+ {
+ isStringClass = false,
+ isStringLike = true
+ };
+ typedef char CharType;
+};
+template <> class StringTraits<StringRef<wchar_t>>
+{
+public:
+ enum
+ {
+ isStringClass = false,
+ isStringLike = true
+ };
+ typedef wchar_t CharType;
+};
}
template <class T>
@@ -162,6 +183,8 @@ inline const char* strBegin(const char* str) { return str; }
inline const wchar_t* strBegin(const wchar_t* str) { return str; }
inline const char* strBegin(const char& ch) { return &ch; }
inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; }
+inline const char* strBegin(const StringRef<char >& ref) { return ref.data(); }
+inline const wchar_t* strBegin(const StringRef<wchar_t>& ref) { return ref.data(); }
template <class S> inline
@@ -174,6 +197,8 @@ inline size_t strLength(const char* str) { return implementation::cStringLeng
inline size_t strLength(const wchar_t* str) { return implementation::cStringLength(str); }
inline size_t strLength(char) { return 1; }
inline size_t strLength(wchar_t) { return 1; }
+inline size_t strLength(const StringRef<char >& ref) { return ref.length(); }
+inline size_t strLength(const StringRef<wchar_t>& ref) { return ref.length(); }
}
#endif //STRING_TRAITS_HEADER_813274321443234
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 95aa84fb..bbced0fa 100644
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -4,8 +4,8 @@
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
-#ifndef SYMLINK_WIN_H_INCLUDED
-#define SYMLINK_WIN_H_INCLUDED
+#ifndef SYMLINK_80178347019835748321473214
+#define SYMLINK_80178347019835748321473214
#include "scope_guard.h"
#include "file_error.h"
@@ -15,6 +15,7 @@
#include "WinIoCtl.h"
#include "privilege.h"
#include "long_path_prefix.h"
+#include "dll.h"
#elif defined FFS_LINUX || defined FFS_MAC
#include <unistd.h>
@@ -26,9 +27,11 @@ namespace zen
#ifdef FFS_WIN
bool isSymlink(const WIN32_FIND_DATA& data); //*not* a simple FILE_ATTRIBUTE_REPARSE_POINT check!
bool isSymlink(DWORD fileAttributes, DWORD reparseTag);
+
+Zstring getResolvedFilePath(const Zstring& filename); //throw FileError; requires Vista or later!
#endif
-Zstring getSymlinkRawTargetString(const Zstring& linkPath); //throw FileError
+Zstring getSymlinkTargetRaw(const Zstring& linkPath); //throw FileError
}
@@ -90,7 +93,7 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
catch (FileError&) {} //This shall not cause an error in user mode!
const HANDLE hLink = ::CreateFile(applyLongPathPrefix(linkPath).c_str(),
- GENERIC_READ,
+ 0, //it seems we do not even need GENERIC_READ!
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING,
@@ -138,32 +141,78 @@ Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileErro
return output;
#elif defined FFS_LINUX || defined FFS_MAC
- const int BUFFER_SIZE = 10000;
+ const size_t BUFFER_SIZE = 10000;
std::vector<char> buffer(BUFFER_SIZE);
- const int bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE);
- if (bytesWritten < 0 || bytesWritten >= BUFFER_SIZE)
+ const ssize_t bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE);
+ if (bytesWritten < 0 || bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation!
{
std::wstring errorMessage = replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtFileName(linkPath));
if (bytesWritten < 0)
errorMessage += L"\n\n" + getLastErrorFormatted();
throw FileError(errorMessage);
}
- buffer[bytesWritten] = 0; //set null-terminating char
-
- return Zstring(&buffer[0], bytesWritten);
+ return Zstring(&buffer[0], bytesWritten); //readlink does not append 0-termination!
#endif
}
+
+
+#ifdef FFS_WIN
+Zstring getResolvedFilePath_impl(const Zstring& filename) //throw FileError
+{
+ using namespace zen;
+
+ const HANDLE hDir = ::CreateFile(applyLongPathPrefix(filename).c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory
+ nullptr);
+ if (hDir == INVALID_HANDLE_VALUE)
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted() + L" (CreateFile)");
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
+
+ //GetFinalPathNameByHandle() is not available before Vista!
+ typedef DWORD (WINAPI* GetFinalPathNameByHandleWFunc)(HANDLE hFile, LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
+ const SysDllFun<GetFinalPathNameByHandleWFunc> getFinalPathNameByHandle(L"kernel32.dll", "GetFinalPathNameByHandleW");
+
+ if (!getFinalPathNameByHandle)
+ throw FileError(replaceCpy(_("Cannot find system function %x."), L"%x", L"\"GetFinalPathNameByHandleW\""));
+
+ const DWORD bufferSize = getFinalPathNameByHandle(hDir, nullptr, 0, 0);
+ if (bufferSize == 0)
+ throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename)) + L"\n\n" + getLastErrorFormatted());
+
+ std::vector<wchar_t> targetPath(bufferSize);
+ const DWORD charsWritten = getFinalPathNameByHandle(hDir, //__in HANDLE hFile,
+ &targetPath[0], //__out LPTSTR lpszFilePath,
+ bufferSize, //__in DWORD cchFilePath,
+ 0); //__in DWORD dwFlags
+ if (charsWritten == 0 || charsWritten >= bufferSize)
+ {
+ std::wstring errorMessage = replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtFileName(filename));
+ if (charsWritten == 0)
+ errorMessage += L"\n\n" + getLastErrorFormatted();
+ throw FileError(errorMessage);
+ }
+
+ return Zstring(&targetPath[0], charsWritten);
+}
+#endif
}
namespace zen
{
inline
-Zstring getSymlinkRawTargetString(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); }
+Zstring getSymlinkTargetRaw(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); }
#ifdef FFS_WIN
+inline
+Zstring getResolvedFilePath(const Zstring& filename) { return getResolvedFilePath_impl(filename); }
+
/*
Reparse Point Tags
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365511(v=vs.85).aspx
@@ -190,4 +239,4 @@ bool isSymlink(const WIN32_FIND_DATA& data)
#endif
}
-#endif // SYMLINK_WIN_H_INCLUDED
+#endif //SYMLINK_80178347019835748321473214
diff --git a/zen/thread.h b/zen/thread.h
index db9cf3a3..638d9474 100644
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -10,11 +10,12 @@
//temporary solution until C++11 thread becomes fully available (considering std::thread's non-interruptibility and std::async craziness, this may be NEVER)
#include <memory>
-//fix this pathetic boost thread warning mess
+//workaround this pathetic boost thread warning mess
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wswitch-enum"
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#ifdef _MSC_VER
diff --git a/zen/tick_count.h b/zen/tick_count.h
index be4839ca..d005a828 100644
--- a/zen/tick_count.h
+++ b/zen/tick_count.h
@@ -129,7 +129,7 @@ std::int64_t ticksPerSec() //return 0 on error
inline
-TickVal getTicks() //return 0 on error
+TickVal getTicks() //return !isValid() on error
{
#ifdef FFS_WIN
LARGE_INTEGER now = {};
diff --git a/zen/time.h b/zen/time.h
index 474c48c5..80481856 100644
--- a/zen/time.h
+++ b/zen/time.h
@@ -291,7 +291,7 @@ bool parseTime(const String& format, const String& str, TimeComp& comp) //return
if (std::any_of(iterStr, iterStr + digitCount, [](CharType c) { return !isDigit(c); }))
return false;
- result = zen::stringTo<int>(StringProxy<CharType>(iterStr, digitCount));
+ result = zen::stringTo<int>(StringRef<CharType>(iterStr, iterStr + digitCount));
iterStr += digitCount;
return true;
};
diff --git a/zen/utf.h b/zen/utf.h
index 27804a21..8da588cd 100644
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -50,18 +50,6 @@ size_t findUnicodePos(const UtfString& str, size_t unicodePos); //return positio
-
-
-
-
-
-
-
-
-
-
-
-
//----------------------- implementation ----------------------------------
namespace implementation
{
@@ -69,45 +57,47 @@ typedef std::uint_fast32_t CodePoint; //must be at least four bytes
typedef std::uint_fast16_t Char16; //we need an unsigned type
typedef unsigned char Char8;
-const CodePoint CODE_POINT_MAX = 0x10ffff;
+const CodePoint LEAD_SURROGATE = 0xd800;
+const CodePoint TRAIL_SURROGATE = 0xdc00; //== LEAD_SURROGATE_MAX + 1
+const CodePoint TRAIL_SURROGATE_MAX = 0xdfff;
-const CodePoint HIGH_SURROGATE = 0xd800;
-const CodePoint HIGH_SURROGATE_MAX = 0xdbff;
-
-const CodePoint LOW_SURROGATE = 0xdc00;
-const CodePoint LOW_SURROGATE_MAX = 0xdfff;
+const CodePoint REPLACEMENT_CHAR = 0xfffd;
+const CodePoint CODE_POINT_MAX = 0x10ffff;
template <class Function> inline
void codePointToUtf16(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char16
{
//http://en.wikipedia.org/wiki/UTF-16
- assert(cp < HIGH_SURROGATE || LOW_SURROGATE_MAX < cp); //code points [0xd800, 0xdfff] are not allowed for UTF-16
- assert(cp <= CODE_POINT_MAX);
- if (cp < 0x10000)
+ if (cp < LEAD_SURROGATE)
writeOutput(static_cast<Char16>(cp));
- else
+ else if (cp <= TRAIL_SURROGATE_MAX) //invalid code point
+ codePointToUtf16(REPLACEMENT_CHAR, writeOutput); //resolves to 1-character utf16
+ else if (cp < 0x10000)
+ writeOutput(static_cast<Char16>(cp));
+ else if (cp <= CODE_POINT_MAX)
{
cp -= 0x10000;
- writeOutput(static_cast<Char16>((cp >> 10) + HIGH_SURROGATE));
- writeOutput(static_cast<Char16>((cp & 0x3ff) + LOW_SURROGATE));
+ writeOutput(LEAD_SURROGATE + static_cast<Char16>(cp >> 10));
+ writeOutput(TRAIL_SURROGATE + static_cast<Char16>(cp & 0x3ff));
}
+ else //invalid code point
+ codePointToUtf16(REPLACEMENT_CHAR, writeOutput); //resolves to 1-character utf16
}
inline
-size_t getUtf16Len(Char16 ch) //ch must be first code unit!
+size_t getUtf16Len(Char16 ch) //ch must be first code unit! returns 0 on error!
{
- const CodePoint cp = ch;
-
- if (HIGH_SURROGATE <= cp && cp <= HIGH_SURROGATE_MAX)
+ if (ch < LEAD_SURROGATE)
+ return 1;
+ else if (ch < TRAIL_SURROGATE)
return 2;
+ else if (ch <= TRAIL_SURROGATE_MAX)
+ return 0; //unexpected trail surrogate!
else
- {
- assert(cp < LOW_SURROGATE || LOW_SURROGATE_MAX < cp); //NO low surrogate expected
return 1;
- }
}
@@ -119,19 +109,27 @@ void utf16ToCodePoint(CharIterator first, CharIterator last, Function writeOutpu
for ( ; first != last; ++first)
{
CodePoint cp = static_cast<Char16>(*first);
- if (HIGH_SURROGATE <= cp && cp <= HIGH_SURROGATE_MAX)
+ switch (getUtf16Len(static_cast<Char16>(cp)))
{
- if (++first == last)
- {
- assert(false); //low surrogate expected
- return;
- }
- assert(LOW_SURROGATE <= static_cast<Char16>(*first) && static_cast<Char16>(*first) <= LOW_SURROGATE_MAX); //low surrogate expected
- cp = ((cp - HIGH_SURROGATE) << 10) + static_cast<Char16>(*first) - LOW_SURROGATE + 0x10000;
+ case 0: //invalid utf16 character
+ cp = REPLACEMENT_CHAR;
+ break;
+ case 1:
+ break;
+ case 2:
+ if (++first != last) //trail surrogate expected!
+ {
+ const Char16 ch = static_cast<Char16>(*first);
+ if (TRAIL_SURROGATE <= ch && ch <= TRAIL_SURROGATE_MAX) //trail surrogate expected!
+ {
+ cp = ((cp - LEAD_SURROGATE) << 10) + (ch - TRAIL_SURROGATE) + 0x10000;
+ break;
+ }
+ }
+ --first;
+ cp = REPLACEMENT_CHAR;
+ break;
}
- else
- assert(cp < LOW_SURROGATE || LOW_SURROGATE_MAX < cp); //NO low surrogate expected
-
writeOutput(cp);
}
}
@@ -141,6 +139,7 @@ template <class Function> inline
void codePointToUtf8(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a Char8
{
//http://en.wikipedia.org/wiki/UTF-8
+ //assert(cp < LEAD_SURROGATE || TRAIL_SURROGATE_MAX < cp); //code points [0xd800, 0xdfff] are reserved for UTF-16 and *should* not be encoded in UTF-8
if (cp < 0x80)
writeOutput(static_cast<Char8>(cp));
@@ -151,23 +150,24 @@ void codePointToUtf8(CodePoint cp, Function writeOutput) //"writeOutput" is a un
}
else if (cp < 0x10000)
{
- writeOutput(static_cast<Char8>((cp >> 12 ) | 0xe0));
+ writeOutput(static_cast<Char8>( (cp >> 12 ) | 0xe0));
writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80));
- writeOutput(static_cast<Char8>((cp & 0x3f ) | 0x80));
+ writeOutput(static_cast<Char8>( (cp & 0x3f ) | 0x80));
}
- else
+ else if (cp <= CODE_POINT_MAX)
{
- assert(cp <= CODE_POINT_MAX);
- writeOutput(static_cast<Char8>((cp >> 18 ) | 0xf0));
+ writeOutput(static_cast<Char8>( (cp >> 18 ) | 0xf0));
writeOutput(static_cast<Char8>(((cp >> 12) & 0x3f) | 0x80));
writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80));
- writeOutput(static_cast<Char8>((cp & 0x3f ) | 0x80));
+ writeOutput(static_cast<Char8>( (cp & 0x3f ) | 0x80));
}
+ else //invalid code point
+ codePointToUtf8(REPLACEMENT_CHAR, writeOutput); //resolves to 3-byte utf8
}
inline
-size_t getUtf8Len(unsigned char ch) //ch must be first code unit!
+size_t getUtf8Len(unsigned char ch) //ch must be first code unit! returns 0 on error!
{
if (ch < 0x80)
return 1;
@@ -177,12 +177,27 @@ size_t getUtf8Len(unsigned char ch) //ch must be first code unit!
return 3;
if (ch >> 3 == 0x1e)
return 4;
-
- assert(false); //no valid begin of UTF8 encoding
- return 1;
+ return 0; //innvalid begin of UTF8 encoding
}
+template <class CharIterator> inline
+bool decodeTrail(CharIterator& first, CharIterator last, CodePoint& cp) //decode trailing surrogate byte
+{
+ if (++first != last) //trail surrogate expected!
+ {
+ const Char8 ch = static_cast<Char8>(*first);
+ if (ch >> 6 == 0x2) //trail surrogate expected!
+ {
+ cp = (cp << 6) + (ch & 0x3f);
+ return true;
+ }
+ }
+ --first;
+ cp = REPLACEMENT_CHAR;
+ return false;
+}
+
template <class CharIterator, class Function> inline
void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint
{
@@ -190,57 +205,32 @@ void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput
for ( ; first != last; ++first)
{
- auto getChar = [&](Char8& ch) -> bool
- {
- if (++first == last)
- {
- assert(false); //low surrogate expected
- return false;
- }
- ch = static_cast<Char8>(*first);
- assert(ch >> 6 == 0x2);
- return true;
- };
-
- Char8 ch = static_cast<Char8>(*first);
- switch (getUtf8Len(ch))
+ CodePoint cp = static_cast<Char8>(*first);
+ switch (getUtf8Len(static_cast<Char8>(cp)))
{
+ case 0: //invalid utf8 character
+ cp = REPLACEMENT_CHAR;
+ break;
case 1:
- writeOutput(ch);
break;
case 2:
- {
- CodePoint cp = (ch & 0x1f) << 6;
- if (!getChar(ch)) return;
- cp += ch & 0x3f;
- writeOutput(cp);
- }
- break;
+ cp &= 0x1f;
+ decodeTrail(first, last, cp);
+ break;
case 3:
- {
- CodePoint cp = (ch & 0xf) << 12;
- if (!getChar(ch)) return;
- cp += (ch & 0x3f) << 6;
- if (!getChar(ch)) return;
- cp += ch & 0x3f;
- writeOutput(cp);
- }
- break;
+ cp &= 0xf;
+ if (decodeTrail(first, last, cp))
+ decodeTrail(first, last, cp);
+ break;
case 4:
- {
- CodePoint cp = (ch & 0x7) << 18;
- if (!getChar(ch)) return;
- cp += (ch & 0x3f) << 12;
- if (!getChar(ch)) return;
- cp += (ch & 0x3f) << 6;
- if (!getChar(ch)) return;
- cp += ch & 0x3f;
- writeOutput(cp);
- }
- break;
- default:
- assert(false);
+ cp &= 0x7;
+ if (decodeTrail(first, last, cp))
+ if (decodeTrail(first, last, cp))
+ decodeTrail(first, last, cp);
+ if (cp > CODE_POINT_MAX) cp = REPLACEMENT_CHAR;
+ break;
}
+ writeOutput(cp);
}
}
@@ -257,7 +247,10 @@ size_t unicodeLength(const CharString& str, char) //utf8
while (strFirst < strLast) //[!]
{
++len;
- strFirst += getUtf8Len(*strFirst); //[!]
+
+ size_t utf8len = getUtf8Len(*strFirst);
+ if (utf8len == 0) ++utf8len; //invalid utf8 character
+ strFirst += utf8len;
}
return len;
}
@@ -275,7 +268,9 @@ size_t unicodeLengthWide(const WideString& str, Int2Type<2>) //windows: utf16-wc
while (strFirst < strLast) //[!]
{
++len;
- strFirst += getUtf16Len(*strFirst); //[!]
+ size_t utf16len = getUtf16Len(*strFirst);
+ if (utf16len == 0) ++utf16len; //invalid utf16 character
+ strFirst += utf16len;
}
return len;
}
@@ -316,7 +311,9 @@ size_t findUnicodePos(const CharString& str, size_t unicodePos, char) //utf8-cha
size_t utfPos = 0;
while (unicodePos-- > 0)
{
- utfPos += getUtf8Len(strFirst[utfPos]);
+ size_t utf8len = getUtf8Len(strFirst[utfPos]);
+ if (utf8len == 0) ++utf8len; //invalid utf8 character
+ utfPos += utf8len;
if (utfPos >= strLen)
return strLen;
@@ -336,7 +333,9 @@ size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>)
size_t utfPos = 0;
while (unicodePos-- > 0)
{
- utfPos += getUtf16Len(strFirst[utfPos]);
+ size_t utf16len = getUtf16Len(strFirst[utfPos]);
+ if (utf16len == 0) ++utf16len; //invalid utf16 character
+ utfPos += utf16len;
if (utfPos >= strLen)
return strLen;
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index 262df49e..085e6f72 100644
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -78,10 +78,8 @@ private:
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);
+ std::string output(reinterpret_cast<const char*>(ptr), std::min<size_t>(size, 100));
+ std::replace(output.begin(), output.end(), '\0', ' '); //don't stop at 0-termination
return output;
}
bgstack15