summaryrefslogtreecommitdiff
path: root/zen
diff options
context:
space:
mode:
Diffstat (limited to 'zen')
-rwxr-xr-xzen/basic_math.h766
-rwxr-xr-xzen/build_info.h58
-rwxr-xr-xzen/crc.h108
-rwxr-xr-xzen/deprecate.h28
-rwxr-xr-xzen/dir_watcher.cpp316
-rwxr-xr-xzen/dir_watcher.h150
-rwxr-xr-xzen/error_log.h270
-rwxr-xr-xzen/file_access.cpp1352
-rwxr-xr-xzen/file_access.h202
-rwxr-xr-xzen/file_error.h106
-rwxr-xr-xzen/file_id_def.h82
-rwxr-xr-xzen/file_io.cpp548
-rwxr-xr-xzen/file_io.h246
-rwxr-xr-xzen/file_traverser.cpp210
-rwxr-xr-xzen/file_traverser.h94
-rwxr-xr-xzen/fixed_list.h478
-rwxr-xr-xzen/format_unit.cpp402
-rwxr-xr-xzen/format_unit.h106
-rwxr-xr-xzen/globals.h130
-rwxr-xr-xzen/guid.h60
-rwxr-xr-xzen/i18n.h238
-rwxr-xr-xzen/optional.h200
-rwxr-xr-xzen/perf.h166
-rwxr-xr-xzen/process_priority.cpp108
-rwxr-xr-xzen/process_priority.h76
-rwxr-xr-xzen/recycler.cpp144
-rwxr-xr-xzen/recycler.h86
-rwxr-xr-xzen/scope_guard.h242
-rwxr-xr-xzen/serialize.h562
-rwxr-xr-xzen/shell_execute.h104
-rwxr-xr-xzen/stl_tools.h488
-rwxr-xr-xzen/string_base.h1226
-rwxr-xr-xzen/string_tools.h1526
-rwxr-xr-xzen/string_traits.h432
-rwxr-xr-xzen/symlink_target.h150
-rwxr-xr-xzen/sys_error.h210
-rwxr-xr-xzen/thread.h830
-rwxr-xr-xzen/time.h736
-rwxr-xr-xzen/type_tools.h206
-rwxr-xr-xzen/type_traits.h390
-rwxr-xr-xzen/utf.h774
-rwxr-xr-xzen/warn_static.h48
-rwxr-xr-xzen/xml_io.cpp166
-rwxr-xr-xzen/xml_io.h52
-rwxr-xr-xzen/zstring.cpp300
-rwxr-xr-xzen/zstring.h318
46 files changed, 7745 insertions, 7745 deletions
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 7836ae81..722722a5 100755
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -1,383 +1,383 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef BASIC_MATH_H_3472639843265675
-#define BASIC_MATH_H_3472639843265675
-
-#include <algorithm>
-#include <iterator>
-#include <limits>
-#include <cmath>
-#include <functional>
-#include <cassert>
-
-
-namespace numeric
-{
-template <class T> T abs(T value);
-template <class T> auto dist(T a, T b);
-template <class T> int sign(T value); //returns one of {-1, 0, 1}
-template <class T> T min(T a, T b, T c);
-template <class T> T max(T a, T b, T c);
-template <class T> bool isNull(T value);
-
-template <class T>
-void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal
-template <class T>
-T clampCpy(T val, T minVal, T maxVal);
-
-template <class T, class InputIterator> //precondition: range must be sorted!
-auto nearMatch(const T& val, InputIterator first, InputIterator last);
-
-int round(double d); //"little rounding function"
-
-template <class N, class D>
-auto integerDivideRoundUp(N numerator, D denominator);
-
-template <size_t N, class T>
-T power(T value);
-
-double radToDeg(double rad); //convert unit [rad] into [°]
-double degToRad(double degree); //convert unit [°] into [rad]
-
-template <class InputIterator>
-double arithmeticMean(InputIterator first, InputIterator last);
-
-template <class RandomAccessIterator>
-double median(RandomAccessIterator first, RandomAccessIterator last); //note: invalidates input range!
-
-template <class InputIterator>
-double stdDeviation(InputIterator first, InputIterator last, double* mean = nullptr); //estimate standard deviation (and thereby arithmetic mean)
-
-//median absolute deviation: "mad / 0.6745" is a robust measure for standard deviation of a normal distribution
-template <class RandomAccessIterator>
-double mad(RandomAccessIterator first, RandomAccessIterator last); //note: invalidates input range!
-
-template <class InputIterator>
-double norm2(InputIterator first, InputIterator last);
-
-//constants
-const double pi = 3.14159265358979323846;
-const double e = 2.71828182845904523536;
-const double sqrt2 = 1.41421356237309504880;
-const double ln2 = 0.693147180559945309417;
-//----------------------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-//################# inline implementation #########################
-template <class T> inline
-T abs(T value)
-{
- //static_assert(std::is_signed<T>::value, "");
- if (value < 0)
- return -value; //operator "?:" caveat: may be different type than "value"
- else
- return value;
-}
-
-template <class T> inline
-auto dist(T a, T b) //return type might be different than T, e.g. std::chrono::duration instead of std::chrono::time_point
-{
- return a > b ? a - b : b - a;
-}
-
-
-template <class T> inline
-int sign(T value) //returns one of {-1, 0, 1}
-{
- static_assert(std::is_signed<T>::value, "");
- return value < 0 ? -1 : (value > 0 ? 1 : 0);
-}
-
-
-template <class T> inline
-T min(T a, T b, T c) //don't follow std::min's "const T&(const T&, const T&)" API
-{
- if (a < b)
- return a < c ? a : c;
- else
- return b < c ? b : c;
- //return std::min(std::min(a, b), c);
-}
-
-
-template <class T> inline
-T max(T a, T b, T c)
-{
- if (a > b)
- return a > c ? a : c;
- else
- return b > c ? b : c;
- //return std::max(std::max(a, b), c);
-}
-
-
-template <class T> inline
-T clampCpy(T val, T minVal, T maxVal)
-{
- assert(minVal <= maxVal);
- if (val < minVal)
- return minVal;
- else if (val > maxVal)
- return maxVal;
- return val;
-}
-
-template <class T> inline
-void clamp(T& val, T minVal, T maxVal)
-{
- assert(minVal <= maxVal);
- if (val < minVal)
- val = minVal;
- else if (val > maxVal)
- val = maxVal;
-}
-
-
-/*
-part of C++11 now!
-template <class InputIterator, class Compare> inline
-std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare compLess)
-{
- //by factor 1.5 to 3 faster than boost::minmax_element (=two-step algorithm) for built-in types!
-
- InputIterator lowest = first;
- InputIterator largest = first;
-
- if (first != last)
- {
- auto minVal = *lowest; //nice speedup on 64 bit!
- auto maxVal = *largest; //
- for (;;)
- {
- ++first;
- if (first == last)
- break;
- const auto val = *first;
-
- if (compLess(maxVal, val))
- {
- largest = first;
- maxVal = val;
- }
- else if (compLess(val, minVal))
- {
- lowest = first;
- minVal = val;
- }
- }
- }
- return std::make_pair(lowest, largest);
-}
-
-
-template <class InputIterator> inline
-std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last)
-{
- return minMaxElement(first, last, std::less<typename std::iterator_traits<InputIterator>::value_type>());
-}
-*/
-
-template <class T, class InputIterator> inline
-auto nearMatch(const T& val, InputIterator first, InputIterator last)
-{
- if (first == last)
- return static_cast<decltype(*first)>(0);
-
- assert(std::is_sorted(first, last));
- InputIterator it = std::lower_bound(first, last, val);
- if (it == last)
- return *--last;
- if (it == first)
- return *first;
-
- const auto nextVal = *it;
- const auto prevVal = *--it;
- return val - prevVal < nextVal - val ? prevVal : nextVal;
-}
-
-
-template <class T> inline
-bool isNull(T value)
-{
- return abs(value) <= std::numeric_limits<T>::epsilon(); //epsilon is 0 für integral types => less-equal
-}
-
-
-inline
-int round(double d)
-{
- assert(d - 0.5 >= std::numeric_limits<int>::min() && //if double is larger than what int can represent:
- d + 0.5 <= std::numeric_limits<int>::max()); //=> undefined behavior!
- return static_cast<int>(d < 0 ? d - 0.5 : d + 0.5);
-}
-
-
-template <class N, class D> inline
-auto integerDivideRoundUp(N numerator, D denominator)
-{
- static_assert(std::is_integral<N>::value && std::is_unsigned<N>::value, "");
- static_assert(std::is_integral<D>::value && std::is_unsigned<D>::value, "");
- assert(denominator > 0);
- return (numerator + denominator - 1) / denominator;
-}
-
-
-namespace
-{
-template <size_t N, class T> struct PowerImpl;
-/*
- template <size_t N, class T> -> let's use non-recursive specializations to help the compiler
- struct PowerImpl { static T result(const T& value) { return PowerImpl<N - 1, T>::result(value) * value; } };
-*/
-template <class T> struct PowerImpl<2, T> { static T result(T value) { return value * value; } };
-template <class T> struct PowerImpl<3, T> { static T result(T value) { return value * value * value; } };
-}
-
-template <size_t n, class T> inline
-T power(T value)
-{
- return PowerImpl<n, T>::result(value);
-}
-
-
-inline
-double radToDeg(double rad)
-{
- return rad * 180.0 / numeric::pi;
-}
-
-
-inline
-double degToRad(double degree)
-{
- return degree * numeric::pi / 180.0;
-}
-
-
-template <class InputIterator> inline
-double arithmeticMean(InputIterator first, InputIterator last)
-{
- size_t n = 0; //avoid random-access requirement for iterator!
- double sum_xi = 0;
-
- for (; first != last; ++first, ++n)
- sum_xi += *first;
-
- return n == 0 ? 0 : sum_xi / n;
-}
-
-
-template <class RandomAccessIterator> inline
-double median(RandomAccessIterator first, RandomAccessIterator last) //note: invalidates input range!
-{
- const size_t n = last - first;
- if (n > 0)
- {
- std::nth_element(first, first + n / 2, last); //complexity: O(n)
- const double midVal = *(first + n / 2);
-
- if (n % 2 != 0)
- return midVal;
- else //n is even and >= 2 in this context: return mean of two middle values
- return 0.5 * (*std::max_element(first, first + n / 2) + midVal); //this operation is the reason why median() CANNOT support a comparison predicate!!!
- }
- return 0;
-}
-
-
-template <class RandomAccessIterator> inline
-double mad(RandomAccessIterator first, RandomAccessIterator last) //note: invalidates input range!
-{
- //http://en.wikipedia.org/wiki/Median_absolute_deviation
-
- const size_t n = last - first;
- if (n > 0)
- {
- const double m = median(first, last);
-
- //the second median needs to operate on absolute residuals => avoid transforming input range which may have less than double precision!
-
- auto lessMedAbs = [m](double lhs, double rhs) { return abs(lhs - m) < abs(rhs - m); };
-
- std::nth_element(first, first + n / 2, last, lessMedAbs); //complexity: O(n)
- const double midVal = abs(*(first + n / 2) - m);
-
- if (n % 2 != 0)
- return midVal;
- else //n is even and >= 2 in this context: return mean of two middle values
- return 0.5 * (abs(*std::max_element(first, first + n / 2, lessMedAbs) - m) + midVal);
- }
- return 0;
-}
-
-
-template <class InputIterator> inline
-double stdDeviation(InputIterator first, InputIterator last, double* arithMean)
-{
- //implementation minimizing rounding errors, see: http://en.wikipedia.org/wiki/Standard_deviation
- //combined with technique avoiding overflow, see: http://www.netlib.org/blas/dnrm2.f -> only 10% performance degradation
-
- size_t n = 0;
- double mean = 0;
- double q = 0;
- double scale = 1;
-
- for (; first != last; ++first)
- {
- ++n;
- const double val = *first - mean;
-
- if (abs(val) > scale)
- {
- q = (n - 1.0) / n + q * power<2>(scale / val);
- scale = abs(val);
- }
- else
- q += (n - 1.0) * power<2>(val / scale) / n;
-
- mean += val / n;
- }
-
- if (arithMean)
- *arithMean = mean;
-
- return n <= 1 ? 0 : std::sqrt(q / (n - 1)) * scale;
-}
-
-
-template <class InputIterator> inline
-double norm2(InputIterator first, InputIterator last)
-{
- double result = 0;
- double scale = 1;
- for (; first != last; ++first)
- {
- const double tmp = abs(*first);
- if (tmp > scale)
- {
- result = 1 + result * power<2>(scale / tmp);
- scale = tmp;
- }
- else
- result += power<2>(tmp / scale);
- }
- return std::sqrt(result) * scale;
-}
-}
-
-#endif //BASIC_MATH_H_3472639843265675
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef BASIC_MATH_H_3472639843265675
+#define BASIC_MATH_H_3472639843265675
+
+#include <algorithm>
+#include <iterator>
+#include <limits>
+#include <cmath>
+#include <functional>
+#include <cassert>
+
+
+namespace numeric
+{
+template <class T> T abs(T value);
+template <class T> auto dist(T a, T b);
+template <class T> int sign(T value); //returns one of {-1, 0, 1}
+template <class T> T min(T a, T b, T c);
+template <class T> T max(T a, T b, T c);
+template <class T> bool isNull(T value);
+
+template <class T>
+void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal
+template <class T>
+T clampCpy(T val, T minVal, T maxVal);
+
+template <class T, class InputIterator> //precondition: range must be sorted!
+auto nearMatch(const T& val, InputIterator first, InputIterator last);
+
+int round(double d); //"little rounding function"
+
+template <class N, class D>
+auto integerDivideRoundUp(N numerator, D denominator);
+
+template <size_t N, class T>
+T power(T value);
+
+double radToDeg(double rad); //convert unit [rad] into [°]
+double degToRad(double degree); //convert unit [°] into [rad]
+
+template <class InputIterator>
+double arithmeticMean(InputIterator first, InputIterator last);
+
+template <class RandomAccessIterator>
+double median(RandomAccessIterator first, RandomAccessIterator last); //note: invalidates input range!
+
+template <class InputIterator>
+double stdDeviation(InputIterator first, InputIterator last, double* mean = nullptr); //estimate standard deviation (and thereby arithmetic mean)
+
+//median absolute deviation: "mad / 0.6745" is a robust measure for standard deviation of a normal distribution
+template <class RandomAccessIterator>
+double mad(RandomAccessIterator first, RandomAccessIterator last); //note: invalidates input range!
+
+template <class InputIterator>
+double norm2(InputIterator first, InputIterator last);
+
+//constants
+const double pi = 3.14159265358979323846;
+const double e = 2.71828182845904523536;
+const double sqrt2 = 1.41421356237309504880;
+const double ln2 = 0.693147180559945309417;
+//----------------------------------------------------------------------------------
+
+
+
+
+
+
+
+
+
+
+
+
+
+//################# inline implementation #########################
+template <class T> inline
+T abs(T value)
+{
+ //static_assert(std::is_signed<T>::value, "");
+ if (value < 0)
+ return -value; //operator "?:" caveat: may be different type than "value"
+ else
+ return value;
+}
+
+template <class T> inline
+auto dist(T a, T b) //return type might be different than T, e.g. std::chrono::duration instead of std::chrono::time_point
+{
+ return a > b ? a - b : b - a;
+}
+
+
+template <class T> inline
+int sign(T value) //returns one of {-1, 0, 1}
+{
+ static_assert(std::is_signed<T>::value, "");
+ return value < 0 ? -1 : (value > 0 ? 1 : 0);
+}
+
+
+template <class T> inline
+T min(T a, T b, T c) //don't follow std::min's "const T&(const T&, const T&)" API
+{
+ if (a < b)
+ return a < c ? a : c;
+ else
+ return b < c ? b : c;
+ //return std::min(std::min(a, b), c);
+}
+
+
+template <class T> inline
+T max(T a, T b, T c)
+{
+ if (a > b)
+ return a > c ? a : c;
+ else
+ return b > c ? b : c;
+ //return std::max(std::max(a, b), c);
+}
+
+
+template <class T> inline
+T clampCpy(T val, T minVal, T maxVal)
+{
+ assert(minVal <= maxVal);
+ if (val < minVal)
+ return minVal;
+ else if (val > maxVal)
+ return maxVal;
+ return val;
+}
+
+template <class T> inline
+void clamp(T& val, T minVal, T maxVal)
+{
+ assert(minVal <= maxVal);
+ if (val < minVal)
+ val = minVal;
+ else if (val > maxVal)
+ val = maxVal;
+}
+
+
+/*
+part of C++11 now!
+template <class InputIterator, class Compare> inline
+std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last, Compare compLess)
+{
+ //by factor 1.5 to 3 faster than boost::minmax_element (=two-step algorithm) for built-in types!
+
+ InputIterator lowest = first;
+ InputIterator largest = first;
+
+ if (first != last)
+ {
+ auto minVal = *lowest; //nice speedup on 64 bit!
+ auto maxVal = *largest; //
+ for (;;)
+ {
+ ++first;
+ if (first == last)
+ break;
+ const auto val = *first;
+
+ if (compLess(maxVal, val))
+ {
+ largest = first;
+ maxVal = val;
+ }
+ else if (compLess(val, minVal))
+ {
+ lowest = first;
+ minVal = val;
+ }
+ }
+ }
+ return std::make_pair(lowest, largest);
+}
+
+
+template <class InputIterator> inline
+std::pair<InputIterator, InputIterator> minMaxElement(InputIterator first, InputIterator last)
+{
+ return minMaxElement(first, last, std::less<typename std::iterator_traits<InputIterator>::value_type>());
+}
+*/
+
+template <class T, class InputIterator> inline
+auto nearMatch(const T& val, InputIterator first, InputIterator last)
+{
+ if (first == last)
+ return static_cast<decltype(*first)>(0);
+
+ assert(std::is_sorted(first, last));
+ InputIterator it = std::lower_bound(first, last, val);
+ if (it == last)
+ return *--last;
+ if (it == first)
+ return *first;
+
+ const auto nextVal = *it;
+ const auto prevVal = *--it;
+ return val - prevVal < nextVal - val ? prevVal : nextVal;
+}
+
+
+template <class T> inline
+bool isNull(T value)
+{
+ return abs(value) <= std::numeric_limits<T>::epsilon(); //epsilon is 0 für integral types => less-equal
+}
+
+
+inline
+int round(double d)
+{
+ assert(d - 0.5 >= std::numeric_limits<int>::min() && //if double is larger than what int can represent:
+ d + 0.5 <= std::numeric_limits<int>::max()); //=> undefined behavior!
+ return static_cast<int>(d < 0 ? d - 0.5 : d + 0.5);
+}
+
+
+template <class N, class D> inline
+auto integerDivideRoundUp(N numerator, D denominator)
+{
+ static_assert(std::is_integral<N>::value && std::is_unsigned<N>::value, "");
+ static_assert(std::is_integral<D>::value && std::is_unsigned<D>::value, "");
+ assert(denominator > 0);
+ return (numerator + denominator - 1) / denominator;
+}
+
+
+namespace
+{
+template <size_t N, class T> struct PowerImpl;
+/*
+ template <size_t N, class T> -> let's use non-recursive specializations to help the compiler
+ struct PowerImpl { static T result(const T& value) { return PowerImpl<N - 1, T>::result(value) * value; } };
+*/
+template <class T> struct PowerImpl<2, T> { static T result(T value) { return value * value; } };
+template <class T> struct PowerImpl<3, T> { static T result(T value) { return value * value * value; } };
+}
+
+template <size_t n, class T> inline
+T power(T value)
+{
+ return PowerImpl<n, T>::result(value);
+}
+
+
+inline
+double radToDeg(double rad)
+{
+ return rad * 180.0 / numeric::pi;
+}
+
+
+inline
+double degToRad(double degree)
+{
+ return degree * numeric::pi / 180.0;
+}
+
+
+template <class InputIterator> inline
+double arithmeticMean(InputIterator first, InputIterator last)
+{
+ size_t n = 0; //avoid random-access requirement for iterator!
+ double sum_xi = 0;
+
+ for (; first != last; ++first, ++n)
+ sum_xi += *first;
+
+ return n == 0 ? 0 : sum_xi / n;
+}
+
+
+template <class RandomAccessIterator> inline
+double median(RandomAccessIterator first, RandomAccessIterator last) //note: invalidates input range!
+{
+ const size_t n = last - first;
+ if (n > 0)
+ {
+ std::nth_element(first, first + n / 2, last); //complexity: O(n)
+ const double midVal = *(first + n / 2);
+
+ if (n % 2 != 0)
+ return midVal;
+ else //n is even and >= 2 in this context: return mean of two middle values
+ return 0.5 * (*std::max_element(first, first + n / 2) + midVal); //this operation is the reason why median() CANNOT support a comparison predicate!!!
+ }
+ return 0;
+}
+
+
+template <class RandomAccessIterator> inline
+double mad(RandomAccessIterator first, RandomAccessIterator last) //note: invalidates input range!
+{
+ //http://en.wikipedia.org/wiki/Median_absolute_deviation
+
+ const size_t n = last - first;
+ if (n > 0)
+ {
+ const double m = median(first, last);
+
+ //the second median needs to operate on absolute residuals => avoid transforming input range which may have less than double precision!
+
+ auto lessMedAbs = [m](double lhs, double rhs) { return abs(lhs - m) < abs(rhs - m); };
+
+ std::nth_element(first, first + n / 2, last, lessMedAbs); //complexity: O(n)
+ const double midVal = abs(*(first + n / 2) - m);
+
+ if (n % 2 != 0)
+ return midVal;
+ else //n is even and >= 2 in this context: return mean of two middle values
+ return 0.5 * (abs(*std::max_element(first, first + n / 2, lessMedAbs) - m) + midVal);
+ }
+ return 0;
+}
+
+
+template <class InputIterator> inline
+double stdDeviation(InputIterator first, InputIterator last, double* arithMean)
+{
+ //implementation minimizing rounding errors, see: http://en.wikipedia.org/wiki/Standard_deviation
+ //combined with technique avoiding overflow, see: http://www.netlib.org/blas/dnrm2.f -> only 10% performance degradation
+
+ size_t n = 0;
+ double mean = 0;
+ double q = 0;
+ double scale = 1;
+
+ for (; first != last; ++first)
+ {
+ ++n;
+ const double val = *first - mean;
+
+ if (abs(val) > scale)
+ {
+ q = (n - 1.0) / n + q * power<2>(scale / val);
+ scale = abs(val);
+ }
+ else
+ q += (n - 1.0) * power<2>(val / scale) / n;
+
+ mean += val / n;
+ }
+
+ if (arithMean)
+ *arithMean = mean;
+
+ return n <= 1 ? 0 : std::sqrt(q / (n - 1)) * scale;
+}
+
+
+template <class InputIterator> inline
+double norm2(InputIterator first, InputIterator last)
+{
+ double result = 0;
+ double scale = 1;
+ for (; first != last; ++first)
+ {
+ const double tmp = abs(*first);
+ if (tmp > scale)
+ {
+ result = 1 + result * power<2>(scale / tmp);
+ scale = tmp;
+ }
+ else
+ result += power<2>(tmp / scale);
+ }
+ return std::sqrt(result) * scale;
+}
+}
+
+#endif //BASIC_MATH_H_3472639843265675
diff --git a/zen/build_info.h b/zen/build_info.h
index f3f7f5b1..8354f492 100755
--- a/zen/build_info.h
+++ b/zen/build_info.h
@@ -1,29 +1,29 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef BUILD_INFO_H_5928539285603428657
-#define BUILD_INFO_H_5928539285603428657
-
-namespace zen
-{
-//determine build info: defines ZEN_BUILD_32BIT or ZEN_BUILD_64BIT
-
- #ifdef __LP64__
- #define ZEN_BUILD_64BIT
- #else
- #define ZEN_BUILD_32BIT
- #endif
-
-#ifdef ZEN_BUILD_32BIT
- static_assert(sizeof(void*) == 4, "");
-#endif
-
-#ifdef ZEN_BUILD_64BIT
- static_assert(sizeof(void*) == 8, "");
-#endif
-}
-
-#endif //BUILD_INFO_H_5928539285603428657
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef BUILD_INFO_H_5928539285603428657
+#define BUILD_INFO_H_5928539285603428657
+
+namespace zen
+{
+//determine build info: defines ZEN_BUILD_32BIT or ZEN_BUILD_64BIT
+
+ #ifdef __LP64__
+ #define ZEN_BUILD_64BIT
+ #else
+ #define ZEN_BUILD_32BIT
+ #endif
+
+#ifdef ZEN_BUILD_32BIT
+ static_assert(sizeof(void*) == 4, "");
+#endif
+
+#ifdef ZEN_BUILD_64BIT
+ static_assert(sizeof(void*) == 8, "");
+#endif
+}
+
+#endif //BUILD_INFO_H_5928539285603428657
diff --git a/zen/crc.h b/zen/crc.h
index b4d53d12..67f0460e 100755
--- a/zen/crc.h
+++ b/zen/crc.h
@@ -1,54 +1,54 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef CRC_H_23489275827847235
-#define CRC_H_23489275827847235
-
-#include <boost/crc.hpp>
-
-
-namespace zen
-{
-uint16_t getCrc16(const std::string& str);
-uint32_t getCrc32(const std::string& str);
-template <class ByteIterator> uint16_t getCrc16(ByteIterator first, ByteIterator last);
-template <class ByteIterator> uint32_t getCrc32(ByteIterator first, ByteIterator last);
-
-
-
-
-//------------------------- implementation -------------------------------
-inline uint16_t getCrc16(const std::string& str) { return getCrc16(str.begin(), str.end()); }
-inline uint32_t getCrc32(const std::string& str) { return getCrc32(str.begin(), str.end()); }
-
-
-template <class ByteIterator> inline
-uint16_t getCrc16(ByteIterator first, ByteIterator last)
-{
- static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
- boost::crc_16_type result;
- if (first != last)
- result.process_bytes(&*first, last - first);
- auto rv = result.checksum();
- static_assert(sizeof(rv) == sizeof(uint16_t), "");
- return rv;
-}
-
-
-template <class ByteIterator> inline
-uint32_t getCrc32(ByteIterator first, ByteIterator last)
-{
- static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
- boost::crc_32_type result;
- if (first != last)
- result.process_bytes(&*first, last - first);
- auto rv = result.checksum();
- static_assert(sizeof(rv) == sizeof(uint32_t), "");
- return rv;
-}
-}
-
-#endif //CRC_H_23489275827847235
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef CRC_H_23489275827847235
+#define CRC_H_23489275827847235
+
+#include <boost/crc.hpp>
+
+
+namespace zen
+{
+uint16_t getCrc16(const std::string& str);
+uint32_t getCrc32(const std::string& str);
+template <class ByteIterator> uint16_t getCrc16(ByteIterator first, ByteIterator last);
+template <class ByteIterator> uint32_t getCrc32(ByteIterator first, ByteIterator last);
+
+
+
+
+//------------------------- implementation -------------------------------
+inline uint16_t getCrc16(const std::string& str) { return getCrc16(str.begin(), str.end()); }
+inline uint32_t getCrc32(const std::string& str) { return getCrc32(str.begin(), str.end()); }
+
+
+template <class ByteIterator> inline
+uint16_t getCrc16(ByteIterator first, ByteIterator last)
+{
+ static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
+ boost::crc_16_type result;
+ if (first != last)
+ result.process_bytes(&*first, last - first);
+ auto rv = result.checksum();
+ static_assert(sizeof(rv) == sizeof(uint16_t), "");
+ return rv;
+}
+
+
+template <class ByteIterator> inline
+uint32_t getCrc32(ByteIterator first, ByteIterator last)
+{
+ static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
+ boost::crc_32_type result;
+ if (first != last)
+ result.process_bytes(&*first, last - first);
+ auto rv = result.checksum();
+ static_assert(sizeof(rv) == sizeof(uint32_t), "");
+ return rv;
+}
+}
+
+#endif //CRC_H_23489275827847235
diff --git a/zen/deprecate.h b/zen/deprecate.h
index 94273ad8..c2a1ebfa 100755
--- a/zen/deprecate.h
+++ b/zen/deprecate.h
@@ -1,14 +1,14 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef DEPRECATE_H_234897087787348
-#define DEPRECATE_H_234897087787348
-
-//compiler macros: http://predef.sourceforge.net/precomp.html
- #define ZEN_DEPRECATE __attribute__ ((deprecated))
-
-
-#endif //DEPRECATE_H_234897087787348
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef DEPRECATE_H_234897087787348
+#define DEPRECATE_H_234897087787348
+
+//compiler macros: http://predef.sourceforge.net/precomp.html
+ #define ZEN_DEPRECATE __attribute__ ((deprecated))
+
+
+#endif //DEPRECATE_H_234897087787348
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 1b6f6f5c..4e759d55 100755
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -1,158 +1,158 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "dir_watcher.h"
-#include <algorithm>
-#include <set>
-#include "thread.h"
-#include "scope_guard.h"
-#include "basic_math.h"
-
- #include <map>
- #include <sys/inotify.h>
- #include <fcntl.h> //fcntl
- #include <unistd.h> //close
- #include <limits.h> //NAME_MAX
- #include "file_traverser.h"
-
-
-using namespace zen;
-
-
-struct DirWatcher::Impl
-{
- int notifDescr = 0;
- std::map<int, Zstring> watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr"
-};
-
-
-DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
- baseDirPath(dirPath),
- pimpl_(std::make_unique<Impl>())
-{
- //get all subdirectories
- std::vector<Zstring> fullFolderList { baseDirPath };
- {
- std::function<void (const Zstring& path)> traverse;
-
- traverse = [&traverse, &fullFolderList](const Zstring& path)
- {
- traverseFolder(path, nullptr,
- [&](const FolderInfo& fi ) { fullFolderList.push_back(fi.fullPath); traverse(fi.fullPath); },
- nullptr, //don't traverse into symlinks (analog to windows build)
- [&](const std::wstring& errorMsg) { throw FileError(errorMsg); });
- };
-
- traverse(baseDirPath);
- }
-
- //init
- pimpl_->notifDescr = ::inotify_init();
- if (pimpl_->notifDescr == -1)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"inotify_init");
-
- ZEN_ON_SCOPE_FAIL( ::close(pimpl_->notifDescr); );
-
- //set non-blocking mode
- bool initSuccess = false;
- {
- int flags = ::fcntl(pimpl_->notifDescr, F_GETFL);
- if (flags != -1)
- initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1;
- }
- if (!initSuccess)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"fcntl");
-
- //add watches
- for (const Zstring& subDirPath : fullFolderList)
- {
- int wd = ::inotify_add_watch(pimpl_->notifDescr, subDirPath.c_str(),
- IN_ONLYDIR | //"Only watch pathname if it is a directory."
- IN_DONT_FOLLOW | //don't follow symbolic links
- IN_CREATE |
- IN_MODIFY |
- IN_CLOSE_WRITE |
- IN_DELETE |
- IN_DELETE_SELF |
- IN_MOVED_FROM |
- IN_MOVED_TO |
- IN_MOVE_SELF);
- if (wd == -1)
- {
- const ErrorCode ec = getLastError(); //copy before directly/indirectly making other system calls!
- if (ec == ENOSPC) //fix misleading system message "No space left on device"
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)),
- formatSystemError(L"inotify_add_watch", numberTo<std::wstring>(ec), L"The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource."));
-
- throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)), formatSystemError(L"inotify_add_watch", ec));
- }
-
- pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath));
- }
-}
-
-
-DirWatcher::~DirWatcher()
-{
- ::close(pimpl_->notifDescr); //associated watches are removed automatically!
-}
-
-
-std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>&) //throw FileError
-{
- std::vector<char> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1));
-
- ssize_t bytesRead = 0;
- do
- {
- //non-blocking call, see O_NONBLOCK
- bytesRead = ::read(pimpl_->notifDescr, &buffer[0], buffer.size());
- }
- while (bytesRead < 0 && errno == EINTR); //"Interrupted function call; When this happens, you should try the call again."
-
- if (bytesRead < 0)
- {
- if (errno == EAGAIN) //this error is ignored in all inotify wrappers I found
- return std::vector<Entry>();
-
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"read");
- }
-
- std::vector<Entry> output;
-
- ssize_t bytePos = 0;
- while (bytePos < bytesRead)
- {
- struct ::inotify_event& evt = reinterpret_cast<struct ::inotify_event&>(buffer[bytePos]);
-
- if (evt.len != 0) //exclude case: deletion of "self", already reported by parent directory watch
- {
- auto it = pimpl_->watchDescrs.find(evt.wd);
- if (it != pimpl_->watchDescrs.end())
- {
- //Note: evt.len is NOT the size of the evt.name c-string, but the array size including all padding 0 characters!
- //It may be even 0 in which case evt.name must not be used!
- const Zstring fullname = it->second + evt.name;
-
- if ((evt.mask & IN_CREATE) ||
- (evt.mask & IN_MOVED_TO))
- output.emplace_back(ACTION_CREATE, fullname);
- else if ((evt.mask & IN_MODIFY) ||
- (evt.mask & IN_CLOSE_WRITE))
- output.emplace_back(ACTION_UPDATE, fullname);
- else if ((evt.mask & IN_DELETE ) ||
- (evt.mask & IN_DELETE_SELF) ||
- (evt.mask & IN_MOVE_SELF ) ||
- (evt.mask & IN_MOVED_FROM))
- output.emplace_back(ACTION_DELETE, fullname);
- }
- }
- bytePos += sizeof(struct ::inotify_event) + evt.len;
- }
-
- return output;
-}
-
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "dir_watcher.h"
+#include <algorithm>
+#include <set>
+#include "thread.h"
+#include "scope_guard.h"
+#include "basic_math.h"
+
+ #include <map>
+ #include <sys/inotify.h>
+ #include <fcntl.h> //fcntl
+ #include <unistd.h> //close
+ #include <limits.h> //NAME_MAX
+ #include "file_traverser.h"
+
+
+using namespace zen;
+
+
+struct DirWatcher::Impl
+{
+ int notifDescr = 0;
+ std::map<int, Zstring> watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr"
+};
+
+
+DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
+ baseDirPath(dirPath),
+ pimpl_(std::make_unique<Impl>())
+{
+ //get all subdirectories
+ std::vector<Zstring> fullFolderList { baseDirPath };
+ {
+ std::function<void (const Zstring& path)> traverse;
+
+ traverse = [&traverse, &fullFolderList](const Zstring& path)
+ {
+ traverseFolder(path, nullptr,
+ [&](const FolderInfo& fi ) { fullFolderList.push_back(fi.fullPath); traverse(fi.fullPath); },
+ nullptr, //don't traverse into symlinks (analog to windows build)
+ [&](const std::wstring& errorMsg) { throw FileError(errorMsg); });
+ };
+
+ traverse(baseDirPath);
+ }
+
+ //init
+ pimpl_->notifDescr = ::inotify_init();
+ if (pimpl_->notifDescr == -1)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"inotify_init");
+
+ ZEN_ON_SCOPE_FAIL( ::close(pimpl_->notifDescr); );
+
+ //set non-blocking mode
+ bool initSuccess = false;
+ {
+ int flags = ::fcntl(pimpl_->notifDescr, F_GETFL);
+ if (flags != -1)
+ initSuccess = ::fcntl(pimpl_->notifDescr, F_SETFL, flags | O_NONBLOCK) != -1;
+ }
+ if (!initSuccess)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"fcntl");
+
+ //add watches
+ for (const Zstring& subDirPath : fullFolderList)
+ {
+ int wd = ::inotify_add_watch(pimpl_->notifDescr, subDirPath.c_str(),
+ IN_ONLYDIR | //"Only watch pathname if it is a directory."
+ IN_DONT_FOLLOW | //don't follow symbolic links
+ IN_CREATE |
+ IN_MODIFY |
+ IN_CLOSE_WRITE |
+ IN_DELETE |
+ IN_DELETE_SELF |
+ IN_MOVED_FROM |
+ IN_MOVED_TO |
+ IN_MOVE_SELF);
+ if (wd == -1)
+ {
+ const ErrorCode ec = getLastError(); //copy before directly/indirectly making other system calls!
+ if (ec == ENOSPC) //fix misleading system message "No space left on device"
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)),
+ formatSystemError(L"inotify_add_watch", numberTo<std::wstring>(ec), L"The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource."));
+
+ throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)), formatSystemError(L"inotify_add_watch", ec));
+ }
+
+ pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath));
+ }
+}
+
+
+DirWatcher::~DirWatcher()
+{
+ ::close(pimpl_->notifDescr); //associated watches are removed automatically!
+}
+
+
+std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()>&) //throw FileError
+{
+ std::vector<char> buffer(512 * (sizeof(struct ::inotify_event) + NAME_MAX + 1));
+
+ ssize_t bytesRead = 0;
+ do
+ {
+ //non-blocking call, see O_NONBLOCK
+ bytesRead = ::read(pimpl_->notifDescr, &buffer[0], buffer.size());
+ }
+ while (bytesRead < 0 && errno == EINTR); //"Interrupted function call; When this happens, you should try the call again."
+
+ if (bytesRead < 0)
+ {
+ if (errno == EAGAIN) //this error is ignored in all inotify wrappers I found
+ return std::vector<Entry>();
+
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(baseDirPath)), L"read");
+ }
+
+ std::vector<Entry> output;
+
+ ssize_t bytePos = 0;
+ while (bytePos < bytesRead)
+ {
+ struct ::inotify_event& evt = reinterpret_cast<struct ::inotify_event&>(buffer[bytePos]);
+
+ if (evt.len != 0) //exclude case: deletion of "self", already reported by parent directory watch
+ {
+ auto it = pimpl_->watchDescrs.find(evt.wd);
+ if (it != pimpl_->watchDescrs.end())
+ {
+ //Note: evt.len is NOT the size of the evt.name c-string, but the array size including all padding 0 characters!
+ //It may be even 0 in which case evt.name must not be used!
+ const Zstring fullname = it->second + evt.name;
+
+ if ((evt.mask & IN_CREATE) ||
+ (evt.mask & IN_MOVED_TO))
+ output.emplace_back(ACTION_CREATE, fullname);
+ else if ((evt.mask & IN_MODIFY) ||
+ (evt.mask & IN_CLOSE_WRITE))
+ output.emplace_back(ACTION_UPDATE, fullname);
+ else if ((evt.mask & IN_DELETE ) ||
+ (evt.mask & IN_DELETE_SELF) ||
+ (evt.mask & IN_MOVE_SELF ) ||
+ (evt.mask & IN_MOVED_FROM))
+ output.emplace_back(ACTION_DELETE, fullname);
+ }
+ }
+ bytePos += sizeof(struct ::inotify_event) + evt.len;
+ }
+
+ return output;
+}
+
diff --git a/zen/dir_watcher.h b/zen/dir_watcher.h
index b16cd417..5676555f 100755
--- a/zen/dir_watcher.h
+++ b/zen/dir_watcher.h
@@ -1,75 +1,75 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef DIR_WATCHER_348577025748023458
-#define DIR_WATCHER_348577025748023458
-
-#include <vector>
-#include <memory>
-#include <functional>
-#include "file_error.h"
-
-
-namespace zen
-{
-//Windows: ReadDirectoryChangesW https://msdn.microsoft.com/en-us/library/aa365465
-//Linux: inotify http://linux.die.net/man/7/inotify
-//OS X: kqueue http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/kqueue.2.html
-
-//watch directory including subdirectories
-/*
-!Note handling of directories!:
- Windows: removal of top watched directory is NOT notified when watching the dir handle, e.g. brute force usb stick removal,
- (watchting for GUID_DEVINTERFACE_WPD OTOH works fine!)
- however manual unmount IS notified (e.g. usb stick removal, then re-insert), but watching is stopped!
- Renaming of top watched directory handled incorrectly: Not notified(!) + additional changes in subfolders
- now do report FILE_ACTION_MODIFIED for directory (check that should prevent this fails!)
-
- Linux: newly added subdirectories are reported but not automatically added for watching! -> reset Dirwatcher!
- removal of top watched directory is NOT notified!
-
- OS X: everything works as expected; renaming of top level folder is also detected
-
- Overcome all issues portably: check existence of top watched directory externally + reinstall watch after changes in directory structure (added directories) are detected
-*/
-class DirWatcher
-{
-public:
- DirWatcher(const Zstring& dirPath); //throw FileError
- ~DirWatcher();
-
- enum ActionType
- {
- ACTION_CREATE, //informal only!
- ACTION_UPDATE, //use for debugging/logging only!
- ACTION_DELETE, //
- };
-
- struct Entry
- {
- Entry() {}
- Entry(ActionType action, const Zstring& filepath) : action_(action), filepath_(filepath) {}
-
- ActionType action_ = ACTION_CREATE;
- Zstring filepath_;
- };
-
- //extract accumulated changes since last call
- std::vector<Entry> getChanges(const std::function<void()>& processGuiMessages); //throw FileError
-
-private:
- DirWatcher (const DirWatcher&) = delete;
- DirWatcher& operator=(const DirWatcher&) = delete;
-
- const Zstring baseDirPath;
-
- struct Impl;
- const std::unique_ptr<Impl> pimpl_;
-};
-
-}
-
-#endif
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef DIR_WATCHER_348577025748023458
+#define DIR_WATCHER_348577025748023458
+
+#include <vector>
+#include <memory>
+#include <functional>
+#include "file_error.h"
+
+
+namespace zen
+{
+//Windows: ReadDirectoryChangesW https://msdn.microsoft.com/en-us/library/aa365465
+//Linux: inotify http://linux.die.net/man/7/inotify
+//OS X: kqueue http://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/kqueue.2.html
+
+//watch directory including subdirectories
+/*
+!Note handling of directories!:
+ Windows: removal of top watched directory is NOT notified when watching the dir handle, e.g. brute force usb stick removal,
+ (watchting for GUID_DEVINTERFACE_WPD OTOH works fine!)
+ however manual unmount IS notified (e.g. usb stick removal, then re-insert), but watching is stopped!
+ Renaming of top watched directory handled incorrectly: Not notified(!) + additional changes in subfolders
+ now do report FILE_ACTION_MODIFIED for directory (check that should prevent this fails!)
+
+ Linux: newly added subdirectories are reported but not automatically added for watching! -> reset Dirwatcher!
+ removal of top watched directory is NOT notified!
+
+ OS X: everything works as expected; renaming of top level folder is also detected
+
+ Overcome all issues portably: check existence of top watched directory externally + reinstall watch after changes in directory structure (added directories) are detected
+*/
+class DirWatcher
+{
+public:
+ DirWatcher(const Zstring& dirPath); //throw FileError
+ ~DirWatcher();
+
+ enum ActionType
+ {
+ ACTION_CREATE, //informal only!
+ ACTION_UPDATE, //use for debugging/logging only!
+ ACTION_DELETE, //
+ };
+
+ struct Entry
+ {
+ Entry() {}
+ Entry(ActionType action, const Zstring& filepath) : action_(action), filepath_(filepath) {}
+
+ ActionType action_ = ACTION_CREATE;
+ Zstring filepath_;
+ };
+
+ //extract accumulated changes since last call
+ std::vector<Entry> getChanges(const std::function<void()>& processGuiMessages); //throw FileError
+
+private:
+ DirWatcher (const DirWatcher&) = delete;
+ DirWatcher& operator=(const DirWatcher&) = delete;
+
+ const Zstring baseDirPath;
+
+ struct Impl;
+ const std::unique_ptr<Impl> pimpl_;
+};
+
+}
+
+#endif
diff --git a/zen/error_log.h b/zen/error_log.h
index 1a9d2679..90016666 100755
--- a/zen/error_log.h
+++ b/zen/error_log.h
@@ -1,135 +1,135 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef ERROR_LOG_H_8917590832147915
-#define ERROR_LOG_H_8917590832147915
-
-#include <cassert>
-#include <algorithm>
-#include <vector>
-#include <string>
-#include "time.h"
-#include "i18n.h"
-#include "string_base.h"
-
-
-namespace zen
-{
-enum MessageType
-{
- TYPE_INFO = 0x1,
- TYPE_WARNING = 0x2,
- TYPE_ERROR = 0x4,
- TYPE_FATAL_ERROR = 0x8,
-};
-
-using MsgString = Zbase<wchar_t>; //std::wstring may employ small string optimization: we cannot accept bloating the "ErrorLog::entries" memory block below (think 1 million items)
-
-struct LogEntry
-{
- time_t time;
- MessageType type;
- MsgString message;
-};
-
-template <class String>
-String formatMessage(const LogEntry& entry);
-
-
-class ErrorLog
-{
-public:
- template <class String> //a wchar_t-based string!
- void logMsg(const String& text, MessageType type);
-
- int getItemCount(int typeFilter = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const;
-
- //subset of std::vector<> interface:
- using const_iterator = std::vector<LogEntry>::const_iterator;
- const_iterator begin() const { return entries.begin(); }
- const_iterator end () const { return entries.end (); }
- bool empty() const { return entries.empty(); }
-
-private:
- std::vector<LogEntry> entries; //list of non-resolved errors and warnings
-};
-
-
-
-
-
-
-
-
-
-//######################## implementation ##########################
-template <class String> inline
-void ErrorLog::logMsg(const String& text, zen::MessageType type)
-{
- const LogEntry newEntry = { std::time(nullptr), type, copyStringTo<MsgString>(text) };
- entries.push_back(newEntry);
-}
-
-
-inline
-int ErrorLog::getItemCount(int typeFilter) const
-{
- return static_cast<int>(std::count_if(entries.begin(), entries.end(), [&](const LogEntry& e) { return e.type & typeFilter; }));
-}
-
-
-namespace
-{
-template <class String>
-String formatMessageImpl(const LogEntry& entry) //internal linkage
-{
- auto getTypeName = [&]
- {
- switch (entry.type)
- {
- case TYPE_INFO:
- return _("Info");
- case TYPE_WARNING:
- return _("Warning");
- case TYPE_ERROR:
- return _("Error");
- case TYPE_FATAL_ERROR:
- return _("Serious Error");
- }
- assert(false);
- return std::wstring();
- };
-
- String formattedText = L"[" + formatTime<String>(FORMAT_TIME, getLocalTime(entry.time)) + L"] " + copyStringTo<String>(getTypeName()) + L": ";
- const size_t prefixLen = formattedText.size();
-
- for (auto it = entry.message.begin(); it != entry.message.end(); )
- if (*it == L'\n')
- {
- formattedText += L'\n';
-
- String blanks;
- blanks.resize(prefixLen, L' ');
- formattedText += blanks;
-
- do //skip duplicate newlines
- {
- ++it;
- }
- while (it != entry.message.end() && *it == L'\n');
- }
- else
- formattedText += *it++;
-
- return formattedText;
-}
-}
-
-template <class String> inline
-String formatMessage(const LogEntry& entry) { return formatMessageImpl<String>(entry); }
-}
-
-#endif //ERROR_LOG_H_8917590832147915
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef ERROR_LOG_H_8917590832147915
+#define ERROR_LOG_H_8917590832147915
+
+#include <cassert>
+#include <algorithm>
+#include <vector>
+#include <string>
+#include "time.h"
+#include "i18n.h"
+#include "string_base.h"
+
+
+namespace zen
+{
+enum MessageType
+{
+ TYPE_INFO = 0x1,
+ TYPE_WARNING = 0x2,
+ TYPE_ERROR = 0x4,
+ TYPE_FATAL_ERROR = 0x8,
+};
+
+using MsgString = Zbase<wchar_t>; //std::wstring may employ small string optimization: we cannot accept bloating the "ErrorLog::entries" memory block below (think 1 million items)
+
+struct LogEntry
+{
+ time_t time;
+ MessageType type;
+ MsgString message;
+};
+
+template <class String>
+String formatMessage(const LogEntry& entry);
+
+
+class ErrorLog
+{
+public:
+ template <class String> //a wchar_t-based string!
+ void logMsg(const String& text, MessageType type);
+
+ int getItemCount(int typeFilter = TYPE_INFO | TYPE_WARNING | TYPE_ERROR | TYPE_FATAL_ERROR) const;
+
+ //subset of std::vector<> interface:
+ using const_iterator = std::vector<LogEntry>::const_iterator;
+ const_iterator begin() const { return entries.begin(); }
+ const_iterator end () const { return entries.end (); }
+ bool empty() const { return entries.empty(); }
+
+private:
+ std::vector<LogEntry> entries; //list of non-resolved errors and warnings
+};
+
+
+
+
+
+
+
+
+
+//######################## implementation ##########################
+template <class String> inline
+void ErrorLog::logMsg(const String& text, zen::MessageType type)
+{
+ const LogEntry newEntry = { std::time(nullptr), type, copyStringTo<MsgString>(text) };
+ entries.push_back(newEntry);
+}
+
+
+inline
+int ErrorLog::getItemCount(int typeFilter) const
+{
+ return static_cast<int>(std::count_if(entries.begin(), entries.end(), [&](const LogEntry& e) { return e.type & typeFilter; }));
+}
+
+
+namespace
+{
+template <class String>
+String formatMessageImpl(const LogEntry& entry) //internal linkage
+{
+ auto getTypeName = [&]
+ {
+ switch (entry.type)
+ {
+ case TYPE_INFO:
+ return _("Info");
+ case TYPE_WARNING:
+ return _("Warning");
+ case TYPE_ERROR:
+ return _("Error");
+ case TYPE_FATAL_ERROR:
+ return _("Serious Error");
+ }
+ assert(false);
+ return std::wstring();
+ };
+
+ String formattedText = L"[" + formatTime<String>(FORMAT_TIME, getLocalTime(entry.time)) + L"] " + copyStringTo<String>(getTypeName()) + L": ";
+ const size_t prefixLen = formattedText.size();
+
+ for (auto it = entry.message.begin(); it != entry.message.end(); )
+ if (*it == L'\n')
+ {
+ formattedText += L'\n';
+
+ String blanks;
+ blanks.resize(prefixLen, L' ');
+ formattedText += blanks;
+
+ do //skip duplicate newlines
+ {
+ ++it;
+ }
+ while (it != entry.message.end() && *it == L'\n');
+ }
+ else
+ formattedText += *it++;
+
+ return formattedText;
+}
+}
+
+template <class String> inline
+String formatMessage(const LogEntry& entry) { return formatMessageImpl<String>(entry); }
+}
+
+#endif //ERROR_LOG_H_8917590832147915
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 71d00386..ed66aac4 100755
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -1,676 +1,676 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "file_access.h"
-#include <map>
-#include <algorithm>
-#include <stdexcept>
-#include "file_traverser.h"
-#include "scope_guard.h"
-#include "symlink_target.h"
-#include "file_id_def.h"
-#include "file_io.h"
-
- #include <sys/vfs.h> //statfs
- #include <sys/time.h> //lutimes
- #ifdef HAVE_SELINUX
- #include <selinux/selinux.h>
- #endif
-
-
- #include <fcntl.h> //open, close, AT_SYMLINK_NOFOLLOW, UTIME_OMIT
- #include <sys/stat.h>
-
-using namespace zen;
-
-
-Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
-{
- if (startsWith(itemPath, "/"))
- {
- Zstring relPath(itemPath.c_str() + 1);
- if (endsWith(relPath, FILE_NAME_SEPARATOR))
- relPath.pop_back();
- return PathComponents({ "/", relPath });
- }
- //we do NOT support relative paths!
- return NoValue();
-}
-
-
-
-Opt<Zstring> zen::getParentFolderPath(const Zstring& itemPath)
-{
- if (const Opt<PathComponents> comp = parsePathComponents(itemPath))
- {
- if (comp->relPath.empty())
- return NoValue();
-
- const Zstring parentRelPath = beforeLast(comp->relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
- if (parentRelPath.empty())
- return comp->rootPath;
- return appendSeparator(comp->rootPath) + parentRelPath;
- }
- assert(false);
- return NoValue();
-}
-
-
-ItemType zen::getItemType(const Zstring& itemPath) //throw FileError
-{
- struct ::stat itemInfo = {};
- if (::lstat(itemPath.c_str(), &itemInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat");
-
- if (S_ISLNK(itemInfo.st_mode))
- return ItemType::SYMLINK;
- if (S_ISDIR(itemInfo.st_mode))
- return ItemType::FOLDER;
- return ItemType::FILE; //S_ISREG || S_ISCHR || S_ISBLK || S_ISFIFO || S_ISSOCK
-}
-
-
-PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError
-{
- const Opt<Zstring> parentPath = getParentFolderPath(itemPath);
- try
- {
- return { getItemType(itemPath), itemPath, {} }; //throw FileError
- }
- catch (FileError&)
- {
- if (!parentPath) //device root
- throw;
- //else: let's dig deeper... don't bother checking Win32 codes; e.g. not existing item may have the codes:
- // ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_NAME, ERROR_INVALID_DRIVE,
- // ERROR_NOT_READY, ERROR_INVALID_PARAMETER, ERROR_BAD_PATHNAME, ERROR_BAD_NETPATH => not reliable
- }
- const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
- assert(!itemName.empty());
-
- PathStatus pd = getPathStatus(*parentPath); //throw FileError
- if (!pd.relPath.empty())
- {
- pd.relPath.push_back(itemName);
- return { pd.existingType, pd.existingPath, pd.relPath };
- }
-
- try
- {
- traverseFolder(*parentPath,
- [&](const FileInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FILE; },
- [&](const FolderInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FOLDER; },
- [&](const SymlinkInfo& si) { if (equalFilePath(si.itemName, itemName)) throw ItemType::SYMLINK; },
- [](const std::wstring& errorMsg) { throw FileError(errorMsg); });
-
- return { pd.existingType, *parentPath, { itemName } }; //throw FileError
- }
- catch (const ItemType& type) { return { type, itemPath, {} }; } //yes, exceptions for control-flow are bad design... but, but...
- //we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found)
-}
-
-
-Opt<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError
-{
- const PathStatus pd = getPathStatus(itemPath); //throw FileError
- if (pd.relPath.empty())
- return pd.existingType;
- return NoValue();
-}
-
-
-bool zen::fileAvailable(const Zstring& filePath) //noexcept
-{
- //symbolic links (broken or not) are also treated as existing files!
- struct ::stat fileInfo = {};
- if (::stat(filePath.c_str(), &fileInfo) == 0) //follow symlinks!
- return S_ISREG(fileInfo.st_mode);
- return false;
-}
-
-
-bool zen::dirAvailable(const Zstring& dirPath) //noexcept
-{
- //symbolic links (broken or not) are also treated as existing directories!
- struct ::stat dirInfo = {};
- if (::stat(dirPath.c_str(), &dirInfo) == 0) //follow symlinks!
- return S_ISDIR(dirInfo.st_mode);
- return false;
-}
-
-
-bool zen::itemNotExisting(const Zstring& itemPath)
-{
- try
- {
- return !getItemTypeIfExists(itemPath); //throw FileError
- }
- catch (FileError&) { return false; }
-}
-
-
-namespace
-{
-}
-
-
-uint64_t zen::getFileSize(const Zstring& filePath) //throw FileError
-{
- struct ::stat fileInfo = {};
- if (::stat(filePath.c_str(), &fileInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"stat");
-
- return fileInfo.st_size;
-}
-
-
-uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0 if not available
-{
- struct ::statfs info = {};
- if (::statfs(path.c_str(), &info) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), L"statfs");
-
- return static_cast<uint64_t>(info.f_bsize) * info.f_bavail;
-}
-
-
-VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError
-{
- struct ::stat fileInfo = {};
- if (::stat(itemPath.c_str(), &fileInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"stat");
-
- return fileInfo.st_dev;
-}
-
-
-Zstring zen::getTempFolderPath() //throw FileError
-{
- const char* buf = ::getenv("TMPDIR"); //no extended error reporting
- if (!buf)
- throw FileError(_("Cannot get process information."), L"getenv: TMPDIR not found.");
- return buf;
-}
-
-
-void zen::removeFilePlain(const Zstring& filePath) //throw FileError
-{
- const wchar_t functionName[] = L"unlink";
- if (::unlink(filePath.c_str()) != 0)
- {
- ErrorCode ec = getLastError(); //copy before directly/indirectly making other system calls!
- //begin of "regular" error reporting
- std::wstring errorDescr = formatSystemError(functionName, ec);
-
- throw FileError(replaceCpy(_("Cannot delete file %x."), L"%x", fmtPath(filePath)), errorDescr);
- }
-}
-
-
-void zen::removeSymlinkPlain(const Zstring& linkPath) //throw FileError
-{
- removeFilePlain(linkPath); //throw FileError
-}
-
-
-void zen::removeDirectoryPlain(const Zstring& dirPath) //throw FileError
-{
- const wchar_t functionName[] = L"rmdir";
- if (::rmdir(dirPath.c_str()) != 0)
- {
- ErrorCode ec = getLastError(); //copy before making other system calls!
- bool symlinkExists = false;
- try { symlinkExists = getItemType(dirPath) == ItemType::SYMLINK; } /*throw FileError*/ catch (FileError&) {} //previous exception is more relevant
-
- if (symlinkExists)
- {
- if (::unlink(dirPath.c_str()) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), L"unlink");
- return;
- }
- throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(functionName, ec));
- }
- /*
- Windows: may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have
- successfully been *marked* for deletion, but some application still has a handle open!
- e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145
- Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html
- Alternatives: 1. move file/empty folder to some other location, then DeleteFile()/RemoveDirectory()
- 2. use CreateFile/FILE_FLAG_DELETE_ON_CLOSE *without* FILE_SHARE_DELETE instead of DeleteFile() => early failure
- */
-}
-
-
-namespace
-{
-void removeDirectoryImpl(const Zstring& folderPath) //throw FileError
-{
- std::vector<Zstring> filePaths;
- std::vector<Zstring> symlinkPaths;
- std::vector<Zstring> folderPaths;
-
- //get all files and directories from current directory (WITHOUT subdirectories!)
- traverseFolder(folderPath,
- [&](const FileInfo& fi) { filePaths .push_back(fi.fullPath); },
- [&](const FolderInfo& fi) { folderPaths .push_back(fi.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies!
- [&](const SymlinkInfo& si) { symlinkPaths.push_back(si.fullPath); },
- [](const std::wstring& errorMsg) { throw FileError(errorMsg); });
-
- for (const Zstring& filePath : filePaths)
- removeFilePlain(filePath); //throw FileError
-
- for (const Zstring& symlinkPath : symlinkPaths)
- removeSymlinkPlain(symlinkPath); //throw FileError
-
- //delete directories recursively
- for (const Zstring& subFolderPath : folderPaths)
- removeDirectoryImpl(subFolderPath); //throw FileError; call recursively to correctly handle symbolic links
-
- removeDirectoryPlain(folderPath); //throw FileError
-}
-}
-
-
-void zen::removeDirectoryPlainRecursion(const Zstring& dirPath) //throw FileError
-{
- if (getItemType(dirPath) == ItemType::SYMLINK) //throw FileError
- removeSymlinkPlain(dirPath); //throw FileError
- else
- removeDirectoryImpl(dirPath); //throw FileError
-}
-
-
-namespace
-{
-/* Usage overview: (avoid circular pattern!)
-
- renameFile() --> renameFile_sub()
- | /|\
- \|/ |
- Fix8Dot3NameClash()
-*/
-//wrapper for file system rename function:
-void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
-{
- //rename() will never fail with EEXIST, but always (atomically) overwrite!
- //=> equivalent to SetFileInformationByHandle() + FILE_RENAME_INFO::ReplaceIfExists or ::MoveFileEx + MOVEFILE_REPLACE_EXISTING
- //=> Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy
- //=> OS X: no solution
-
- auto throwException = [&](int ec)
- {
- const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget));
- const std::wstring errorDescr = formatSystemError(L"rename", ec);
-
- if (ec == EXDEV)
- throw ErrorDifferentVolume(errorMsg, errorDescr);
- if (ec == EEXIST)
- throw ErrorTargetExisting(errorMsg, errorDescr);
- throw FileError(errorMsg, errorDescr);
- };
-
- if (!equalFilePath(pathSource, pathTarget)) //exception for OS X: changing file name case is not an "already exists" situation!
- {
- bool alreadyExists = true;
- try { /*ItemType type = */getItemType(pathTarget); } /*throw FileError*/ catch (FileError&) { alreadyExists = false; }
-
- if (alreadyExists)
- throwException(EEXIST);
- //else: nothing exists or other error (hopefully ::rename will also fail!)
- }
-
- if (::rename(pathSource.c_str(), pathTarget.c_str()) != 0)
- throwException(errno);
-}
-
-
-}
-
-
-//rename file: no copying!!!
-void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
-{
- try
- {
- renameFile_sub(pathSource, pathTarget); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
- }
- catch (const ErrorTargetExisting&)
- {
- throw;
- }
-}
-
-
-namespace
-{
-void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTime, ProcSymlink procSl) //throw FileError
-{
- /*
- [2013-05-01] sigh, we can't use utimensat() on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit???
- => fallback to "retarded-idiot version"! -- DarkByte
-
- [2015-03-09]
- - cannot reproduce issues with NTFS and utimensat() on Ubuntu
- - utimensat() is supposed to obsolete utime/utimes and is also used by "cp" and "touch"
- => let's give utimensat another chance:
- using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"!
- */
- struct ::timespec newTimes[2] = {};
- newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: http://www.freefilesync.org/forum/viewtopic.php?t=1701
- newTimes[1] = modTime; //modification time
-
- if (procSl == ProcSymlink::FOLLOW)
- {
- //hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP:
- //http://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works
- if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, 0) == 0)
- return;
-
- //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: http://www.freefilesync.org/forum/viewtopic.php?t=387
- const int fdFile = ::open(itemPath.c_str(), O_WRONLY);
- if (fdFile == -1)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"open");
- ZEN_ON_SCOPE_EXIT(::close(fdFile));
-
- if (::futimens(fdFile, newTimes) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"futimens");
- }
- else
- {
- if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, AT_SYMLINK_NOFOLLOW) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"utimensat");
- }
-}
-
-
-}
-
-
-void zen::setFileTime(const Zstring& filePath, int64_t modTime, ProcSymlink procSl) //throw FileError
-{
- struct ::timespec writeTime = {};
- writeTime.tv_sec = modTime;
- setWriteTimeNative(filePath, writeTime, procSl); //throw FileError
-
-}
-
-
-bool zen::supportsPermissions(const Zstring& dirPath) //throw FileError
-{
- return true;
-}
-
-
-namespace
-{
-#ifdef HAVE_SELINUX
-//copy SELinux security context
-void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError
-{
- security_context_t contextSource = nullptr;
- const int rv = procSl == ProcSymlink::FOLLOW ?
- ::getfilecon(source.c_str(), &contextSource) :
- ::lgetfilecon(source.c_str(), &contextSource);
- if (rv < 0)
- {
- if (errno == ENODATA || //no security context (allegedly) is not an error condition on SELinux
- errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem
- return;
-
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtPath(source)), L"getfilecon");
- }
- ZEN_ON_SCOPE_EXIT(::freecon(contextSource));
-
- {
- security_context_t contextTarget = nullptr;
- const int rv2 = procSl == ProcSymlink::FOLLOW ?
- ::getfilecon(target.c_str(), &contextTarget) :
- ::lgetfilecon(target.c_str(), &contextTarget);
- if (rv2 < 0)
- {
- if (errno == EOPNOTSUPP)
- return;
- //else: still try to set security context
- }
- else
- {
- ZEN_ON_SCOPE_EXIT(::freecon(contextTarget));
-
- if (::strcmp(contextSource, contextTarget) == 0) //nothing to do
- return;
- }
- }
-
- const int rv3 = procSl == ProcSymlink::FOLLOW ?
- ::setfilecon(target.c_str(), contextSource) :
- ::lsetfilecon(target.c_str(), contextSource);
- if (rv3 < 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtPath(target)), L"setfilecon");
-}
-#endif
-
-
-//copy permissions for files, directories or symbolic links: requires admin rights
-void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, ProcSymlink procSl) //throw FileError
-{
-
-#ifdef HAVE_SELINUX //copy SELinux security context
- copySecurityContext(sourcePath, targetPath, procSl); //throw FileError
-#endif
-
- struct ::stat fileInfo = {};
- if (procSl == ProcSymlink::FOLLOW)
- {
- if (::stat(sourcePath.c_str(), &fileInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"stat");
-
- if (::chown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chown");
-
- if (::chmod(targetPath.c_str(), fileInfo.st_mode) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod");
- }
- else
- {
- if (::lstat(sourcePath.c_str(), &fileInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"lstat");
-
- if (::lchown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown");
-
- const bool isSymlinkTarget = getItemType(targetPath) == ItemType::SYMLINK; //throw FileError
- if (!isSymlinkTarget && //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod()
- ::chmod(targetPath.c_str(), fileInfo.st_mode) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod");
- }
-
-}
-}
-
-
-void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError
-{
- if (!getParentFolderPath(dirPath)) //device root
- return static_cast<void>(/*ItemType =*/ getItemType(dirPath)); //throw FileError
-
- try
- {
- copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting
- }
- catch (FileError&)
- {
- Opt<PathStatus> pd;
- try { pd = getPathStatus(dirPath); /*throw FileError*/ }
- catch (FileError&) {} //previous exception is more relevant
-
- if (pd && pd->existingType != ItemType::FILE)
- {
- Zstring intermediatePath = pd->existingPath;
- for (const Zstring& itemName : pd->relPath)
- {
- intermediatePath = appendSeparator(intermediatePath) + itemName;
- copyNewDirectory(Zstring(), intermediatePath, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting)
- }
- return;
- }
- throw;
- }
-}
-
-
-//source path is optional (may be empty)
-void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, //throw FileError, ErrorTargetExisting
- bool copyFilePermissions)
-{
- mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; //0777, default for newly created directories
-
- struct ::stat dirInfo = {};
- if (!sourcePath.empty())
- if (::stat(sourcePath.c_str(), &dirInfo) == 0)
- {
- mode = dirInfo.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); //analog to "cp" which copies "mode" (considering umask) by default
- mode |= S_IRWXU; //FFS only: we need full access to copy child items! "cp" seems to apply permissions *after* copying child items
- }
- //=> need copyItemPermissions() only for "chown" and umask-agnostic permissions
-
- if (::mkdir(targetPath.c_str(), mode) != 0)
- {
- const int lastError = errno; //copy before directly or indirectly making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(targetPath));
- const std::wstring errorDescr = formatSystemError(L"mkdir", lastError);
-
- if (lastError == EEXIST)
- throw ErrorTargetExisting(errorMsg, errorDescr);
- //else if (lastError == ENOENT)
- // throw ErrorTargetPathMissing(errorMsg, errorDescr);
- throw FileError(errorMsg, errorDescr);
- }
-
- if (!sourcePath.empty())
- {
-
- ZEN_ON_SCOPE_FAIL(try { removeDirectoryPlain(targetPath); }
- catch (FileError&) {}); //ensure cleanup:
-
- //enforce copying file permissions: it's advertized on GUI...
- if (copyFilePermissions)
- copyItemPermissions(sourcePath, targetPath, ProcSymlink::FOLLOW); //throw FileError
- }
-}
-
-
-void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions) //throw FileError
-{
- const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks
-
- const wchar_t functionName[] = L"symlink";
- if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), functionName);
-
- //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist!
- ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetLink); /*throw FileError*/ }
- catch (FileError&) {});
-
- //file times: essential for sync'ing a symlink: enforce this! (don't just try!)
- struct ::stat sourceInfo = {};
- if (::lstat(sourceLink.c_str(), &sourceInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat");
-
- setWriteTimeNative(targetLink, sourceInfo.st_mtim, ProcSymlink::DIRECT); //throw FileError
-
- if (copyFilePermissions)
- copyItemPermissions(sourceLink, targetLink, ProcSymlink::DIRECT); //throw FileError
-}
-
-
-namespace
-{
-InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting
- const Zstring& targetFile,
- const IOCallback& notifyUnbufferedIO)
-{
- int64_t totalUnbufferedIO = 0;
-
- FileInput fileIn(sourceFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //throw FileError, (ErrorFileLocked -> Windows-only)
-
- struct ::stat sourceInfo = {};
- if (::fstat(fileIn.getHandle(), &sourceInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"fstat");
-
- const mode_t mode = sourceInfo.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); //analog to "cp" which copies "mode" (considering umask) by default
- //it seems we don't need S_IWUSR, not even for the setFileTime() below! (tested with source file having different user/group!)
-
- //=> need copyItemPermissions() only for "chown" and umask-agnostic permissions
- const int fdTarget = ::open(targetFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode);
- if (fdTarget == -1)
- {
- const int ec = errno; //copy before making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile));
- const std::wstring errorDescr = formatSystemError(L"open", ec);
-
- if (ec == EEXIST)
- throw ErrorTargetExisting(errorMsg, errorDescr);
-
- throw FileError(errorMsg, errorDescr);
- }
- ZEN_ON_SCOPE_FAIL( try { removeFilePlain(targetFile); }
- catch (FileError&) {} );
- //place guard AFTER ::open() and BEFORE lifetime of FileOutput:
- //=> don't delete file that existed previously!!!
- FileOutput fileOut(fdTarget, targetFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //pass ownership
-
- bufferedStreamCopy(fileIn, fileOut); //throw FileError, X
- fileOut.flushBuffers(); //throw FileError, X
- struct ::stat targetInfo = {};
- if (::fstat(fileOut.getHandle(), &targetInfo) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"fstat");
-
- //close output file handle before setting file time; also good place to catch errors when closing stream!
- fileOut.finalize(); //throw FileError, (X) essentially a close() since buffers were already flushed
-
- //we cannot set the target file times (::futimes) while the file descriptor is still open after a write operation:
- //this triggers bugs on samba shares where the modification time is set to current time instead.
- //Linux: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236
- // http://comments.gmane.org/gmane.linux.file-systems.cifs/2854
- //OS X: http://www.freefilesync.org/forum/viewtopic.php?t=356
- setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::FOLLOW); //throw FileError
-
- InSyncAttributes newAttrib;
- newAttrib.fileSize = sourceInfo.st_size;
- newAttrib.modificationTime = sourceInfo.st_mtim.tv_sec; //
- newAttrib.sourceFileId = extractFileId(sourceInfo);
- newAttrib.targetFileId = extractFileId(targetInfo);
- return newAttrib;
-}
-
-/* ------------------
- |File Copy Layers|
- ------------------
- copyNewFile
- |
- copyFileOsSpecific (solve 8.3 issue on Windows)
- |
- copyFileWindowsSelectRoutine
- / \
-copyFileWindowsDefault(::CopyFileEx) copyFileWindowsStream(::BackupRead/::BackupWrite)
-*/
-}
-
-
-InSyncAttributes zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked
- const IOCallback& notifyUnbufferedIO)
-{
- const InSyncAttributes attr = copyFileOsSpecific(sourceFile, targetFile, notifyUnbufferedIO); //throw FileError, ErrorTargetExisting, ErrorFileLocked
-
- //at this point we know we created a new file, so it's fine to delete it for cleanup!
- ZEN_ON_SCOPE_FAIL(try { removeFilePlain(targetFile); }
- catch (FileError&) {});
-
- if (copyFilePermissions)
- copyItemPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError
-
- return attr;
-}
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "file_access.h"
+#include <map>
+#include <algorithm>
+#include <stdexcept>
+#include "file_traverser.h"
+#include "scope_guard.h"
+#include "symlink_target.h"
+#include "file_id_def.h"
+#include "file_io.h"
+
+ #include <sys/vfs.h> //statfs
+ #include <sys/time.h> //lutimes
+ #ifdef HAVE_SELINUX
+ #include <selinux/selinux.h>
+ #endif
+
+
+ #include <fcntl.h> //open, close, AT_SYMLINK_NOFOLLOW, UTIME_OMIT
+ #include <sys/stat.h>
+
+using namespace zen;
+
+
+Opt<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
+{
+ if (startsWith(itemPath, "/"))
+ {
+ Zstring relPath(itemPath.c_str() + 1);
+ if (endsWith(relPath, FILE_NAME_SEPARATOR))
+ relPath.pop_back();
+ return PathComponents({ "/", relPath });
+ }
+ //we do NOT support relative paths!
+ return NoValue();
+}
+
+
+
+Opt<Zstring> zen::getParentFolderPath(const Zstring& itemPath)
+{
+ if (const Opt<PathComponents> comp = parsePathComponents(itemPath))
+ {
+ if (comp->relPath.empty())
+ return NoValue();
+
+ const Zstring parentRelPath = beforeLast(comp->relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ if (parentRelPath.empty())
+ return comp->rootPath;
+ return appendSeparator(comp->rootPath) + parentRelPath;
+ }
+ assert(false);
+ return NoValue();
+}
+
+
+ItemType zen::getItemType(const Zstring& itemPath) //throw FileError
+{
+ struct ::stat itemInfo = {};
+ if (::lstat(itemPath.c_str(), &itemInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat");
+
+ if (S_ISLNK(itemInfo.st_mode))
+ return ItemType::SYMLINK;
+ if (S_ISDIR(itemInfo.st_mode))
+ return ItemType::FOLDER;
+ return ItemType::FILE; //S_ISREG || S_ISCHR || S_ISBLK || S_ISFIFO || S_ISSOCK
+}
+
+
+PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError
+{
+ const Opt<Zstring> parentPath = getParentFolderPath(itemPath);
+ try
+ {
+ return { getItemType(itemPath), itemPath, {} }; //throw FileError
+ }
+ catch (FileError&)
+ {
+ if (!parentPath) //device root
+ throw;
+ //else: let's dig deeper... don't bother checking Win32 codes; e.g. not existing item may have the codes:
+ // ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_NAME, ERROR_INVALID_DRIVE,
+ // ERROR_NOT_READY, ERROR_INVALID_PARAMETER, ERROR_BAD_PATHNAME, ERROR_BAD_NETPATH => not reliable
+ }
+ const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+ assert(!itemName.empty());
+
+ PathStatus pd = getPathStatus(*parentPath); //throw FileError
+ if (!pd.relPath.empty())
+ {
+ pd.relPath.push_back(itemName);
+ return { pd.existingType, pd.existingPath, pd.relPath };
+ }
+
+ try
+ {
+ traverseFolder(*parentPath,
+ [&](const FileInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FILE; },
+ [&](const FolderInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FOLDER; },
+ [&](const SymlinkInfo& si) { if (equalFilePath(si.itemName, itemName)) throw ItemType::SYMLINK; },
+ [](const std::wstring& errorMsg) { throw FileError(errorMsg); });
+
+ return { pd.existingType, *parentPath, { itemName } }; //throw FileError
+ }
+ catch (const ItemType& type) { return { type, itemPath, {} }; } //yes, exceptions for control-flow are bad design... but, but...
+ //we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found)
+}
+
+
+Opt<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError
+{
+ const PathStatus pd = getPathStatus(itemPath); //throw FileError
+ if (pd.relPath.empty())
+ return pd.existingType;
+ return NoValue();
+}
+
+
+bool zen::fileAvailable(const Zstring& filePath) //noexcept
+{
+ //symbolic links (broken or not) are also treated as existing files!
+ struct ::stat fileInfo = {};
+ if (::stat(filePath.c_str(), &fileInfo) == 0) //follow symlinks!
+ return S_ISREG(fileInfo.st_mode);
+ return false;
+}
+
+
+bool zen::dirAvailable(const Zstring& dirPath) //noexcept
+{
+ //symbolic links (broken or not) are also treated as existing directories!
+ struct ::stat dirInfo = {};
+ if (::stat(dirPath.c_str(), &dirInfo) == 0) //follow symlinks!
+ return S_ISDIR(dirInfo.st_mode);
+ return false;
+}
+
+
+bool zen::itemNotExisting(const Zstring& itemPath)
+{
+ try
+ {
+ return !getItemTypeIfExists(itemPath); //throw FileError
+ }
+ catch (FileError&) { return false; }
+}
+
+
+namespace
+{
+}
+
+
+uint64_t zen::getFileSize(const Zstring& filePath) //throw FileError
+{
+ struct ::stat fileInfo = {};
+ if (::stat(filePath.c_str(), &fileInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(filePath)), L"stat");
+
+ return fileInfo.st_size;
+}
+
+
+uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0 if not available
+{
+ struct ::statfs info = {};
+ if (::statfs(path.c_str(), &info) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine free disk space for %x."), L"%x", fmtPath(path)), L"statfs");
+
+ return static_cast<uint64_t>(info.f_bsize) * info.f_bavail;
+}
+
+
+VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError
+{
+ struct ::stat fileInfo = {};
+ if (::stat(itemPath.c_str(), &fileInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"stat");
+
+ return fileInfo.st_dev;
+}
+
+
+Zstring zen::getTempFolderPath() //throw FileError
+{
+ const char* buf = ::getenv("TMPDIR"); //no extended error reporting
+ if (!buf)
+ throw FileError(_("Cannot get process information."), L"getenv: TMPDIR not found.");
+ return buf;
+}
+
+
+void zen::removeFilePlain(const Zstring& filePath) //throw FileError
+{
+ const wchar_t functionName[] = L"unlink";
+ if (::unlink(filePath.c_str()) != 0)
+ {
+ ErrorCode ec = getLastError(); //copy before directly/indirectly making other system calls!
+ //begin of "regular" error reporting
+ std::wstring errorDescr = formatSystemError(functionName, ec);
+
+ throw FileError(replaceCpy(_("Cannot delete file %x."), L"%x", fmtPath(filePath)), errorDescr);
+ }
+}
+
+
+void zen::removeSymlinkPlain(const Zstring& linkPath) //throw FileError
+{
+ removeFilePlain(linkPath); //throw FileError
+}
+
+
+void zen::removeDirectoryPlain(const Zstring& dirPath) //throw FileError
+{
+ const wchar_t functionName[] = L"rmdir";
+ if (::rmdir(dirPath.c_str()) != 0)
+ {
+ ErrorCode ec = getLastError(); //copy before making other system calls!
+ bool symlinkExists = false;
+ try { symlinkExists = getItemType(dirPath) == ItemType::SYMLINK; } /*throw FileError*/ catch (FileError&) {} //previous exception is more relevant
+
+ if (symlinkExists)
+ {
+ if (::unlink(dirPath.c_str()) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), L"unlink");
+ return;
+ }
+ throw FileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtPath(dirPath)), formatSystemError(functionName, ec));
+ }
+ /*
+ Windows: may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have
+ successfully been *marked* for deletion, but some application still has a handle open!
+ e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145
+ Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html
+ Alternatives: 1. move file/empty folder to some other location, then DeleteFile()/RemoveDirectory()
+ 2. use CreateFile/FILE_FLAG_DELETE_ON_CLOSE *without* FILE_SHARE_DELETE instead of DeleteFile() => early failure
+ */
+}
+
+
+namespace
+{
+void removeDirectoryImpl(const Zstring& folderPath) //throw FileError
+{
+ std::vector<Zstring> filePaths;
+ std::vector<Zstring> symlinkPaths;
+ std::vector<Zstring> folderPaths;
+
+ //get all files and directories from current directory (WITHOUT subdirectories!)
+ traverseFolder(folderPath,
+ [&](const FileInfo& fi) { filePaths .push_back(fi.fullPath); },
+ [&](const FolderInfo& fi) { folderPaths .push_back(fi.fullPath); }, //defer recursion => save stack space and allow deletion of extremely deep hierarchies!
+ [&](const SymlinkInfo& si) { symlinkPaths.push_back(si.fullPath); },
+ [](const std::wstring& errorMsg) { throw FileError(errorMsg); });
+
+ for (const Zstring& filePath : filePaths)
+ removeFilePlain(filePath); //throw FileError
+
+ for (const Zstring& symlinkPath : symlinkPaths)
+ removeSymlinkPlain(symlinkPath); //throw FileError
+
+ //delete directories recursively
+ for (const Zstring& subFolderPath : folderPaths)
+ removeDirectoryImpl(subFolderPath); //throw FileError; call recursively to correctly handle symbolic links
+
+ removeDirectoryPlain(folderPath); //throw FileError
+}
+}
+
+
+void zen::removeDirectoryPlainRecursion(const Zstring& dirPath) //throw FileError
+{
+ if (getItemType(dirPath) == ItemType::SYMLINK) //throw FileError
+ removeSymlinkPlain(dirPath); //throw FileError
+ else
+ removeDirectoryImpl(dirPath); //throw FileError
+}
+
+
+namespace
+{
+/* Usage overview: (avoid circular pattern!)
+
+ renameFile() --> renameFile_sub()
+ | /|\
+ \|/ |
+ Fix8Dot3NameClash()
+*/
+//wrapper for file system rename function:
+void renameFile_sub(const Zstring& pathSource, const Zstring& pathTarget) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+{
+ //rename() will never fail with EEXIST, but always (atomically) overwrite!
+ //=> equivalent to SetFileInformationByHandle() + FILE_RENAME_INFO::ReplaceIfExists or ::MoveFileEx + MOVEFILE_REPLACE_EXISTING
+ //=> Linux: renameat2() with RENAME_NOREPLACE -> still new, probably buggy
+ //=> OS X: no solution
+
+ auto throwException = [&](int ec)
+ {
+ const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(pathSource)), L"%y", L"\n" + fmtPath(pathTarget));
+ const std::wstring errorDescr = formatSystemError(L"rename", ec);
+
+ if (ec == EXDEV)
+ throw ErrorDifferentVolume(errorMsg, errorDescr);
+ if (ec == EEXIST)
+ throw ErrorTargetExisting(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
+ };
+
+ if (!equalFilePath(pathSource, pathTarget)) //exception for OS X: changing file name case is not an "already exists" situation!
+ {
+ bool alreadyExists = true;
+ try { /*ItemType type = */getItemType(pathTarget); } /*throw FileError*/ catch (FileError&) { alreadyExists = false; }
+
+ if (alreadyExists)
+ throwException(EEXIST);
+ //else: nothing exists or other error (hopefully ::rename will also fail!)
+ }
+
+ if (::rename(pathSource.c_str(), pathTarget.c_str()) != 0)
+ throwException(errno);
+}
+
+
+}
+
+
+//rename file: no copying!!!
+void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+{
+ try
+ {
+ renameFile_sub(pathSource, pathTarget); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+ }
+ catch (const ErrorTargetExisting&)
+ {
+ throw;
+ }
+}
+
+
+namespace
+{
+void setWriteTimeNative(const Zstring& itemPath, const struct ::timespec& modTime, ProcSymlink procSl) //throw FileError
+{
+ /*
+ [2013-05-01] sigh, we can't use utimensat() on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit???
+ => fallback to "retarded-idiot version"! -- DarkByte
+
+ [2015-03-09]
+ - cannot reproduce issues with NTFS and utimensat() on Ubuntu
+ - utimensat() is supposed to obsolete utime/utimes and is also used by "cp" and "touch"
+ => let's give utimensat another chance:
+ using open()/futimens() for regular files and utimensat(AT_SYMLINK_NOFOLLOW) for symlinks is consistent with "cp" and "touch"!
+ */
+ struct ::timespec newTimes[2] = {};
+ newTimes[0].tv_sec = ::time(nullptr); //access time; using UTIME_OMIT for tv_nsec would trigger even more bugs: http://www.freefilesync.org/forum/viewtopic.php?t=1701
+ newTimes[1] = modTime; //modification time
+
+ if (procSl == ProcSymlink::FOLLOW)
+ {
+ //hell knows why files on gvfs-mounted Samba shares fail to open(O_WRONLY) returning EOPNOTSUPP:
+ //http://www.freefilesync.org/forum/viewtopic.php?t=2803 => utimensat() works
+ if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, 0) == 0)
+ return;
+
+ //in other cases utimensat() returns EINVAL for CIFS/NTFS drives, but open+futimens works: http://www.freefilesync.org/forum/viewtopic.php?t=387
+ const int fdFile = ::open(itemPath.c_str(), O_WRONLY);
+ if (fdFile == -1)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"open");
+ ZEN_ON_SCOPE_EXIT(::close(fdFile));
+
+ if (::futimens(fdFile, newTimes) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"futimens");
+ }
+ else
+ {
+ if (::utimensat(AT_FDCWD, itemPath.c_str(), newTimes, AT_SYMLINK_NOFOLLOW) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtPath(itemPath)), L"utimensat");
+ }
+}
+
+
+}
+
+
+void zen::setFileTime(const Zstring& filePath, int64_t modTime, ProcSymlink procSl) //throw FileError
+{
+ struct ::timespec writeTime = {};
+ writeTime.tv_sec = modTime;
+ setWriteTimeNative(filePath, writeTime, procSl); //throw FileError
+
+}
+
+
+bool zen::supportsPermissions(const Zstring& dirPath) //throw FileError
+{
+ return true;
+}
+
+
+namespace
+{
+#ifdef HAVE_SELINUX
+//copy SELinux security context
+void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError
+{
+ security_context_t contextSource = nullptr;
+ const int rv = procSl == ProcSymlink::FOLLOW ?
+ ::getfilecon(source.c_str(), &contextSource) :
+ ::lgetfilecon(source.c_str(), &contextSource);
+ if (rv < 0)
+ {
+ if (errno == ENODATA || //no security context (allegedly) is not an error condition on SELinux
+ errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem
+ return;
+
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtPath(source)), L"getfilecon");
+ }
+ ZEN_ON_SCOPE_EXIT(::freecon(contextSource));
+
+ {
+ security_context_t contextTarget = nullptr;
+ const int rv2 = procSl == ProcSymlink::FOLLOW ?
+ ::getfilecon(target.c_str(), &contextTarget) :
+ ::lgetfilecon(target.c_str(), &contextTarget);
+ if (rv2 < 0)
+ {
+ if (errno == EOPNOTSUPP)
+ return;
+ //else: still try to set security context
+ }
+ else
+ {
+ ZEN_ON_SCOPE_EXIT(::freecon(contextTarget));
+
+ if (::strcmp(contextSource, contextTarget) == 0) //nothing to do
+ return;
+ }
+ }
+
+ const int rv3 = procSl == ProcSymlink::FOLLOW ?
+ ::setfilecon(target.c_str(), contextSource) :
+ ::lsetfilecon(target.c_str(), contextSource);
+ if (rv3 < 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtPath(target)), L"setfilecon");
+}
+#endif
+
+
+//copy permissions for files, directories or symbolic links: requires admin rights
+void copyItemPermissions(const Zstring& sourcePath, const Zstring& targetPath, ProcSymlink procSl) //throw FileError
+{
+
+#ifdef HAVE_SELINUX //copy SELinux security context
+ copySecurityContext(sourcePath, targetPath, procSl); //throw FileError
+#endif
+
+ struct ::stat fileInfo = {};
+ if (procSl == ProcSymlink::FOLLOW)
+ {
+ if (::stat(sourcePath.c_str(), &fileInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"stat");
+
+ if (::chown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chown");
+
+ if (::chmod(targetPath.c_str(), fileInfo.st_mode) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod");
+ }
+ else
+ {
+ if (::lstat(sourcePath.c_str(), &fileInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtPath(sourcePath)), L"lstat");
+
+ if (::lchown(targetPath.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights!
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"lchown");
+
+ const bool isSymlinkTarget = getItemType(targetPath) == ItemType::SYMLINK; //throw FileError
+ if (!isSymlinkTarget && //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod()
+ ::chmod(targetPath.c_str(), fileInfo.st_mode) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(targetPath)), L"chmod");
+ }
+
+}
+}
+
+
+void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError
+{
+ if (!getParentFolderPath(dirPath)) //device root
+ return static_cast<void>(/*ItemType =*/ getItemType(dirPath)); //throw FileError
+
+ try
+ {
+ copyNewDirectory(Zstring(), dirPath, false /*copyFilePermissions*/); //throw FileError, ErrorTargetExisting
+ }
+ catch (FileError&)
+ {
+ Opt<PathStatus> pd;
+ try { pd = getPathStatus(dirPath); /*throw FileError*/ }
+ catch (FileError&) {} //previous exception is more relevant
+
+ if (pd && pd->existingType != ItemType::FILE)
+ {
+ Zstring intermediatePath = pd->existingPath;
+ for (const Zstring& itemName : pd->relPath)
+ {
+ intermediatePath = appendSeparator(intermediatePath) + itemName;
+ copyNewDirectory(Zstring(), intermediatePath, false /*copyFilePermissions*/); //throw FileError, (ErrorTargetExisting)
+ }
+ return;
+ }
+ throw;
+ }
+}
+
+
+//source path is optional (may be empty)
+void zen::copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, //throw FileError, ErrorTargetExisting
+ bool copyFilePermissions)
+{
+ mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; //0777, default for newly created directories
+
+ struct ::stat dirInfo = {};
+ if (!sourcePath.empty())
+ if (::stat(sourcePath.c_str(), &dirInfo) == 0)
+ {
+ mode = dirInfo.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); //analog to "cp" which copies "mode" (considering umask) by default
+ mode |= S_IRWXU; //FFS only: we need full access to copy child items! "cp" seems to apply permissions *after* copying child items
+ }
+ //=> need copyItemPermissions() only for "chown" and umask-agnostic permissions
+
+ if (::mkdir(targetPath.c_str(), mode) != 0)
+ {
+ const int lastError = errno; //copy before directly or indirectly making other system calls!
+ const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtPath(targetPath));
+ const std::wstring errorDescr = formatSystemError(L"mkdir", lastError);
+
+ if (lastError == EEXIST)
+ throw ErrorTargetExisting(errorMsg, errorDescr);
+ //else if (lastError == ENOENT)
+ // throw ErrorTargetPathMissing(errorMsg, errorDescr);
+ throw FileError(errorMsg, errorDescr);
+ }
+
+ if (!sourcePath.empty())
+ {
+
+ ZEN_ON_SCOPE_FAIL(try { removeDirectoryPlain(targetPath); }
+ catch (FileError&) {}); //ensure cleanup:
+
+ //enforce copying file permissions: it's advertized on GUI...
+ if (copyFilePermissions)
+ copyItemPermissions(sourcePath, targetPath, ProcSymlink::FOLLOW); //throw FileError
+ }
+}
+
+
+void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions) //throw FileError
+{
+ const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks
+
+ const wchar_t functionName[] = L"symlink";
+ if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(sourceLink)), L"%y", L"\n" + fmtPath(targetLink)), functionName);
+
+ //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist!
+ ZEN_ON_SCOPE_FAIL(try { removeSymlinkPlain(targetLink); /*throw FileError*/ }
+ catch (FileError&) {});
+
+ //file times: essential for sync'ing a symlink: enforce this! (don't just try!)
+ struct ::stat sourceInfo = {};
+ if (::lstat(sourceLink.c_str(), &sourceInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceLink)), L"lstat");
+
+ setWriteTimeNative(targetLink, sourceInfo.st_mtim, ProcSymlink::DIRECT); //throw FileError
+
+ if (copyFilePermissions)
+ copyItemPermissions(sourceLink, targetLink, ProcSymlink::DIRECT); //throw FileError
+}
+
+
+namespace
+{
+InSyncAttributes copyFileOsSpecific(const Zstring& sourceFile, //throw FileError, ErrorTargetExisting
+ const Zstring& targetFile,
+ const IOCallback& notifyUnbufferedIO)
+{
+ int64_t totalUnbufferedIO = 0;
+
+ FileInput fileIn(sourceFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //throw FileError, (ErrorFileLocked -> Windows-only)
+
+ struct ::stat sourceInfo = {};
+ if (::fstat(fileIn.getHandle(), &sourceInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(sourceFile)), L"fstat");
+
+ const mode_t mode = sourceInfo.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); //analog to "cp" which copies "mode" (considering umask) by default
+ //it seems we don't need S_IWUSR, not even for the setFileTime() below! (tested with source file having different user/group!)
+
+ //=> need copyItemPermissions() only for "chown" and umask-agnostic permissions
+ const int fdTarget = ::open(targetFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode);
+ if (fdTarget == -1)
+ {
+ const int ec = errno; //copy before making other system calls!
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(targetFile));
+ const std::wstring errorDescr = formatSystemError(L"open", ec);
+
+ if (ec == EEXIST)
+ throw ErrorTargetExisting(errorMsg, errorDescr);
+
+ throw FileError(errorMsg, errorDescr);
+ }
+ ZEN_ON_SCOPE_FAIL( try { removeFilePlain(targetFile); }
+ catch (FileError&) {} );
+ //place guard AFTER ::open() and BEFORE lifetime of FileOutput:
+ //=> don't delete file that existed previously!!!
+ FileOutput fileOut(fdTarget, targetFile, IOCallbackDivider(notifyUnbufferedIO, totalUnbufferedIO)); //pass ownership
+
+ bufferedStreamCopy(fileIn, fileOut); //throw FileError, X
+ fileOut.flushBuffers(); //throw FileError, X
+ struct ::stat targetInfo = {};
+ if (::fstat(fileOut.getHandle(), &targetInfo) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(targetFile)), L"fstat");
+
+ //close output file handle before setting file time; also good place to catch errors when closing stream!
+ fileOut.finalize(); //throw FileError, (X) essentially a close() since buffers were already flushed
+
+ //we cannot set the target file times (::futimes) while the file descriptor is still open after a write operation:
+ //this triggers bugs on samba shares where the modification time is set to current time instead.
+ //Linux: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236
+ // http://comments.gmane.org/gmane.linux.file-systems.cifs/2854
+ //OS X: http://www.freefilesync.org/forum/viewtopic.php?t=356
+ setWriteTimeNative(targetFile, sourceInfo.st_mtim, ProcSymlink::FOLLOW); //throw FileError
+
+ InSyncAttributes newAttrib;
+ newAttrib.fileSize = sourceInfo.st_size;
+ newAttrib.modificationTime = sourceInfo.st_mtim.tv_sec; //
+ newAttrib.sourceFileId = extractFileId(sourceInfo);
+ newAttrib.targetFileId = extractFileId(targetInfo);
+ return newAttrib;
+}
+
+/* ------------------
+ |File Copy Layers|
+ ------------------
+ copyNewFile
+ |
+ copyFileOsSpecific (solve 8.3 issue on Windows)
+ |
+ copyFileWindowsSelectRoutine
+ / \
+copyFileWindowsDefault(::CopyFileEx) copyFileWindowsStream(::BackupRead/::BackupWrite)
+*/
+}
+
+
+InSyncAttributes zen::copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked
+ const IOCallback& notifyUnbufferedIO)
+{
+ const InSyncAttributes attr = copyFileOsSpecific(sourceFile, targetFile, notifyUnbufferedIO); //throw FileError, ErrorTargetExisting, ErrorFileLocked
+
+ //at this point we know we created a new file, so it's fine to delete it for cleanup!
+ ZEN_ON_SCOPE_FAIL(try { removeFilePlain(targetFile); }
+ catch (FileError&) {});
+
+ if (copyFilePermissions)
+ copyItemPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError
+
+ return attr;
+}
diff --git a/zen/file_access.h b/zen/file_access.h
index a6b221e5..ee33da93 100755
--- a/zen/file_access.h
+++ b/zen/file_access.h
@@ -1,101 +1,101 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FILE_ACCESS_H_8017341345614857
-#define FILE_ACCESS_H_8017341345614857
-
-#include <functional>
-#include "zstring.h"
-#include "file_error.h"
-#include "file_id_def.h"
-#include "serialize.h"
-
-namespace zen
-{
-//note: certain functions require COM initialization! (vista_file_op.h)
-
-struct PathComponents
-{
- Zstring rootPath; //itemPath = rootPath + (FILE_NAME_SEPARATOR?) + relPath
- Zstring relPath; //
-};
-Opt<PathComponents> parsePathComponents(const Zstring& itemPath); //no value on failure
-
-Opt<Zstring> getParentFolderPath(const Zstring& itemPath);
-
-//POSITIVE existence checks; if false: 1. item not existing 2. different type 3.device access error or similar
-bool fileAvailable(const Zstring& filePath); //noexcept
-bool dirAvailable (const Zstring& dirPath ); //
-//NEGATIVE existence checks; if false: 1. item existing 2.device access error or similar
-bool itemNotExisting(const Zstring& itemPath);
-
-enum class ItemType
-{
- FILE,
- FOLDER,
- SYMLINK,
-};
-//(hopefully) fast: does not distinguish between error/not existing
-ItemType getItemType (const Zstring& itemPath); //throw FileError
-//execute potentially SLOW folder traversal but distinguish error/not existing
-Opt<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError
-
-struct PathStatus
-{
- ItemType existingType;
- Zstring existingPath; //itemPath =: existingPath + relPath
- std::vector<Zstring> relPath; //
-};
-PathStatus getPathStatus(const Zstring& itemPath); //throw FileError
-
-enum class ProcSymlink
-{
- DIRECT,
- FOLLOW
-};
-void setFileTime(const Zstring& filePath, int64_t modificationTime, ProcSymlink procSl); //throw FileError
-
-//symlink handling: always evaluate target
-uint64_t getFileSize(const Zstring& filePath); //throw FileError
-uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available
-VolumeId getVolumeId(const Zstring& itemPath); //throw FileError
-//get per-user directory designated for temporary files:
-Zstring getTempFolderPath(); //throw FileError
-
-void removeFilePlain (const Zstring& filePath); //throw FileError; ERROR if not existing
-void removeSymlinkPlain (const Zstring& linkPath); //throw FileError; ERROR if not existing
-void removeDirectoryPlain(const Zstring& dirPath ); //throw FileError; ERROR if not existing
-void removeDirectoryPlainRecursion(const Zstring& dirPath); //throw FileError; ERROR if not existing
-
-//rename file or directory: no copying!!!
-void renameFile(const Zstring& itemPathOld, const Zstring& itemPathNew); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
-
-bool supportsPermissions(const Zstring& dirPath); //throw FileError, dereferences symlinks
-
-//- no error if already existing
-//- create recursively if parent directory is not existing
-void createDirectoryIfMissingRecursion(const Zstring& dirPath); //throw FileError
-
-//fail if already existing or parent directory not existing:
-//source path is optional (may be empty)
-void copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions); //throw FileError, ErrorTargetExisting
-
-void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError
-
-struct InSyncAttributes
-{
- uint64_t fileSize = 0;
- int64_t modificationTime = 0; //time_t UTC compatible
- FileId sourceFileId;
- FileId targetFileId;
-};
-
-InSyncAttributes copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked
- //accummulated delta != file size! consider ADS, sparse, compressed files
- const IOCallback& notifyUnbufferedIO); //may be nullptr; throw X!
-}
-
-#endif //FILE_ACCESS_H_8017341345614857
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef FILE_ACCESS_H_8017341345614857
+#define FILE_ACCESS_H_8017341345614857
+
+#include <functional>
+#include "zstring.h"
+#include "file_error.h"
+#include "file_id_def.h"
+#include "serialize.h"
+
+namespace zen
+{
+//note: certain functions require COM initialization! (vista_file_op.h)
+
+struct PathComponents
+{
+ Zstring rootPath; //itemPath = rootPath + (FILE_NAME_SEPARATOR?) + relPath
+ Zstring relPath; //
+};
+Opt<PathComponents> parsePathComponents(const Zstring& itemPath); //no value on failure
+
+Opt<Zstring> getParentFolderPath(const Zstring& itemPath);
+
+//POSITIVE existence checks; if false: 1. item not existing 2. different type 3.device access error or similar
+bool fileAvailable(const Zstring& filePath); //noexcept
+bool dirAvailable (const Zstring& dirPath ); //
+//NEGATIVE existence checks; if false: 1. item existing 2.device access error or similar
+bool itemNotExisting(const Zstring& itemPath);
+
+enum class ItemType
+{
+ FILE,
+ FOLDER,
+ SYMLINK,
+};
+//(hopefully) fast: does not distinguish between error/not existing
+ItemType getItemType (const Zstring& itemPath); //throw FileError
+//execute potentially SLOW folder traversal but distinguish error/not existing
+Opt<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError
+
+struct PathStatus
+{
+ ItemType existingType;
+ Zstring existingPath; //itemPath =: existingPath + relPath
+ std::vector<Zstring> relPath; //
+};
+PathStatus getPathStatus(const Zstring& itemPath); //throw FileError
+
+enum class ProcSymlink
+{
+ DIRECT,
+ FOLLOW
+};
+void setFileTime(const Zstring& filePath, int64_t modificationTime, ProcSymlink procSl); //throw FileError
+
+//symlink handling: always evaluate target
+uint64_t getFileSize(const Zstring& filePath); //throw FileError
+uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available
+VolumeId getVolumeId(const Zstring& itemPath); //throw FileError
+//get per-user directory designated for temporary files:
+Zstring getTempFolderPath(); //throw FileError
+
+void removeFilePlain (const Zstring& filePath); //throw FileError; ERROR if not existing
+void removeSymlinkPlain (const Zstring& linkPath); //throw FileError; ERROR if not existing
+void removeDirectoryPlain(const Zstring& dirPath ); //throw FileError; ERROR if not existing
+void removeDirectoryPlainRecursion(const Zstring& dirPath); //throw FileError; ERROR if not existing
+
+//rename file or directory: no copying!!!
+void renameFile(const Zstring& itemPathOld, const Zstring& itemPathNew); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting
+
+bool supportsPermissions(const Zstring& dirPath); //throw FileError, dereferences symlinks
+
+//- no error if already existing
+//- create recursively if parent directory is not existing
+void createDirectoryIfMissingRecursion(const Zstring& dirPath); //throw FileError
+
+//fail if already existing or parent directory not existing:
+//source path is optional (may be empty)
+void copyNewDirectory(const Zstring& sourcePath, const Zstring& targetPath, bool copyFilePermissions); //throw FileError, ErrorTargetExisting
+
+void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError
+
+struct InSyncAttributes
+{
+ uint64_t fileSize = 0;
+ int64_t modificationTime = 0; //time_t UTC compatible
+ FileId sourceFileId;
+ FileId targetFileId;
+};
+
+InSyncAttributes copyNewFile(const Zstring& sourceFile, const Zstring& targetFile, bool copyFilePermissions, //throw FileError, ErrorTargetExisting, ErrorFileLocked
+ //accummulated delta != file size! consider ADS, sparse, compressed files
+ const IOCallback& notifyUnbufferedIO); //may be nullptr; throw X!
+}
+
+#endif //FILE_ACCESS_H_8017341345614857
diff --git a/zen/file_error.h b/zen/file_error.h
index 949c644f..b318e708 100755
--- a/zen/file_error.h
+++ b/zen/file_error.h
@@ -1,53 +1,53 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FILE_ERROR_H_839567308565656789
-#define FILE_ERROR_H_839567308565656789
-
-#include <string>
-#include "zstring.h"
-#include "utf.h"
-#include "sys_error.h" //we'll need this later anyway!
-
-namespace zen
-{
-//A high-level exception class giving detailed context information for end users
-class FileError
-{
-public:
- explicit FileError(const std::wstring& msg) : msg_(msg) {}
- FileError(const std::wstring& msg, const std::wstring& details) : msg_(msg + L"\n\n" + details) {}
- virtual ~FileError() {}
-
- const std::wstring& toString() const { return msg_; }
-
-private:
- std::wstring msg_;
-};
-
-#define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} };
-
-DEFINE_NEW_FILE_ERROR(ErrorTargetExisting);
-//DEFINE_NEW_FILE_ERROR(ErrorTargetPathMissing);
-DEFINE_NEW_FILE_ERROR(ErrorFileLocked);
-DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume);
-
-
-//CAVEAT: thread-local Win32 error code is easily overwritten => evaluate *before* making any (indirect) system calls:
-//-> MinGW + Win XP: "throw" statement allocates memory to hold the exception object => error code is cleared
-//-> VC 2015, Debug: std::wstring allocator internally calls ::FlsGetValue() => error code is cleared
-// https://connect.microsoft.com/VisualStudio/feedback/details/1775690/calling-operator-new-may-set-lasterror-to-0
-#define THROW_LAST_FILE_ERROR(msg, functionName) \
- do { const ErrorCode ecInternal = getLastError(); throw FileError(msg, formatSystemError(functionName, ecInternal)); } while (false)
-
-//----------- facilitate usage of std::wstring for error messages --------------------
-
-inline std::wstring fmtPath(const std::wstring& displayPath) { return L'\"' + displayPath + L'\"'; }
-inline std::wstring fmtPath(const Zstring& displayPath) { return fmtPath(utfTo<std::wstring>(displayPath)); }
-inline std::wstring fmtPath(const wchar_t* displayPath) { return fmtPath(std::wstring(displayPath)); } //resolve overload ambiguity
-}
-
-#endif //FILE_ERROR_H_839567308565656789
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef FILE_ERROR_H_839567308565656789
+#define FILE_ERROR_H_839567308565656789
+
+#include <string>
+#include "zstring.h"
+#include "utf.h"
+#include "sys_error.h" //we'll need this later anyway!
+
+namespace zen
+{
+//A high-level exception class giving detailed context information for end users
+class FileError
+{
+public:
+ explicit FileError(const std::wstring& msg) : msg_(msg) {}
+ FileError(const std::wstring& msg, const std::wstring& details) : msg_(msg + L"\n\n" + details) {}
+ virtual ~FileError() {}
+
+ const std::wstring& toString() const { return msg_; }
+
+private:
+ std::wstring msg_;
+};
+
+#define DEFINE_NEW_FILE_ERROR(X) struct X : public FileError { X(const std::wstring& msg) : FileError(msg) {} X(const std::wstring& msg, const std::wstring& descr) : FileError(msg, descr) {} };
+
+DEFINE_NEW_FILE_ERROR(ErrorTargetExisting);
+//DEFINE_NEW_FILE_ERROR(ErrorTargetPathMissing);
+DEFINE_NEW_FILE_ERROR(ErrorFileLocked);
+DEFINE_NEW_FILE_ERROR(ErrorDifferentVolume);
+
+
+//CAVEAT: thread-local Win32 error code is easily overwritten => evaluate *before* making any (indirect) system calls:
+//-> MinGW + Win XP: "throw" statement allocates memory to hold the exception object => error code is cleared
+//-> VC 2015, Debug: std::wstring allocator internally calls ::FlsGetValue() => error code is cleared
+// https://connect.microsoft.com/VisualStudio/feedback/details/1775690/calling-operator-new-may-set-lasterror-to-0
+#define THROW_LAST_FILE_ERROR(msg, functionName) \
+ do { const ErrorCode ecInternal = getLastError(); throw FileError(msg, formatSystemError(functionName, ecInternal)); } while (false)
+
+//----------- facilitate usage of std::wstring for error messages --------------------
+
+inline std::wstring fmtPath(const std::wstring& displayPath) { return L'\"' + displayPath + L'\"'; }
+inline std::wstring fmtPath(const Zstring& displayPath) { return fmtPath(utfTo<std::wstring>(displayPath)); }
+inline std::wstring fmtPath(const wchar_t* displayPath) { return fmtPath(std::wstring(displayPath)); } //resolve overload ambiguity
+}
+
+#endif //FILE_ERROR_H_839567308565656789
diff --git a/zen/file_id_def.h b/zen/file_id_def.h
index 87a5b199..3dcd21b9 100755
--- a/zen/file_id_def.h
+++ b/zen/file_id_def.h
@@ -1,41 +1,41 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FILE_ID_DEF_H_013287632486321493
-#define FILE_ID_DEF_H_013287632486321493
-
-#include <utility>
-
- #include <sys/stat.h>
-
-
-namespace zen
-{
-namespace impl { typedef struct ::stat StatDummy; } //sigh...
-
-using VolumeId = decltype(impl::StatDummy::st_dev);
-using FileIndex = decltype(impl::StatDummy::st_ino);
-
-
-struct FileId //always available on Linux, and *generally* available on Windows)
-{
- FileId() {}
- FileId(VolumeId volId, FileIndex fIdx) : volumeId(volId), fileIndex(fIdx) {}
- VolumeId volumeId = 0;
- FileIndex fileIndex = 0;
-};
-inline bool operator==(const FileId& lhs, const FileId& rhs) { return lhs.volumeId == rhs.volumeId && lhs.fileIndex == rhs.fileIndex; }
-
-
-inline
-FileId extractFileId(const struct ::stat& fileInfo)
-{
- return fileInfo.st_dev != 0 && fileInfo.st_ino != 0 ?
- FileId(fileInfo.st_dev, fileInfo.st_ino) : FileId();
-}
-}
-
-#endif //FILE_ID_DEF_H_013287632486321493
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef FILE_ID_DEF_H_013287632486321493
+#define FILE_ID_DEF_H_013287632486321493
+
+#include <utility>
+
+ #include <sys/stat.h>
+
+
+namespace zen
+{
+namespace impl { typedef struct ::stat StatDummy; } //sigh...
+
+using VolumeId = decltype(impl::StatDummy::st_dev);
+using FileIndex = decltype(impl::StatDummy::st_ino);
+
+
+struct FileId //always available on Linux, and *generally* available on Windows)
+{
+ FileId() {}
+ FileId(VolumeId volId, FileIndex fIdx) : volumeId(volId), fileIndex(fIdx) {}
+ VolumeId volumeId = 0;
+ FileIndex fileIndex = 0;
+};
+inline bool operator==(const FileId& lhs, const FileId& rhs) { return lhs.volumeId == rhs.volumeId && lhs.fileIndex == rhs.fileIndex; }
+
+
+inline
+FileId extractFileId(const struct ::stat& fileInfo)
+{
+ return fileInfo.st_dev != 0 && fileInfo.st_ino != 0 ?
+ FileId(fileInfo.st_dev, fileInfo.st_ino) : FileId();
+}
+}
+
+#endif //FILE_ID_DEF_H_013287632486321493
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index 0c5ff490..d291e741 100755
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -1,274 +1,274 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "file_io.h"
-#include "file_access.h"
-
- #include <sys/stat.h>
- #include <fcntl.h> //open, close
- #include <unistd.h> //read, write
-
-using namespace zen;
-
-
-namespace
-{
-//- "filePath" could be a named pipe which *blocks* forever for open()!
-//- open() with O_NONBLOCK avoids the block, but opens successfully
-//- create sample pipe: "sudo mkfifo named_pipe"
-void checkForUnsupportedType(const Zstring& filePath) //throw FileError
-{
- struct ::stat fileInfo = {};
- if (::stat(filePath.c_str(), &fileInfo) != 0) //follows symlinks
- return; //let the caller handle errors like "not existing"
-
- if (!S_ISREG(fileInfo.st_mode) &&
- !S_ISLNK(fileInfo.st_mode) &&
- !S_ISDIR(fileInfo.st_mode))
- {
- auto getTypeName = [](mode_t m) -> std::wstring
- {
- const wchar_t* name =
- S_ISCHR (m) ? L"character device":
- S_ISBLK (m) ? L"block device" :
- S_ISFIFO(m) ? L"FIFO, named pipe" :
- S_ISSOCK(m) ? L"socket" : nullptr;
- const std::wstring numFmt = printNumber<std::wstring>(L"0%06o", m & S_IFMT);
- return name ? numFmt + L", " + name : numFmt;
- };
- throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtPath(filePath)) + L" " + getTypeName(fileInfo.st_mode));
- }
-}
-}
-
-
- const FileBase::FileHandle FileBase::invalidHandleValue = -1;
-
-
-FileBase::~FileBase()
-{
- if (fileHandle_ != invalidHandleValue)
- try
- {
- close(); //throw FileError
- }
- catch (FileError&) { assert(false); }
-}
-
-
-void FileBase::close() //throw FileError
-{
- if (fileHandle_ == invalidHandleValue)
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"Contract error: close() called more than once.");
- ZEN_ON_SCOPE_EXIT(fileHandle_ = invalidHandleValue);
-
- //no need to clean-up on failure here (just like there is no clean on FileOutput::write failure!) => FileOutput is not transactional!
-
- if (::close(fileHandle_) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"close");
-}
-
-//----------------------------------------------------------------------------------------------------
-
-namespace
-{
-FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileError, ErrorFileLocked
-{
- checkForUnsupportedType(filePath); //throw FileError; opening a named pipe would block forever!
-
- //don't use O_DIRECT: http://yarchive.net/comp/linux/o_direct.html
- const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_RDONLY);
- if (fileHandle == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filePath)), L"open");
- return fileHandle; //pass ownership
-}
-}
-
-
-FileInput::FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
- FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {}
-
-
-FileInput::FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
- FileBase(openHandleForRead(filePath), filePath), //throw FileError, ErrorFileLocked
- notifyUnbufferedIO_(notifyUnbufferedIO)
-{
- //optimize read-ahead on input file:
- if (::posix_fadvise(getHandle(), 0, 0, POSIX_FADV_SEQUENTIAL) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(filePath)), L"posix_fadvise");
-
-}
-
-
-size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; may return short, only 0 means EOF!
-{
- if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check!
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
- assert(bytesToRead == getBlockSize());
-
- ssize_t bytesRead = 0;
- do
- {
- bytesRead = ::read(getHandle(), buffer, bytesToRead);
- }
- while (bytesRead < 0 && errno == EINTR); //Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-8.23.tar.xz
-
- if (bytesRead < 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"read");
- if (static_cast<size_t>(bytesRead) > bytesToRead) //better safe than sorry
- throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"ReadFile: buffer overflow."); //user should never see this
-
- //if ::read is interrupted (EINTR) right in the middle, it will return successfully with "bytesRead < bytesToRead"
-
- return bytesRead; //"zero indicates end of file"
-}
-
-
-size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; return "bytesToRead" bytes unless end of stream!
-{
- const size_t blockSize = getBlockSize();
-
- while (memBuf_.size() < bytesToRead)
- {
- memBuf_.resize(memBuf_.size() + blockSize);
- const size_t bytesRead = tryRead(&*(memBuf_.end() - blockSize), blockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
- memBuf_.resize(memBuf_.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
-
- if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X
-
- if (bytesRead == 0) //end of file
- bytesToRead = std::min(bytesToRead, memBuf_.size());
- }
-
- std::copy(memBuf_.begin(), memBuf_.begin() + bytesToRead, static_cast<char*>(buffer));
- memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesToRead);
- return bytesToRead;
-}
-
-//----------------------------------------------------------------------------------------------------
-
-namespace
-{
-FileBase::FileHandle openHandleForWrite(const Zstring& filePath, FileOutput::AccessFlag access) //throw FileError, ErrorTargetExisting
-{
- //checkForUnsupportedType(filePath); -> not needed, open() + O_WRONLY should fail fast
-
- const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_WRONLY | O_CREAT | (access == FileOutput::ACC_CREATE_NEW ? O_EXCL : O_TRUNC),
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //0666
- if (fileHandle == -1)
- {
- const int ec = errno; //copy before making other system calls!
- const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filePath));
- const std::wstring errorDescr = formatSystemError(L"open", ec);
-
- if (ec == EEXIST)
- throw ErrorTargetExisting(errorMsg, errorDescr);
- //if (ec == ENOENT) throw ErrorTargetPathMissing(errorMsg, errorDescr);
-
- throw FileError(errorMsg, errorDescr);
- }
- return fileHandle; //pass ownership
-}
-}
-
-
-FileOutput::FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
- FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {}
-
-
-FileOutput::FileOutput(const Zstring& filePath, AccessFlag access, const IOCallback& notifyUnbufferedIO) :
- FileBase(openHandleForWrite(filePath, access), filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {} //throw FileError, ErrorTargetExisting
-
-
-FileOutput::~FileOutput()
-{
- notifyUnbufferedIO_ = nullptr; //no call-backs during destruction!!!
- try
- {
- flushBuffers(); //throw FileError, (X)
- }
- catch (...) { assert(false); }
-}
-
-
-size_t FileOutput::tryWrite(const void* buffer, size_t bytesToWrite) //throw FileError; may return short! CONTRACT: bytesToWrite > 0
-{
- if (bytesToWrite == 0)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
- assert(bytesToWrite <= getBlockSize());
-
- ssize_t bytesWritten = 0;
- do
- {
- bytesWritten = ::write(getHandle(), buffer, bytesToWrite);
- }
- while (bytesWritten < 0 && errno == EINTR);
-
- if (bytesWritten <= 0)
- {
- if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers
- errno = ENOSPC;
-
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write");
- }
- if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry
- throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write: buffer overflow."); //user should never see this
-
- //if ::write() is interrupted (EINTR) right in the middle, it will return successfully with "bytesWritten < bytesToWrite"!
- return bytesWritten;
-}
-
-
-void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileError, X
-{
- auto bufFirst = static_cast<const char*>(buffer);
- memBuf_.insert(memBuf_.end(), bufFirst, bufFirst + bytesToWrite);
-
- const size_t blockSize = getBlockSize();
- size_t bytesRemaining = memBuf_.size();
- ZEN_ON_SCOPE_EXIT(memBuf_.erase(memBuf_.begin(), memBuf_.end() - bytesRemaining));
- while (bytesRemaining >= blockSize)
- {
- const size_t bytesWritten = tryWrite(&*(memBuf_.end() - bytesRemaining), blockSize); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
- bytesRemaining -= bytesWritten;
- if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesWritten); //throw X!
- }
-}
-
-
-void FileOutput::flushBuffers() //throw FileError, X
-{
- size_t bytesRemaining = memBuf_.size();
- ZEN_ON_SCOPE_EXIT(memBuf_.erase(memBuf_.begin(), memBuf_.end() - bytesRemaining));
- while (bytesRemaining > 0)
- {
- const size_t bytesWritten = tryWrite(&*(memBuf_.end() - bytesRemaining), bytesRemaining); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
- bytesRemaining -= bytesWritten;
- if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesWritten); //throw X!
- }
-}
-
-
-void FileOutput::finalize() //throw FileError, X
-{
- flushBuffers(); //throw FileError, X
- //~FileBase() calls this one, too, but we want to propagate errors if any:
- close(); //throw FileError
-}
-
-
-void FileOutput::preAllocateSpaceBestEffort(uint64_t expectedSize) //throw FileError
-{
- const FileHandle fh = getHandle();
- //don't use potentially inefficient ::posix_fallocate!
- const int rv = ::fallocate(fh, //int fd,
- 0, //int mode,
- 0, //off_t offset
- expectedSize); //off_t len
- if (rv != 0)
- return; //may fail with EOPNOTSUPP, unlike posix_fallocate
-
-}
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "file_io.h"
+#include "file_access.h"
+
+ #include <sys/stat.h>
+ #include <fcntl.h> //open, close
+ #include <unistd.h> //read, write
+
+using namespace zen;
+
+
+namespace
+{
+//- "filePath" could be a named pipe which *blocks* forever for open()!
+//- open() with O_NONBLOCK avoids the block, but opens successfully
+//- create sample pipe: "sudo mkfifo named_pipe"
+void checkForUnsupportedType(const Zstring& filePath) //throw FileError
+{
+ struct ::stat fileInfo = {};
+ if (::stat(filePath.c_str(), &fileInfo) != 0) //follows symlinks
+ return; //let the caller handle errors like "not existing"
+
+ if (!S_ISREG(fileInfo.st_mode) &&
+ !S_ISLNK(fileInfo.st_mode) &&
+ !S_ISDIR(fileInfo.st_mode))
+ {
+ auto getTypeName = [](mode_t m) -> std::wstring
+ {
+ const wchar_t* name =
+ S_ISCHR (m) ? L"character device":
+ S_ISBLK (m) ? L"block device" :
+ S_ISFIFO(m) ? L"FIFO, named pipe" :
+ S_ISSOCK(m) ? L"socket" : nullptr;
+ const std::wstring numFmt = printNumber<std::wstring>(L"0%06o", m & S_IFMT);
+ return name ? numFmt + L", " + name : numFmt;
+ };
+ throw FileError(replaceCpy(_("Type of item %x is not supported:"), L"%x", fmtPath(filePath)) + L" " + getTypeName(fileInfo.st_mode));
+ }
+}
+}
+
+
+ const FileBase::FileHandle FileBase::invalidHandleValue = -1;
+
+
+FileBase::~FileBase()
+{
+ if (fileHandle_ != invalidHandleValue)
+ try
+ {
+ close(); //throw FileError
+ }
+ catch (FileError&) { assert(false); }
+}
+
+
+void FileBase::close() //throw FileError
+{
+ if (fileHandle_ == invalidHandleValue)
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"Contract error: close() called more than once.");
+ ZEN_ON_SCOPE_EXIT(fileHandle_ = invalidHandleValue);
+
+ //no need to clean-up on failure here (just like there is no clean on FileOutput::write failure!) => FileOutput is not transactional!
+
+ if (::close(fileHandle_) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"close");
+}
+
+//----------------------------------------------------------------------------------------------------
+
+namespace
+{
+FileBase::FileHandle openHandleForRead(const Zstring& filePath) //throw FileError, ErrorFileLocked
+{
+ checkForUnsupportedType(filePath); //throw FileError; opening a named pipe would block forever!
+
+ //don't use O_DIRECT: http://yarchive.net/comp/linux/o_direct.html
+ const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_RDONLY);
+ if (fileHandle == -1) //don't check "< 0" -> docu seems to allow "-2" to be a valid file handle
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open file %x."), L"%x", fmtPath(filePath)), L"open");
+ return fileHandle; //pass ownership
+}
+}
+
+
+FileInput::FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
+ FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {}
+
+
+FileInput::FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
+ FileBase(openHandleForRead(filePath), filePath), //throw FileError, ErrorFileLocked
+ notifyUnbufferedIO_(notifyUnbufferedIO)
+{
+ //optimize read-ahead on input file:
+ if (::posix_fadvise(getHandle(), 0, 0, POSIX_FADV_SEQUENTIAL) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(filePath)), L"posix_fadvise");
+
+}
+
+
+size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError; may return short, only 0 means EOF!
+{
+ if (bytesToRead == 0) //"read() with a count of 0 returns zero" => indistinguishable from end of file! => check!
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ assert(bytesToRead == getBlockSize());
+
+ ssize_t bytesRead = 0;
+ do
+ {
+ bytesRead = ::read(getHandle(), buffer, bytesToRead);
+ }
+ while (bytesRead < 0 && errno == EINTR); //Compare copy_reg() in copy.c: ftp://ftp.gnu.org/gnu/coreutils/coreutils-8.23.tar.xz
+
+ if (bytesRead < 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"read");
+ if (static_cast<size_t>(bytesRead) > bytesToRead) //better safe than sorry
+ throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtPath(getFilePath())), L"ReadFile: buffer overflow."); //user should never see this
+
+ //if ::read is interrupted (EINTR) right in the middle, it will return successfully with "bytesRead < bytesToRead"
+
+ return bytesRead; //"zero indicates end of file"
+}
+
+
+size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, X; return "bytesToRead" bytes unless end of stream!
+{
+ const size_t blockSize = getBlockSize();
+
+ while (memBuf_.size() < bytesToRead)
+ {
+ memBuf_.resize(memBuf_.size() + blockSize);
+ const size_t bytesRead = tryRead(&*(memBuf_.end() - blockSize), blockSize); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0
+ memBuf_.resize(memBuf_.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
+
+ if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesRead); //throw X
+
+ if (bytesRead == 0) //end of file
+ bytesToRead = std::min(bytesToRead, memBuf_.size());
+ }
+
+ std::copy(memBuf_.begin(), memBuf_.begin() + bytesToRead, static_cast<char*>(buffer));
+ memBuf_.erase(memBuf_.begin(), memBuf_.begin() + bytesToRead);
+ return bytesToRead;
+}
+
+//----------------------------------------------------------------------------------------------------
+
+namespace
+{
+FileBase::FileHandle openHandleForWrite(const Zstring& filePath, FileOutput::AccessFlag access) //throw FileError, ErrorTargetExisting
+{
+ //checkForUnsupportedType(filePath); -> not needed, open() + O_WRONLY should fail fast
+
+ const FileBase::FileHandle fileHandle = ::open(filePath.c_str(), O_WRONLY | O_CREAT | (access == FileOutput::ACC_CREATE_NEW ? O_EXCL : O_TRUNC),
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //0666
+ if (fileHandle == -1)
+ {
+ const int ec = errno; //copy before making other system calls!
+ const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(filePath));
+ const std::wstring errorDescr = formatSystemError(L"open", ec);
+
+ if (ec == EEXIST)
+ throw ErrorTargetExisting(errorMsg, errorDescr);
+ //if (ec == ENOENT) throw ErrorTargetPathMissing(errorMsg, errorDescr);
+
+ throw FileError(errorMsg, errorDescr);
+ }
+ return fileHandle; //pass ownership
+}
+}
+
+
+FileOutput::FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO) :
+ FileBase(handle, filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {}
+
+
+FileOutput::FileOutput(const Zstring& filePath, AccessFlag access, const IOCallback& notifyUnbufferedIO) :
+ FileBase(openHandleForWrite(filePath, access), filePath), notifyUnbufferedIO_(notifyUnbufferedIO) {} //throw FileError, ErrorTargetExisting
+
+
+FileOutput::~FileOutput()
+{
+ notifyUnbufferedIO_ = nullptr; //no call-backs during destruction!!!
+ try
+ {
+ flushBuffers(); //throw FileError, (X)
+ }
+ catch (...) { assert(false); }
+}
+
+
+size_t FileOutput::tryWrite(const void* buffer, size_t bytesToWrite) //throw FileError; may return short! CONTRACT: bytesToWrite > 0
+{
+ if (bytesToWrite == 0)
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ assert(bytesToWrite <= getBlockSize());
+
+ ssize_t bytesWritten = 0;
+ do
+ {
+ bytesWritten = ::write(getHandle(), buffer, bytesToWrite);
+ }
+ while (bytesWritten < 0 && errno == EINTR);
+
+ if (bytesWritten <= 0)
+ {
+ if (bytesWritten == 0) //comment in safe-read.c suggests to treat this as an error due to buggy drivers
+ errno = ENOSPC;
+
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write");
+ }
+ if (bytesWritten > static_cast<ssize_t>(bytesToWrite)) //better safe than sorry
+ throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(getFilePath())), L"write: buffer overflow."); //user should never see this
+
+ //if ::write() is interrupted (EINTR) right in the middle, it will return successfully with "bytesWritten < bytesToWrite"!
+ return bytesWritten;
+}
+
+
+void FileOutput::write(const void* buffer, size_t bytesToWrite) //throw FileError, X
+{
+ auto bufFirst = static_cast<const char*>(buffer);
+ memBuf_.insert(memBuf_.end(), bufFirst, bufFirst + bytesToWrite);
+
+ const size_t blockSize = getBlockSize();
+ size_t bytesRemaining = memBuf_.size();
+ ZEN_ON_SCOPE_EXIT(memBuf_.erase(memBuf_.begin(), memBuf_.end() - bytesRemaining));
+ while (bytesRemaining >= blockSize)
+ {
+ const size_t bytesWritten = tryWrite(&*(memBuf_.end() - bytesRemaining), blockSize); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
+ bytesRemaining -= bytesWritten;
+ if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesWritten); //throw X!
+ }
+}
+
+
+void FileOutput::flushBuffers() //throw FileError, X
+{
+ size_t bytesRemaining = memBuf_.size();
+ ZEN_ON_SCOPE_EXIT(memBuf_.erase(memBuf_.begin(), memBuf_.end() - bytesRemaining));
+ while (bytesRemaining > 0)
+ {
+ const size_t bytesWritten = tryWrite(&*(memBuf_.end() - bytesRemaining), bytesRemaining); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
+ bytesRemaining -= bytesWritten;
+ if (notifyUnbufferedIO_) notifyUnbufferedIO_(bytesWritten); //throw X!
+ }
+}
+
+
+void FileOutput::finalize() //throw FileError, X
+{
+ flushBuffers(); //throw FileError, X
+ //~FileBase() calls this one, too, but we want to propagate errors if any:
+ close(); //throw FileError
+}
+
+
+void FileOutput::preAllocateSpaceBestEffort(uint64_t expectedSize) //throw FileError
+{
+ const FileHandle fh = getHandle();
+ //don't use potentially inefficient ::posix_fallocate!
+ const int rv = ::fallocate(fh, //int fd,
+ 0, //int mode,
+ 0, //off_t offset
+ expectedSize); //off_t len
+ if (rv != 0)
+ return; //may fail with EOPNOTSUPP, unlike posix_fallocate
+
+}
diff --git a/zen/file_io.h b/zen/file_io.h
index 827abd9e..6b0665a1 100755
--- a/zen/file_io.h
+++ b/zen/file_io.h
@@ -1,123 +1,123 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FILE_IO_H_89578342758342572345
-#define FILE_IO_H_89578342758342572345
-
-#include "file_error.h"
-#include "serialize.h"
-
-
-namespace zen
-{
- const char LINE_BREAK[] = "\n"; //since OS X apple uses newline, too
-
-/*
-OS-buffered file IO optimized for
- - sequential read/write accesses
- - better error reporting
- - long path support
- - follows symlinks
- */
-class FileBase
-{
-public:
- const Zstring& getFilePath() const { return filePath_; }
-
- using FileHandle = int;
-
- FileHandle getHandle() { return fileHandle_; }
-
- //Windows: use 64kB ?? https://technet.microsoft.com/en-us/library/cc938632
- //Linux: use st_blksize?
- static size_t getBlockSize() { return 128 * 1024; };
-
-protected:
- FileBase(FileHandle handle, const Zstring& filePath) : fileHandle_(handle), filePath_(filePath) {}
- ~FileBase();
-
- void close(); //throw FileError -> optional, but good place to catch errors when closing stream!
- static const FileHandle invalidHandleValue;
-
-private:
- FileBase (const FileBase&) = delete;
- FileBase& operator=(const FileBase&) = delete;
-
- FileHandle fileHandle_ = invalidHandleValue;
- const Zstring filePath_;
-};
-
-//-----------------------------------------------------------------------------------------------
-
-class FileInput : public FileBase
-{
-public:
- FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorFileLocked
- FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership!
-
- size_t read(void* buffer, size_t bytesToRead); //throw FileError, X; return "bytesToRead" bytes unless end of stream!
-
-private:
- size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0!
-
- std::vector<char> memBuf_;
- const IOCallback notifyUnbufferedIO_; //throw X
-};
-
-
-class FileOutput : public FileBase
-{
-public:
- enum AccessFlag
- {
- ACC_OVERWRITE,
- ACC_CREATE_NEW
- };
- FileOutput(const Zstring& filePath, AccessFlag access, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorTargetExisting
- FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership!
- ~FileOutput();
-
- void preAllocateSpaceBestEffort(uint64_t expectedSize); //throw FileError
-
- void write(const void* buffer, size_t bytesToWrite); //throw FileError, X
- void flushBuffers(); //throw FileError, X
- void finalize(); /*= flushBuffers() + close()*/ //throw FileError, X
-
-private:
- size_t tryWrite(const void* buffer, size_t bytesToWrite); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
-
- std::vector<char> memBuf_;
- IOCallback notifyUnbufferedIO_; //throw X
-};
-
-//-----------------------------------------------------------------------------------------------
-
-//native stream I/O convenience functions:
-
-template <class BinContainer> inline
-BinContainer loadBinContainer(const Zstring& filePath, //throw FileError
- const IOCallback& notifyUnbufferedIO)
-{
- FileInput streamIn(filePath, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
- return bufferedLoad<BinContainer>(streamIn); //throw FileError, X;
-}
-
-
-template <class BinContainer> inline
-void saveBinContainer(const Zstring& filePath, const BinContainer& buffer, //throw FileError
- const IOCallback& notifyUnbufferedIO)
-{
- FileOutput fileOut(filePath, FileOutput::ACC_OVERWRITE, notifyUnbufferedIO); //throw FileError, (ErrorTargetExisting)
- if (!buffer.empty())
- {
- /*snake oil?*/ fileOut.preAllocateSpaceBestEffort(buffer.size()); //throw FileError
- fileOut.write(&*buffer.begin(), buffer.size()); //throw FileError, X
- }
- fileOut.finalize(); //throw FileError, X
-}
-}
-
-#endif //FILE_IO_H_89578342758342572345
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef FILE_IO_H_89578342758342572345
+#define FILE_IO_H_89578342758342572345
+
+#include "file_error.h"
+#include "serialize.h"
+
+
+namespace zen
+{
+ const char LINE_BREAK[] = "\n"; //since OS X apple uses newline, too
+
+/*
+OS-buffered file IO optimized for
+ - sequential read/write accesses
+ - better error reporting
+ - long path support
+ - follows symlinks
+ */
+class FileBase
+{
+public:
+ const Zstring& getFilePath() const { return filePath_; }
+
+ using FileHandle = int;
+
+ FileHandle getHandle() { return fileHandle_; }
+
+ //Windows: use 64kB ?? https://technet.microsoft.com/en-us/library/cc938632
+ //Linux: use st_blksize?
+ static size_t getBlockSize() { return 128 * 1024; };
+
+protected:
+ FileBase(FileHandle handle, const Zstring& filePath) : fileHandle_(handle), filePath_(filePath) {}
+ ~FileBase();
+
+ void close(); //throw FileError -> optional, but good place to catch errors when closing stream!
+ static const FileHandle invalidHandleValue;
+
+private:
+ FileBase (const FileBase&) = delete;
+ FileBase& operator=(const FileBase&) = delete;
+
+ FileHandle fileHandle_ = invalidHandleValue;
+ const Zstring filePath_;
+};
+
+//-----------------------------------------------------------------------------------------------
+
+class FileInput : public FileBase
+{
+public:
+ FileInput(const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorFileLocked
+ FileInput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership!
+
+ size_t read(void* buffer, size_t bytesToRead); //throw FileError, X; return "bytesToRead" bytes unless end of stream!
+
+private:
+ size_t tryRead(void* buffer, size_t bytesToRead); //throw FileError; may return short, only 0 means EOF! => CONTRACT: bytesToRead > 0!
+
+ std::vector<char> memBuf_;
+ const IOCallback notifyUnbufferedIO_; //throw X
+};
+
+
+class FileOutput : public FileBase
+{
+public:
+ enum AccessFlag
+ {
+ ACC_OVERWRITE,
+ ACC_CREATE_NEW
+ };
+ FileOutput(const Zstring& filePath, AccessFlag access, const IOCallback& notifyUnbufferedIO); //throw FileError, ErrorTargetExisting
+ FileOutput(FileHandle handle, const Zstring& filePath, const IOCallback& notifyUnbufferedIO); //takes ownership!
+ ~FileOutput();
+
+ void preAllocateSpaceBestEffort(uint64_t expectedSize); //throw FileError
+
+ void write(const void* buffer, size_t bytesToWrite); //throw FileError, X
+ void flushBuffers(); //throw FileError, X
+ void finalize(); /*= flushBuffers() + close()*/ //throw FileError, X
+
+private:
+ size_t tryWrite(const void* buffer, size_t bytesToWrite); //throw FileError; may return short! CONTRACT: bytesToWrite > 0
+
+ std::vector<char> memBuf_;
+ IOCallback notifyUnbufferedIO_; //throw X
+};
+
+//-----------------------------------------------------------------------------------------------
+
+//native stream I/O convenience functions:
+
+template <class BinContainer> inline
+BinContainer loadBinContainer(const Zstring& filePath, //throw FileError
+ const IOCallback& notifyUnbufferedIO)
+{
+ FileInput streamIn(filePath, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
+ return bufferedLoad<BinContainer>(streamIn); //throw FileError, X;
+}
+
+
+template <class BinContainer> inline
+void saveBinContainer(const Zstring& filePath, const BinContainer& buffer, //throw FileError
+ const IOCallback& notifyUnbufferedIO)
+{
+ FileOutput fileOut(filePath, FileOutput::ACC_OVERWRITE, notifyUnbufferedIO); //throw FileError, (ErrorTargetExisting)
+ if (!buffer.empty())
+ {
+ /*snake oil?*/ fileOut.preAllocateSpaceBestEffort(buffer.size()); //throw FileError
+ fileOut.write(&*buffer.begin(), buffer.size()); //throw FileError, X
+ }
+ fileOut.finalize(); //throw FileError, X
+}
+}
+
+#endif //FILE_IO_H_89578342758342572345
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index d5e3912b..916b6e8d 100755
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -1,105 +1,105 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "file_traverser.h"
-#include "file_error.h"
-
-
- #include <cstddef> //offsetof
- #include <unistd.h> //::pathconf()
- #include <sys/stat.h>
- #include <dirent.h>
-
-using namespace zen;
-
-
-void zen::traverseFolder(const Zstring& dirPath,
- const std::function<void (const FileInfo& fi)>& onFile,
- const std::function<void (const FolderInfo& fi)>& onFolder,
- const std::function<void (const SymlinkInfo& si)>& onSymlink,
- const std::function<void (const std::wstring& errorMsg)>& onError)
-{
- try
- {
- /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede
- that field within the dirent structure, portable applications that use readdir_r() should allocate
- the buffer whose address is passed in entry as follows:
- len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1
- entryp = malloc(len); */
- const size_t nameMax = std::max<long>(::pathconf(dirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1)
- std::vector<char> buffer(offsetof(struct ::dirent, d_name) + nameMax + 1);
-
- DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/"
- if (!folder)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir");
- ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash
-
- for (;;)
- {
- struct ::dirent* dirEntry = nullptr;
- if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r");
- //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
-
- if (!dirEntry) //no more items
- return;
-
- //don't return "." and ".."
- const char* itemNameRaw = dirEntry->d_name;
-
- if (itemNameRaw[0] == '.' &&
- (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0)))
- continue;
-
- const Zstring& itemName = itemNameRaw;
- if (itemName.empty()) //checks result of osx::normalizeUtfForPosix, too!
- throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name.");
-
- const Zstring& itemPath = appendSeparator(dirPath) + itemName;
-
- struct ::stat statData = {};
- try
- {
- if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat");
- }
- catch (const FileError& e)
- {
- if (onError)
- onError(e.toString());
- continue; //ignore error: skip file
- }
-
- if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks!
- {
- if (onSymlink)
- onSymlink({ itemName, itemPath, statData.st_mtime});
- }
- else if (S_ISDIR(statData.st_mode)) //a directory
- {
- if (onFolder)
- onFolder({ itemName, itemPath });
- }
- else //a file or named pipe, ect.
- {
- if (onFile)
- onFile({ itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime });
- }
- /*
- It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios:
- - RTS setup watch (essentially wants to read directories only)
- - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink")
-
- However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!!
- */
- }
- }
- catch (const FileError& e)
- {
- if (onError)
- onError(e.toString());
- }
-}
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "file_traverser.h"
+#include "file_error.h"
+
+
+ #include <cstddef> //offsetof
+ #include <unistd.h> //::pathconf()
+ #include <sys/stat.h>
+ #include <dirent.h>
+
+using namespace zen;
+
+
+void zen::traverseFolder(const Zstring& dirPath,
+ const std::function<void (const FileInfo& fi)>& onFile,
+ const std::function<void (const FolderInfo& fi)>& onFolder,
+ const std::function<void (const SymlinkInfo& si)>& onSymlink,
+ const std::function<void (const std::wstring& errorMsg)>& onError)
+{
+ try
+ {
+ /* quote: "Since POSIX.1 does not specify the size of the d_name field, and other nonstandard fields may precede
+ that field within the dirent structure, portable applications that use readdir_r() should allocate
+ the buffer whose address is passed in entry as follows:
+ len = offsetof(struct dirent, d_name) + pathconf(dirPath, _PC_NAME_MAX) + 1
+ entryp = malloc(len); */
+ const size_t nameMax = std::max<long>(::pathconf(dirPath.c_str(), _PC_NAME_MAX), 10000); //::pathconf may return long(-1)
+ std::vector<char> buffer(offsetof(struct ::dirent, d_name) + nameMax + 1);
+
+ DIR* folder = ::opendir(dirPath.c_str()); //directory must NOT end with path separator, except "/"
+ if (!folder)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot open directory %x."), L"%x", fmtPath(dirPath)), L"opendir");
+ ZEN_ON_SCOPE_EXIT(::closedir(folder)); //never close nullptr handles! -> crash
+
+ for (;;)
+ {
+ struct ::dirent* dirEntry = nullptr;
+ if (::readdir_r(folder, reinterpret_cast< ::dirent*>(&buffer[0]), &dirEntry) != 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r");
+ //don't retry but restart dir traversal on error! https://blogs.msdn.microsoft.com/oldnewthing/20140612-00/?p=753/
+
+ if (!dirEntry) //no more items
+ return;
+
+ //don't return "." and ".."
+ const char* itemNameRaw = dirEntry->d_name;
+
+ if (itemNameRaw[0] == '.' &&
+ (itemNameRaw[1] == 0 || (itemNameRaw[1] == '.' && itemNameRaw[2] == 0)))
+ continue;
+
+ const Zstring& itemName = itemNameRaw;
+ if (itemName.empty()) //checks result of osx::normalizeUtfForPosix, too!
+ throw FileError(replaceCpy(_("Cannot read directory %x."), L"%x", fmtPath(dirPath)), L"readdir_r: Data corruption; item with empty name.");
+
+ const Zstring& itemPath = appendSeparator(dirPath) + itemName;
+
+ struct ::stat statData = {};
+ try
+ {
+ if (::lstat(itemPath.c_str(), &statData) != 0) //lstat() does not resolve symlinks
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"lstat");
+ }
+ catch (const FileError& e)
+ {
+ if (onError)
+ onError(e.toString());
+ continue; //ignore error: skip file
+ }
+
+ if (S_ISLNK(statData.st_mode)) //on Linux there is no distinction between file and directory symlinks!
+ {
+ if (onSymlink)
+ onSymlink({ itemName, itemPath, statData.st_mtime});
+ }
+ else if (S_ISDIR(statData.st_mode)) //a directory
+ {
+ if (onFolder)
+ onFolder({ itemName, itemPath });
+ }
+ else //a file or named pipe, ect.
+ {
+ if (onFile)
+ onFile({ itemName, itemPath, makeUnsigned(statData.st_size), statData.st_mtime });
+ }
+ /*
+ It may be a good idea to not check "S_ISREG(statData.st_mode)" explicitly and to not issue an error message on other types to support these scenarios:
+ - RTS setup watch (essentially wants to read directories only)
+ - removeDirectory (wants to delete everything; pipes can be deleted just like files via "unlink")
+
+ However an "open" on a pipe will block (https://sourceforge.net/p/freefilesync/bugs/221/), so the copy routines need to be smarter!!
+ */
+ }
+ }
+ catch (const FileError& e)
+ {
+ if (onError)
+ onError(e.toString());
+ }
+}
diff --git a/zen/file_traverser.h b/zen/file_traverser.h
index 0eb3bbee..79bcae11 100755
--- a/zen/file_traverser.h
+++ b/zen/file_traverser.h
@@ -1,47 +1,47 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FILER_TRAVERSER_H_127463214871234
-#define FILER_TRAVERSER_H_127463214871234
-
-#include <cstdint>
-#include <functional>
-#include "zstring.h"
-
-
-namespace zen
-{
-struct FileInfo
-{
- Zstring itemName;
- Zstring fullPath;
- uint64_t fileSize; //[bytes]
- int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC
-};
-
-struct FolderInfo
-{
- Zstring itemName;
- Zstring fullPath;
-};
-
-struct SymlinkInfo
-{
- Zstring itemName;
- Zstring fullPath;
- int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC
-};
-
-//- non-recursive
-//- directory path may end with PATH_SEPARATOR
-void traverseFolder(const Zstring& dirPath, //noexcept
- const std::function<void (const FileInfo& fi)>& onFile, //
- const std::function<void (const FolderInfo& fi)>& onFolder, //optional
- const std::function<void (const SymlinkInfo& si)>& onSymlink, //
- const std::function<void (const std::wstring& errorMsg)>& onError); //
-}
-
-#endif //FILER_TRAVERSER_H_127463214871234
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef FILER_TRAVERSER_H_127463214871234
+#define FILER_TRAVERSER_H_127463214871234
+
+#include <cstdint>
+#include <functional>
+#include "zstring.h"
+
+
+namespace zen
+{
+struct FileInfo
+{
+ Zstring itemName;
+ Zstring fullPath;
+ uint64_t fileSize; //[bytes]
+ int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC
+};
+
+struct FolderInfo
+{
+ Zstring itemName;
+ Zstring fullPath;
+};
+
+struct SymlinkInfo
+{
+ Zstring itemName;
+ Zstring fullPath;
+ int64_t lastWriteTime; //number of seconds since Jan. 1st 1970 UTC
+};
+
+//- non-recursive
+//- directory path may end with PATH_SEPARATOR
+void traverseFolder(const Zstring& dirPath, //noexcept
+ const std::function<void (const FileInfo& fi)>& onFile, //
+ const std::function<void (const FolderInfo& fi)>& onFolder, //optional
+ const std::function<void (const SymlinkInfo& si)>& onSymlink, //
+ const std::function<void (const std::wstring& errorMsg)>& onError); //
+}
+
+#endif //FILER_TRAVERSER_H_127463214871234
diff --git a/zen/fixed_list.h b/zen/fixed_list.h
index e143c7b7..27eb488c 100755
--- a/zen/fixed_list.h
+++ b/zen/fixed_list.h
@@ -1,239 +1,239 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FIXED_LIST_H_01238467085684139453534
-#define FIXED_LIST_H_01238467085684139453534
-
-#include <cassert>
-#include <iterator>
-#include "stl_tools.h"
-
-namespace zen
-{
-//std::list(C++11)-like class for inplace element construction supporting non-copyable/non-movable types
-//-> no iterator invalidation after emplace_back()
-
-template <class T>
-class FixedList
-{
- struct Node
- {
- template <class... Args>
- Node(Args&& ... args) : val(std::forward<Args>(args)...) {}
-
- Node* next = nullptr; //singly-linked list is sufficient
- T val;
- };
-
-public:
- FixedList() {}
-
- ~FixedList() { clear(); }
-
- template <class NodeT, class U>
- class FixedIterator : public std::iterator<std::forward_iterator_tag, U>
- {
- public:
- FixedIterator(NodeT* it = nullptr) : it_(it) {}
- FixedIterator& operator++() { it_ = it_->next; return *this; }
- inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
- inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
- U& operator* () const { return it_->val; }
- U* operator->() const { return &it_->val; }
- private:
- NodeT* it_;
- };
-
- using value_type = T;
- using iterator = FixedIterator< Node, T>;
- using const_iterator = FixedIterator<const Node, const T>;
- using reference = T&;
- using const_reference = const T&;
-
- iterator begin() { return firstInsert_; }
- iterator end () { return iterator(); }
-
- const_iterator begin() const { return firstInsert_; }
- const_iterator end () const { return const_iterator(); }
-
- //const_iterator cbegin() const { return firstInsert_; }
- //const_iterator cend () const { return const_iterator(); }
-
- reference front() { return firstInsert_->val; }
- const_reference front() const { return firstInsert_->val; }
-
- reference& back() { return lastInsert_->val; }
- const_reference& back() const { return lastInsert_->val; }
-
- template <class... Args>
- void emplace_back(Args&& ... args)
- {
- Node* newNode = new Node(std::forward<Args>(args)...);
-
- if (!lastInsert_)
- {
- assert(!firstInsert_ && sz_ == 0);
- firstInsert_ = lastInsert_ = newNode;
- }
- else
- {
- assert(lastInsert_->next == nullptr);
- lastInsert_->next = newNode;
- lastInsert_ = newNode;
- }
- ++sz_;
- }
-
- template <class Predicate>
- void remove_if(Predicate pred)
- {
- Node* prev = nullptr;
- Node* ptr = firstInsert_;
-
- while (ptr)
- if (pred(ptr->val))
- {
- Node* next = ptr->next;
-
- delete ptr;
- assert(sz_ > 0);
- --sz_;
-
- ptr = next;
-
- if (prev)
- prev->next = next;
- else
- firstInsert_ = next;
- if (!next)
- lastInsert_ = prev;
- }
- else
- {
- prev = ptr;
- ptr = ptr->next;
- }
- }
-
- void clear()
- {
- Node* ptr = firstInsert_;
- while (ptr)
- {
- Node* next = ptr->next;
- delete ptr;
- ptr = next;
- }
-
- sz_ = 0;
- firstInsert_ = lastInsert_ = nullptr;
- }
-
- bool empty() const { return sz_ == 0; }
-
- size_t size() const { return sz_; }
-
- void swap(FixedList& other)
- {
- std::swap(firstInsert_, other.firstInsert_);
- std::swap(lastInsert_, other.lastInsert_);
- std::swap(sz_, other.sz_);
- }
-
-private:
- FixedList (const FixedList&) = delete;
- FixedList& operator=(const FixedList&) = delete;
-
- Node* firstInsert_ = nullptr;
- Node* lastInsert_ = nullptr; //point to last insertion; required by efficient emplace_back()
- size_t sz_ = 0;
-};
-
-
-//just as fast as FixedList, but simpler, more CPU-cache-friendly => superseeds FixedList!
-template <class T>
-class FixedVector
-{
-public:
- FixedVector() {}
-
- /*
- class EndIterator {}; //just like FixedList: no iterator invalidation after emplace_back()
-
- template <class V>
- class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this random-access if needed
- {
- public:
- FixedIterator(std::vector<std::unique_ptr<T>>& cont, size_t pos) : cont_(cont), pos_(pos) {}
- FixedIterator& operator++() { ++pos_; return *this; }
- inline friend bool operator==(const FixedIterator& lhs, EndIterator) { return lhs.pos_ == lhs.cont_.size(); }
- inline friend bool operator!=(const FixedIterator& lhs, EndIterator) { return !(lhs == EndIterator()); }
- V& operator* () const { return *cont_[pos_]; }
- V* operator->() const { return &*cont_[pos_]; }
- private:
- std::vector<std::unique_ptr<T>>& cont_;
- size_t pos_ = 0;
- };
- */
-
- template <class IterImpl, class V>
- class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this bidirectional if needed
- {
- public:
- FixedIterator(IterImpl it) : it_(it) {}
- FixedIterator& operator++() { ++it_; return *this; }
- inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
- inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
- V& operator* () const { return **it_; }
- V* operator->() const { return &** it_; }
- private:
- IterImpl it_; //TODO: avoid iterator invalidation after emplace_back(); caveat: end() must not store old length!
- };
-
- using value_type = T;
- using iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::iterator, T>;
- using const_iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::const_iterator, const T>;
- using reference = T&;
- using const_reference = const T&;
-
- iterator begin() { return items_.begin(); }
- iterator end () { return items_.end (); }
-
- const_iterator begin() const { return items_.begin(); }
- const_iterator end () const { return items_.end (); }
-
- reference front() { return *items_.front(); }
- const_reference front() const { return *items_.front(); }
-
- reference& back() { return *items_.back(); }
- const_reference& back() const { return *items_.back(); }
-
- template <class... Args>
- void emplace_back(Args&& ... args)
- {
- items_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
- }
-
- template <class Predicate>
- void remove_if(Predicate pred)
- {
- erase_if(items_, [&](const std::unique_ptr<T>& p) { return pred(*p); });
- }
-
- void clear() { items_.clear(); }
- bool empty() const { return items_.empty(); }
- size_t size () const { return items_.size(); }
- void swap(FixedVector& other) { items_.swap(other.items_); }
-
-private:
- FixedVector (const FixedVector&) = delete;
- FixedVector& operator=(const FixedVector&) = delete;
-
- std::vector<std::unique_ptr<T>> items_;
-};
-}
-
-#endif //FIXED_LIST_H_01238467085684139453534
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef FIXED_LIST_H_01238467085684139453534
+#define FIXED_LIST_H_01238467085684139453534
+
+#include <cassert>
+#include <iterator>
+#include "stl_tools.h"
+
+namespace zen
+{
+//std::list(C++11)-like class for inplace element construction supporting non-copyable/non-movable types
+//-> no iterator invalidation after emplace_back()
+
+template <class T>
+class FixedList
+{
+ struct Node
+ {
+ template <class... Args>
+ Node(Args&& ... args) : val(std::forward<Args>(args)...) {}
+
+ Node* next = nullptr; //singly-linked list is sufficient
+ T val;
+ };
+
+public:
+ FixedList() {}
+
+ ~FixedList() { clear(); }
+
+ template <class NodeT, class U>
+ class FixedIterator : public std::iterator<std::forward_iterator_tag, U>
+ {
+ public:
+ FixedIterator(NodeT* it = nullptr) : it_(it) {}
+ FixedIterator& operator++() { it_ = it_->next; return *this; }
+ inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
+ inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
+ U& operator* () const { return it_->val; }
+ U* operator->() const { return &it_->val; }
+ private:
+ NodeT* it_;
+ };
+
+ using value_type = T;
+ using iterator = FixedIterator< Node, T>;
+ using const_iterator = FixedIterator<const Node, const T>;
+ using reference = T&;
+ using const_reference = const T&;
+
+ iterator begin() { return firstInsert_; }
+ iterator end () { return iterator(); }
+
+ const_iterator begin() const { return firstInsert_; }
+ const_iterator end () const { return const_iterator(); }
+
+ //const_iterator cbegin() const { return firstInsert_; }
+ //const_iterator cend () const { return const_iterator(); }
+
+ reference front() { return firstInsert_->val; }
+ const_reference front() const { return firstInsert_->val; }
+
+ reference& back() { return lastInsert_->val; }
+ const_reference& back() const { return lastInsert_->val; }
+
+ template <class... Args>
+ void emplace_back(Args&& ... args)
+ {
+ Node* newNode = new Node(std::forward<Args>(args)...);
+
+ if (!lastInsert_)
+ {
+ assert(!firstInsert_ && sz_ == 0);
+ firstInsert_ = lastInsert_ = newNode;
+ }
+ else
+ {
+ assert(lastInsert_->next == nullptr);
+ lastInsert_->next = newNode;
+ lastInsert_ = newNode;
+ }
+ ++sz_;
+ }
+
+ template <class Predicate>
+ void remove_if(Predicate pred)
+ {
+ Node* prev = nullptr;
+ Node* ptr = firstInsert_;
+
+ while (ptr)
+ if (pred(ptr->val))
+ {
+ Node* next = ptr->next;
+
+ delete ptr;
+ assert(sz_ > 0);
+ --sz_;
+
+ ptr = next;
+
+ if (prev)
+ prev->next = next;
+ else
+ firstInsert_ = next;
+ if (!next)
+ lastInsert_ = prev;
+ }
+ else
+ {
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ }
+
+ void clear()
+ {
+ Node* ptr = firstInsert_;
+ while (ptr)
+ {
+ Node* next = ptr->next;
+ delete ptr;
+ ptr = next;
+ }
+
+ sz_ = 0;
+ firstInsert_ = lastInsert_ = nullptr;
+ }
+
+ bool empty() const { return sz_ == 0; }
+
+ size_t size() const { return sz_; }
+
+ void swap(FixedList& other)
+ {
+ std::swap(firstInsert_, other.firstInsert_);
+ std::swap(lastInsert_, other.lastInsert_);
+ std::swap(sz_, other.sz_);
+ }
+
+private:
+ FixedList (const FixedList&) = delete;
+ FixedList& operator=(const FixedList&) = delete;
+
+ Node* firstInsert_ = nullptr;
+ Node* lastInsert_ = nullptr; //point to last insertion; required by efficient emplace_back()
+ size_t sz_ = 0;
+};
+
+
+//just as fast as FixedList, but simpler, more CPU-cache-friendly => superseeds FixedList!
+template <class T>
+class FixedVector
+{
+public:
+ FixedVector() {}
+
+ /*
+ class EndIterator {}; //just like FixedList: no iterator invalidation after emplace_back()
+
+ template <class V>
+ class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this random-access if needed
+ {
+ public:
+ FixedIterator(std::vector<std::unique_ptr<T>>& cont, size_t pos) : cont_(cont), pos_(pos) {}
+ FixedIterator& operator++() { ++pos_; return *this; }
+ inline friend bool operator==(const FixedIterator& lhs, EndIterator) { return lhs.pos_ == lhs.cont_.size(); }
+ inline friend bool operator!=(const FixedIterator& lhs, EndIterator) { return !(lhs == EndIterator()); }
+ V& operator* () const { return *cont_[pos_]; }
+ V* operator->() const { return &*cont_[pos_]; }
+ private:
+ std::vector<std::unique_ptr<T>>& cont_;
+ size_t pos_ = 0;
+ };
+ */
+
+ template <class IterImpl, class V>
+ class FixedIterator : public std::iterator<std::forward_iterator_tag, V> //could make this bidirectional if needed
+ {
+ public:
+ FixedIterator(IterImpl it) : it_(it) {}
+ FixedIterator& operator++() { ++it_; return *this; }
+ inline friend bool operator==(const FixedIterator& lhs, const FixedIterator& rhs) { return lhs.it_ == rhs.it_; }
+ inline friend bool operator!=(const FixedIterator& lhs, const FixedIterator& rhs) { return !(lhs == rhs); }
+ V& operator* () const { return **it_; }
+ V* operator->() const { return &** it_; }
+ private:
+ IterImpl it_; //TODO: avoid iterator invalidation after emplace_back(); caveat: end() must not store old length!
+ };
+
+ using value_type = T;
+ using iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::iterator, T>;
+ using const_iterator = FixedIterator<typename std::vector<std::unique_ptr<T>>::const_iterator, const T>;
+ using reference = T&;
+ using const_reference = const T&;
+
+ iterator begin() { return items_.begin(); }
+ iterator end () { return items_.end (); }
+
+ const_iterator begin() const { return items_.begin(); }
+ const_iterator end () const { return items_.end (); }
+
+ reference front() { return *items_.front(); }
+ const_reference front() const { return *items_.front(); }
+
+ reference& back() { return *items_.back(); }
+ const_reference& back() const { return *items_.back(); }
+
+ template <class... Args>
+ void emplace_back(Args&& ... args)
+ {
+ items_.push_back(std::make_unique<T>(std::forward<Args>(args)...));
+ }
+
+ template <class Predicate>
+ void remove_if(Predicate pred)
+ {
+ erase_if(items_, [&](const std::unique_ptr<T>& p) { return pred(*p); });
+ }
+
+ void clear() { items_.clear(); }
+ bool empty() const { return items_.empty(); }
+ size_t size () const { return items_.size(); }
+ void swap(FixedVector& other) { items_.swap(other.items_); }
+
+private:
+ FixedVector (const FixedVector&) = delete;
+ FixedVector& operator=(const FixedVector&) = delete;
+
+ std::vector<std::unique_ptr<T>> items_;
+};
+}
+
+#endif //FIXED_LIST_H_01238467085684139453534
diff --git a/zen/format_unit.cpp b/zen/format_unit.cpp
index a2208b3e..c0667fb1 100755
--- a/zen/format_unit.cpp
+++ b/zen/format_unit.cpp
@@ -1,201 +1,201 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "format_unit.h"
-//#include <cwchar> //swprintf
-#include <ctime>
-#include <cstdio>
-#include "basic_math.h"
-#include "i18n.h"
-#include "time.h"
-#include "globals.h"
-
- #include <clocale> //thousands separator
- #include "utf.h" //
-
-using namespace zen;
-
-
-std::wstring zen::formatTwoDigitPrecision(double value)
-{
- //print two digits: 0,1 | 1,1 | 11
- if (numeric::abs(value) < 9.95) //9.99 must not be formatted as "10.0"
- return printNumber<std::wstring>(L"%.1f", value);
- return numberTo<std::wstring>(numeric::round(value));
-}
-
-
-std::wstring zen::formatThreeDigitPrecision(double value)
-{
- //print three digits: 0,01 | 0,11 | 1,11 | 11,1 | 111
- if (numeric::abs(value) < 9.995) //9.999 must not be formatted as "10.00"
- return printNumber<std::wstring>(L"%.2f", value);
- if (numeric::abs(value) < 99.95) //99.99 must not be formatted as "100.0"
- return printNumber<std::wstring>(L"%.1f", value);
- return numberTo<std::wstring>(numeric::round(value));
-}
-
-
-std::wstring zen::filesizeToShortString(int64_t size)
-{
- //if (size < 0) return _("Error"); -> really?
-
- if (numeric::abs(size) <= 999)
- return _P("1 byte", "%x bytes", static_cast<int>(size));
-
- double sizeInUnit = static_cast<double>(size);
-
- auto formatUnit = [&](const std::wstring& unitTxt) { return replaceCpy(unitTxt, L"%x", formatThreeDigitPrecision(sizeInUnit)); };
-
- sizeInUnit /= 1024;
- if (numeric::abs(sizeInUnit) < 999.5)
- return formatUnit(_("%x KB"));
-
- sizeInUnit /= 1024;
- if (numeric::abs(sizeInUnit) < 999.5)
- return formatUnit(_("%x MB"));
-
- sizeInUnit /= 1024;
- if (numeric::abs(sizeInUnit) < 999.5)
- return formatUnit(_("%x GB"));
-
- sizeInUnit /= 1024;
- if (numeric::abs(sizeInUnit) < 999.5)
- return formatUnit(_("%x TB"));
-
- sizeInUnit /= 1024;
- return formatUnit(_("%x PB"));
-}
-
-
-namespace
-{
-enum UnitRemTime
-{
- URT_SEC,
- URT_MIN,
- URT_HOUR,
- URT_DAY
-};
-
-
-std::wstring formatUnitTime(int val, UnitRemTime unit)
-{
- switch (unit)
- {
- case URT_SEC:
- return _P("1 sec", "%x sec", val);
- case URT_MIN:
- return _P("1 min", "%x min", val);
- case URT_HOUR:
- return _P("1 hour", "%x hours", val);
- case URT_DAY:
- return _P("1 day", "%x days", val);
- }
- assert(false);
- return _("Error");
-}
-
-
-template <int M, int N>
-std::wstring roundToBlock(double timeInHigh,
- UnitRemTime unitHigh, const int (&stepsHigh)[M],
- int unitLowPerHigh,
- UnitRemTime unitLow, const int (&stepsLow)[N])
-{
- assert(unitLowPerHigh > 0);
- const double granularity = 0.1;
- const double timeInLow = timeInHigh * unitLowPerHigh;
- const int blockSizeLow = granularity * timeInHigh < 1 ?
- numeric::nearMatch(granularity * timeInLow, std::begin(stepsLow), std::end(stepsLow)):
- numeric::nearMatch(granularity * timeInHigh, std::begin(stepsHigh), std::end(stepsHigh)) * unitLowPerHigh;
- const int roundedtimeInLow = numeric::round(timeInLow / blockSizeLow) * blockSizeLow;
-
- std::wstring output = formatUnitTime(roundedtimeInLow / unitLowPerHigh, unitHigh);
- if (unitLowPerHigh > blockSizeLow)
- output += L" " + formatUnitTime(roundedtimeInLow % unitLowPerHigh, unitLow);
- return output;
-};
-}
-
-
-std::wstring zen::remainingTimeToString(double timeInSec)
-{
- const int steps10[] = { 1, 2, 5, 10 };
- const int steps24[] = { 1, 2, 3, 4, 6, 8, 12, 24 };
- const int steps60[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
-
- //determine preferred unit
- double timeInUnit = timeInSec;
- if (timeInUnit <= 60)
- return roundToBlock(timeInUnit, URT_SEC, steps60, 1, URT_SEC, steps60);
-
- timeInUnit /= 60;
- if (timeInUnit <= 60)
- return roundToBlock(timeInUnit, URT_MIN, steps60, 60, URT_SEC, steps60);
-
- timeInUnit /= 60;
- if (timeInUnit <= 24)
- return roundToBlock(timeInUnit, URT_HOUR, steps24, 60, URT_MIN, steps60);
-
- timeInUnit /= 24;
- return roundToBlock(timeInUnit, URT_DAY, steps10, 24, URT_HOUR, steps24);
- //note: for 10% granularity steps10 yields a valid blocksize only up to timeInUnit == 100!
- //for larger time sizes this results in a finer granularity than expected: 10 days -> should not be a problem considering "usual" remaining time for synchronization
-}
-
-
-//std::wstring zen::fractionToString1Dec(double fraction)
-//{
-// return printNumber<std::wstring>(L"%.1f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
-//}
-
-
-std::wstring zen::fractionToString(double fraction)
-{
- return printNumber<std::wstring>(L"%.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
-}
-
-
-
-
-std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
-{
- //we have to include thousands separator ourselves; this doesn't work for all countries (e.g india), but is better than nothing
-
- //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale
- const lconv* localInfo = ::localeconv(); //always bound according to doc
- const std::wstring& thousandSep = utfTo<std::wstring>(localInfo->thousands_sep);
-
- // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t>>(std::locale("")).thousands_sep(); - why not working?
- // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t>>(std::locale("")).decimal_point();
-
- std::wstring output(number);
- size_t i = output.size();
- for (;;)
- {
- if (i <= 3)
- break;
- i -= 3;
- if (!isDigit(output[i - 1])) //stop on +, - signs
- break;
- output.insert(i, thousandSep);
- }
- return output;
-}
-
-
-std::wstring zen::utcToLocalTimeString(int64_t utcTime)
-{
- auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L")"; };
-
- TimeComp loc = getLocalTime(utcTime);
-
- std::wstring dateString = formatTime<std::wstring>(L"%x %X", loc);
- return !dateString.empty() ? dateString : errorMsg();
-}
-
-
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "format_unit.h"
+//#include <cwchar> //swprintf
+#include <ctime>
+#include <cstdio>
+#include "basic_math.h"
+#include "i18n.h"
+#include "time.h"
+#include "globals.h"
+
+ #include <clocale> //thousands separator
+ #include "utf.h" //
+
+using namespace zen;
+
+
+std::wstring zen::formatTwoDigitPrecision(double value)
+{
+ //print two digits: 0,1 | 1,1 | 11
+ if (numeric::abs(value) < 9.95) //9.99 must not be formatted as "10.0"
+ return printNumber<std::wstring>(L"%.1f", value);
+ return numberTo<std::wstring>(numeric::round(value));
+}
+
+
+std::wstring zen::formatThreeDigitPrecision(double value)
+{
+ //print three digits: 0,01 | 0,11 | 1,11 | 11,1 | 111
+ if (numeric::abs(value) < 9.995) //9.999 must not be formatted as "10.00"
+ return printNumber<std::wstring>(L"%.2f", value);
+ if (numeric::abs(value) < 99.95) //99.99 must not be formatted as "100.0"
+ return printNumber<std::wstring>(L"%.1f", value);
+ return numberTo<std::wstring>(numeric::round(value));
+}
+
+
+std::wstring zen::filesizeToShortString(int64_t size)
+{
+ //if (size < 0) return _("Error"); -> really?
+
+ if (numeric::abs(size) <= 999)
+ return _P("1 byte", "%x bytes", static_cast<int>(size));
+
+ double sizeInUnit = static_cast<double>(size);
+
+ auto formatUnit = [&](const std::wstring& unitTxt) { return replaceCpy(unitTxt, L"%x", formatThreeDigitPrecision(sizeInUnit)); };
+
+ sizeInUnit /= 1024;
+ if (numeric::abs(sizeInUnit) < 999.5)
+ return formatUnit(_("%x KB"));
+
+ sizeInUnit /= 1024;
+ if (numeric::abs(sizeInUnit) < 999.5)
+ return formatUnit(_("%x MB"));
+
+ sizeInUnit /= 1024;
+ if (numeric::abs(sizeInUnit) < 999.5)
+ return formatUnit(_("%x GB"));
+
+ sizeInUnit /= 1024;
+ if (numeric::abs(sizeInUnit) < 999.5)
+ return formatUnit(_("%x TB"));
+
+ sizeInUnit /= 1024;
+ return formatUnit(_("%x PB"));
+}
+
+
+namespace
+{
+enum UnitRemTime
+{
+ URT_SEC,
+ URT_MIN,
+ URT_HOUR,
+ URT_DAY
+};
+
+
+std::wstring formatUnitTime(int val, UnitRemTime unit)
+{
+ switch (unit)
+ {
+ case URT_SEC:
+ return _P("1 sec", "%x sec", val);
+ case URT_MIN:
+ return _P("1 min", "%x min", val);
+ case URT_HOUR:
+ return _P("1 hour", "%x hours", val);
+ case URT_DAY:
+ return _P("1 day", "%x days", val);
+ }
+ assert(false);
+ return _("Error");
+}
+
+
+template <int M, int N>
+std::wstring roundToBlock(double timeInHigh,
+ UnitRemTime unitHigh, const int (&stepsHigh)[M],
+ int unitLowPerHigh,
+ UnitRemTime unitLow, const int (&stepsLow)[N])
+{
+ assert(unitLowPerHigh > 0);
+ const double granularity = 0.1;
+ const double timeInLow = timeInHigh * unitLowPerHigh;
+ const int blockSizeLow = granularity * timeInHigh < 1 ?
+ numeric::nearMatch(granularity * timeInLow, std::begin(stepsLow), std::end(stepsLow)):
+ numeric::nearMatch(granularity * timeInHigh, std::begin(stepsHigh), std::end(stepsHigh)) * unitLowPerHigh;
+ const int roundedtimeInLow = numeric::round(timeInLow / blockSizeLow) * blockSizeLow;
+
+ std::wstring output = formatUnitTime(roundedtimeInLow / unitLowPerHigh, unitHigh);
+ if (unitLowPerHigh > blockSizeLow)
+ output += L" " + formatUnitTime(roundedtimeInLow % unitLowPerHigh, unitLow);
+ return output;
+};
+}
+
+
+std::wstring zen::remainingTimeToString(double timeInSec)
+{
+ const int steps10[] = { 1, 2, 5, 10 };
+ const int steps24[] = { 1, 2, 3, 4, 6, 8, 12, 24 };
+ const int steps60[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
+
+ //determine preferred unit
+ double timeInUnit = timeInSec;
+ if (timeInUnit <= 60)
+ return roundToBlock(timeInUnit, URT_SEC, steps60, 1, URT_SEC, steps60);
+
+ timeInUnit /= 60;
+ if (timeInUnit <= 60)
+ return roundToBlock(timeInUnit, URT_MIN, steps60, 60, URT_SEC, steps60);
+
+ timeInUnit /= 60;
+ if (timeInUnit <= 24)
+ return roundToBlock(timeInUnit, URT_HOUR, steps24, 60, URT_MIN, steps60);
+
+ timeInUnit /= 24;
+ return roundToBlock(timeInUnit, URT_DAY, steps10, 24, URT_HOUR, steps24);
+ //note: for 10% granularity steps10 yields a valid blocksize only up to timeInUnit == 100!
+ //for larger time sizes this results in a finer granularity than expected: 10 days -> should not be a problem considering "usual" remaining time for synchronization
+}
+
+
+//std::wstring zen::fractionToString1Dec(double fraction)
+//{
+// return printNumber<std::wstring>(L"%.1f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
+//}
+
+
+std::wstring zen::fractionToString(double fraction)
+{
+ return printNumber<std::wstring>(L"%.2f", fraction * 100.0) + L'%'; //no need to internationalize fraction!?
+}
+
+
+
+
+std::wstring zen::ffs_Impl::includeNumberSeparator(const std::wstring& number)
+{
+ //we have to include thousands separator ourselves; this doesn't work for all countries (e.g india), but is better than nothing
+
+ //::setlocale (LC_ALL, ""); -> implicitly called by wxLocale
+ const lconv* localInfo = ::localeconv(); //always bound according to doc
+ const std::wstring& thousandSep = utfTo<std::wstring>(localInfo->thousands_sep);
+
+ // THOUSANDS_SEPARATOR = std::use_facet<std::numpunct<wchar_t>>(std::locale("")).thousands_sep(); - why not working?
+ // DECIMAL_POINT = std::use_facet<std::numpunct<wchar_t>>(std::locale("")).decimal_point();
+
+ std::wstring output(number);
+ size_t i = output.size();
+ for (;;)
+ {
+ if (i <= 3)
+ break;
+ i -= 3;
+ if (!isDigit(output[i - 1])) //stop on +, - signs
+ break;
+ output.insert(i, thousandSep);
+ }
+ return output;
+}
+
+
+std::wstring zen::utcToLocalTimeString(int64_t utcTime)
+{
+ auto errorMsg = [&] { return _("Error") + L" (time_t: " + numberTo<std::wstring>(utcTime) + L")"; };
+
+ TimeComp loc = getLocalTime(utcTime);
+
+ std::wstring dateString = formatTime<std::wstring>(L"%x %X", loc);
+ return !dateString.empty() ? dateString : errorMsg();
+}
+
+
diff --git a/zen/format_unit.h b/zen/format_unit.h
index fc79738a..336df74c 100755
--- a/zen/format_unit.h
+++ b/zen/format_unit.h
@@ -1,53 +1,53 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef FMT_UNIT_8702184019487324
-#define FMT_UNIT_8702184019487324
-
-#include <string>
-#include <cstdint>
-#include "optional.h"
-#include "string_tools.h"
-
-
-namespace zen
-{
-std::wstring filesizeToShortString(int64_t filesize);
-std::wstring remainingTimeToString(double timeInSec);
-std::wstring fractionToString(double fraction); //within [0, 1]
-std::wstring utcToLocalTimeString(int64_t utcTime); //like Windows Explorer would...
-
-std::wstring formatTwoDigitPrecision (double value); //format with fixed number of digits
-std::wstring formatThreeDigitPrecision(double value); //(unless value is too large)
-
-template <class NumberType>
-std::wstring toGuiString(NumberType number); //format integer number including thousands separator
-
-
-
-
-
-
-
-
-
-
-
-//--------------- inline impelementation -------------------------------------------
-namespace ffs_Impl
-{
-std::wstring includeNumberSeparator(const std::wstring& number);
-}
-
-template <class NumberType> inline
-std::wstring toGuiString(NumberType number)
-{
- static_assert(IsInteger<NumberType>::value, "");
- return ffs_Impl::includeNumberSeparator(zen::numberTo<std::wstring>(number));
-}
-}
-
-#endif
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef FMT_UNIT_8702184019487324
+#define FMT_UNIT_8702184019487324
+
+#include <string>
+#include <cstdint>
+#include "optional.h"
+#include "string_tools.h"
+
+
+namespace zen
+{
+std::wstring filesizeToShortString(int64_t filesize);
+std::wstring remainingTimeToString(double timeInSec);
+std::wstring fractionToString(double fraction); //within [0, 1]
+std::wstring utcToLocalTimeString(int64_t utcTime); //like Windows Explorer would...
+
+std::wstring formatTwoDigitPrecision (double value); //format with fixed number of digits
+std::wstring formatThreeDigitPrecision(double value); //(unless value is too large)
+
+template <class NumberType>
+std::wstring toGuiString(NumberType number); //format integer number including thousands separator
+
+
+
+
+
+
+
+
+
+
+
+//--------------- inline impelementation -------------------------------------------
+namespace ffs_Impl
+{
+std::wstring includeNumberSeparator(const std::wstring& number);
+}
+
+template <class NumberType> inline
+std::wstring toGuiString(NumberType number)
+{
+ static_assert(IsInteger<NumberType>::value, "");
+ return ffs_Impl::includeNumberSeparator(zen::numberTo<std::wstring>(number));
+}
+}
+
+#endif
diff --git a/zen/globals.h b/zen/globals.h
index b6c5dd28..b85d5f77 100755
--- a/zen/globals.h
+++ b/zen/globals.h
@@ -1,65 +1,65 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef GLOBALS_H_8013740213748021573485
-#define GLOBALS_H_8013740213748021573485
-
-#include <atomic>
-#include <memory>
-#include "scope_guard.h"
-
-namespace zen
-{
-//solve static destruction order fiasco by providing shared ownership and serialized access to global variables
-template <class T>
-class Global
-{
-public:
- Global()
- {
- static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever");
- assert(!pod.inst && !pod.spinLock); //we depend on static zero-initialization!
- }
- explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); }
- ~Global() { set(nullptr); }
-
- std::shared_ptr<T> get() //=> return std::shared_ptr to let instance life time be handled by caller (MT usage!)
- {
- while (pod.spinLock.exchange(true)) ;
- ZEN_ON_SCOPE_EXIT(pod.spinLock = false);
- if (pod.inst)
- return *pod.inst;
- return nullptr;
- }
-
- void set(std::unique_ptr<T>&& newInst)
- {
- std::shared_ptr<T>* tmpInst = nullptr;
- if (newInst)
- tmpInst = new std::shared_ptr<T>(std::move(newInst));
- {
- while (pod.spinLock.exchange(true)) ;
- ZEN_ON_SCOPE_EXIT(pod.spinLock = false);
- std::swap(pod.inst, tmpInst);
- }
- delete tmpInst;
- }
-
-private:
- //avoid static destruction order fiasco: there may be accesses to "Global<T>::get()" during process shutdown
- //e.g. _("") used by message in debug_minidump.cpp or by some detached thread assembling an error message!
- //=> use trivially-destructible POD only!!!
- struct Pod
- {
- std::shared_ptr<T>* inst; // = nullptr;
- std::atomic<bool> spinLock; // { false }; rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction!
- //serialize access; can't use std::mutex: has non-trival destructor
- } pod;
-};
-
-}
-
-#endif //GLOBALS_H_8013740213748021573485
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef GLOBALS_H_8013740213748021573485
+#define GLOBALS_H_8013740213748021573485
+
+#include <atomic>
+#include <memory>
+#include "scope_guard.h"
+
+namespace zen
+{
+//solve static destruction order fiasco by providing shared ownership and serialized access to global variables
+template <class T>
+class Global
+{
+public:
+ Global()
+ {
+ static_assert(std::is_trivially_destructible<Pod>::value, "this memory needs to live forever");
+ assert(!pod.inst && !pod.spinLock); //we depend on static zero-initialization!
+ }
+ explicit Global(std::unique_ptr<T>&& newInst) { set(std::move(newInst)); }
+ ~Global() { set(nullptr); }
+
+ std::shared_ptr<T> get() //=> return std::shared_ptr to let instance life time be handled by caller (MT usage!)
+ {
+ while (pod.spinLock.exchange(true)) ;
+ ZEN_ON_SCOPE_EXIT(pod.spinLock = false);
+ if (pod.inst)
+ return *pod.inst;
+ return nullptr;
+ }
+
+ void set(std::unique_ptr<T>&& newInst)
+ {
+ std::shared_ptr<T>* tmpInst = nullptr;
+ if (newInst)
+ tmpInst = new std::shared_ptr<T>(std::move(newInst));
+ {
+ while (pod.spinLock.exchange(true)) ;
+ ZEN_ON_SCOPE_EXIT(pod.spinLock = false);
+ std::swap(pod.inst, tmpInst);
+ }
+ delete tmpInst;
+ }
+
+private:
+ //avoid static destruction order fiasco: there may be accesses to "Global<T>::get()" during process shutdown
+ //e.g. _("") used by message in debug_minidump.cpp or by some detached thread assembling an error message!
+ //=> use trivially-destructible POD only!!!
+ struct Pod
+ {
+ std::shared_ptr<T>* inst; // = nullptr;
+ std::atomic<bool> spinLock; // { false }; rely entirely on static zero-initialization! => avoid potential contention with worker thread during Global<> construction!
+ //serialize access; can't use std::mutex: has non-trival destructor
+ } pod;
+};
+
+}
+
+#endif //GLOBALS_H_8013740213748021573485
diff --git a/zen/guid.h b/zen/guid.h
index 052f3fdc..d88578a6 100755
--- a/zen/guid.h
+++ b/zen/guid.h
@@ -1,30 +1,30 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef GUID_H_80425780237502345
-#define GUID_H_80425780237502345
-
-#include <string>
-
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#include <boost/uuid/uuid_generators.hpp>
- #pragma GCC diagnostic pop
-
-
-namespace zen
-{
-inline
-std::string generateGUID() //creates a 16-byte GUID
-{
- boost::uuids::uuid nativeRep = boost::uuids::random_generator()();
- //generator is only thread-safe like an int, so we keep it local until we need to optimize perf
- //perf: generator: 0.22ms per call; retrieve GUID: 0.12µs per call
- return std::string(nativeRep.begin(), nativeRep.end());
-}
-}
-
-#endif //GUID_H_80425780237502345
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef GUID_H_80425780237502345
+#define GUID_H_80425780237502345
+
+#include <string>
+
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#include <boost/uuid/uuid_generators.hpp>
+ #pragma GCC diagnostic pop
+
+
+namespace zen
+{
+inline
+std::string generateGUID() //creates a 16-byte GUID
+{
+ boost::uuids::uuid nativeRep = boost::uuids::random_generator()();
+ //generator is only thread-safe like an int, so we keep it local until we need to optimize perf
+ //perf: generator: 0.22ms per call; retrieve GUID: 0.12µs per call
+ return std::string(nativeRep.begin(), nativeRep.end());
+}
+}
+
+#endif //GUID_H_80425780237502345
diff --git a/zen/i18n.h b/zen/i18n.h
index ef3f3706..c7d1d5dc 100755
--- a/zen/i18n.h
+++ b/zen/i18n.h
@@ -1,119 +1,119 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef I18_N_H_3843489325044253425456
-#define I18_N_H_3843489325044253425456
-
-#include <string>
-#include <cstdint>
-#include "globals.h"
-#include "string_tools.h"
-#include "format_unit.h"
-
-//minimal layer enabling text translation - without platform/library dependencies!
-
-#define ZEN_TRANS_CONCAT_SUB(X, Y) X ## Y
-#define _(s) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s))
-#define _P(s, p, n) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s), ZEN_TRANS_CONCAT_SUB(L, p), n)
-//source and translation are required to use %x as number placeholder
-//for plural form, which will be substituted automatically!!!
-
-namespace zen
-{
-//implement handler to enable program-wide localizations:
-struct TranslationHandler
-{
- //THREAD-SAFETY: "const" member must model thread-safe access!
- TranslationHandler() {}
- virtual ~TranslationHandler() {}
-
- //C++11: std::wstring should be thread-safe like an int
- virtual std::wstring translate(const std::wstring& text) const = 0; //simple translation
- virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n) const = 0;
-
-private:
- TranslationHandler (const TranslationHandler&) = delete;
- TranslationHandler& operator=(const TranslationHandler&) = delete;
-};
-
-void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler); //take ownership
-std::shared_ptr<const TranslationHandler> getTranslator();
-
-
-
-
-
-
-
-
-
-
-
-
-
-//######################## implementation ##############################
-namespace implementation
-{
-inline
-std::wstring translate(const std::wstring& text)
-{
- if (std::shared_ptr<const TranslationHandler> t = getTranslator()) //std::shared_ptr => temporarily take (shared) ownership while using the interface!
- return t->translate(text);
- return text;
-}
-
-
-//translate plural forms: "%x day" "%x days"
-//returns "1 day" if n == 1; "123 days" if n == 123 for english language
-inline
-std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n)
-{
- assert(contains(plural, L"%x"));
-
- if (std::shared_ptr<const TranslationHandler> t = getTranslator())
- {
- std::wstring translation = t->translate(singular, plural, n);
- assert(!contains(translation, L"%x"));
- return translation;
- }
-
- return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n));
-}
-
-
-template <class T> inline
-std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n)
-{
- static_assert(sizeof(n) <= sizeof(int64_t), "");
- return translate(singular, plural, static_cast<int64_t>(n));
-}
-
-
-inline
-Global<const TranslationHandler>& refGlobalTranslationHandler()
-{
- //getTranslator() may be called even after static objects of this translation unit are destroyed!
- static Global<const TranslationHandler> inst; //external linkage even in header!
- return inst;
-}
-}
-
-
-inline
-void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler)
-{
- implementation::refGlobalTranslationHandler().set(std::move(newHandler));
-}
-
-
-inline
-std::shared_ptr<const TranslationHandler> getTranslator()
-{
- return implementation::refGlobalTranslationHandler().get();
-}
-}
-
-#endif //I18_N_H_3843489325044253425456
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef I18_N_H_3843489325044253425456
+#define I18_N_H_3843489325044253425456
+
+#include <string>
+#include <cstdint>
+#include "globals.h"
+#include "string_tools.h"
+#include "format_unit.h"
+
+//minimal layer enabling text translation - without platform/library dependencies!
+
+#define ZEN_TRANS_CONCAT_SUB(X, Y) X ## Y
+#define _(s) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s))
+#define _P(s, p, n) zen::implementation::translate(ZEN_TRANS_CONCAT_SUB(L, s), ZEN_TRANS_CONCAT_SUB(L, p), n)
+//source and translation are required to use %x as number placeholder
+//for plural form, which will be substituted automatically!!!
+
+namespace zen
+{
+//implement handler to enable program-wide localizations:
+struct TranslationHandler
+{
+ //THREAD-SAFETY: "const" member must model thread-safe access!
+ TranslationHandler() {}
+ virtual ~TranslationHandler() {}
+
+ //C++11: std::wstring should be thread-safe like an int
+ virtual std::wstring translate(const std::wstring& text) const = 0; //simple translation
+ virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n) const = 0;
+
+private:
+ TranslationHandler (const TranslationHandler&) = delete;
+ TranslationHandler& operator=(const TranslationHandler&) = delete;
+};
+
+void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler); //take ownership
+std::shared_ptr<const TranslationHandler> getTranslator();
+
+
+
+
+
+
+
+
+
+
+
+
+
+//######################## implementation ##############################
+namespace implementation
+{
+inline
+std::wstring translate(const std::wstring& text)
+{
+ if (std::shared_ptr<const TranslationHandler> t = getTranslator()) //std::shared_ptr => temporarily take (shared) ownership while using the interface!
+ return t->translate(text);
+ return text;
+}
+
+
+//translate plural forms: "%x day" "%x days"
+//returns "1 day" if n == 1; "123 days" if n == 123 for english language
+inline
+std::wstring translate(const std::wstring& singular, const std::wstring& plural, int64_t n)
+{
+ assert(contains(plural, L"%x"));
+
+ if (std::shared_ptr<const TranslationHandler> t = getTranslator())
+ {
+ std::wstring translation = t->translate(singular, plural, n);
+ assert(!contains(translation, L"%x"));
+ return translation;
+ }
+
+ return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n));
+}
+
+
+template <class T> inline
+std::wstring translate(const std::wstring& singular, const std::wstring& plural, T n)
+{
+ static_assert(sizeof(n) <= sizeof(int64_t), "");
+ return translate(singular, plural, static_cast<int64_t>(n));
+}
+
+
+inline
+Global<const TranslationHandler>& refGlobalTranslationHandler()
+{
+ //getTranslator() may be called even after static objects of this translation unit are destroyed!
+ static Global<const TranslationHandler> inst; //external linkage even in header!
+ return inst;
+}
+}
+
+
+inline
+void setTranslator(std::unique_ptr<const TranslationHandler>&& newHandler)
+{
+ implementation::refGlobalTranslationHandler().set(std::move(newHandler));
+}
+
+
+inline
+std::shared_ptr<const TranslationHandler> getTranslator()
+{
+ return implementation::refGlobalTranslationHandler().get();
+}
+}
+
+#endif //I18_N_H_3843489325044253425456
diff --git a/zen/optional.h b/zen/optional.h
index a4c67984..0efc34fe 100755
--- a/zen/optional.h
+++ b/zen/optional.h
@@ -1,100 +1,100 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef OPTIONAL_H_2857428578342203589
-#define OPTIONAL_H_2857428578342203589
-
-#include <cassert>
-#include <type_traits>
-
-namespace zen
-{
-/*
-Optional return value without heap memory allocation!
- -> interface like a pointer, performance like a value
-
- Usage:
- ------
- Opt<MyEnum> someFunction();
-{
- if (allIsWell)
- return enumVal;
- else
- return NoValue();
-}
-
- Opt<MyEnum> optValue = someFunction();
- if (optValue)
- ... use *optValue ...
-*/
-
-struct NoValue {};
-
-template <class T>
-class Opt
-{
-public:
- Opt() {}
- Opt(NoValue) {}
- Opt(const T& val) : valid_(true) { new (&rawMem_) T(val); } //throw X
- Opt( T&& tmp) : valid_(true) { new (&rawMem_) T(std::move(tmp)); }
-
- Opt(const Opt& other) : valid_(other.valid_)
- {
- if (const T* val = other.get())
- new (&rawMem_) T(*val); //throw X
- }
-
- ~Opt() { if (T* val = get()) val->~T(); }
-
- Opt& operator=(NoValue) //support assignment to Opt<const T>
- {
- if (T* val = get())
- {
- valid_ = false;
- val->~T();
- }
- return *this;
- }
-
- Opt& operator=(const Opt& other) //strong exception-safety iff T::operator=() is strongly exception-safe
- {
- if (T* val = get())
- {
- if (const T* valOther = other.get())
- *val = *valOther; //throw X
- else
- {
- valid_ = false;
- val->~T();
- }
- }
- else if (const T* valOther = other.get())
- {
- new (&rawMem_) T(*valOther); //throw X
- valid_ = true;
- }
- return *this;
- }
-
- explicit operator bool() const { return valid_; } //thank you, C++11!!!
-
- const T* get() const { return valid_ ? reinterpret_cast<const T*>(&rawMem_) : nullptr; }
- T* get() { return valid_ ? reinterpret_cast< T*>(&rawMem_) : nullptr; }
-
- const T& operator*() const { return *get(); }
- /**/ T& operator*() { return *get(); }
-
- const T* operator->() const { return get(); }
- /**/ T* operator->() { return get(); }
-
-private:
- std::aligned_storage_t<sizeof(T), alignof(T)> rawMem_; //don't require T to be default-constructible!
- bool valid_ = false;
-};
-}
-
-#endif //OPTIONAL_H_2857428578342203589
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef OPTIONAL_H_2857428578342203589
+#define OPTIONAL_H_2857428578342203589
+
+#include <cassert>
+#include <type_traits>
+
+namespace zen
+{
+/*
+Optional return value without heap memory allocation!
+ -> interface like a pointer, performance like a value
+
+ Usage:
+ ------
+ Opt<MyEnum> someFunction();
+{
+ if (allIsWell)
+ return enumVal;
+ else
+ return NoValue();
+}
+
+ Opt<MyEnum> optValue = someFunction();
+ if (optValue)
+ ... use *optValue ...
+*/
+
+struct NoValue {};
+
+template <class T>
+class Opt
+{
+public:
+ Opt() {}
+ Opt(NoValue) {}
+ Opt(const T& val) : valid_(true) { new (&rawMem_) T(val); } //throw X
+ Opt( T&& tmp) : valid_(true) { new (&rawMem_) T(std::move(tmp)); }
+
+ Opt(const Opt& other) : valid_(other.valid_)
+ {
+ if (const T* val = other.get())
+ new (&rawMem_) T(*val); //throw X
+ }
+
+ ~Opt() { if (T* val = get()) val->~T(); }
+
+ Opt& operator=(NoValue) //support assignment to Opt<const T>
+ {
+ if (T* val = get())
+ {
+ valid_ = false;
+ val->~T();
+ }
+ return *this;
+ }
+
+ Opt& operator=(const Opt& other) //strong exception-safety iff T::operator=() is strongly exception-safe
+ {
+ if (T* val = get())
+ {
+ if (const T* valOther = other.get())
+ *val = *valOther; //throw X
+ else
+ {
+ valid_ = false;
+ val->~T();
+ }
+ }
+ else if (const T* valOther = other.get())
+ {
+ new (&rawMem_) T(*valOther); //throw X
+ valid_ = true;
+ }
+ return *this;
+ }
+
+ explicit operator bool() const { return valid_; } //thank you, C++11!!!
+
+ const T* get() const { return valid_ ? reinterpret_cast<const T*>(&rawMem_) : nullptr; }
+ T* get() { return valid_ ? reinterpret_cast< T*>(&rawMem_) : nullptr; }
+
+ const T& operator*() const { return *get(); }
+ /**/ T& operator*() { return *get(); }
+
+ const T* operator->() const { return get(); }
+ /**/ T* operator->() { return get(); }
+
+private:
+ std::aligned_storage_t<sizeof(T), alignof(T)> rawMem_; //don't require T to be default-constructible!
+ bool valid_ = false;
+};
+}
+
+#endif //OPTIONAL_H_2857428578342203589
diff --git a/zen/perf.h b/zen/perf.h
index ada59691..bd21159c 100755
--- a/zen/perf.h
+++ b/zen/perf.h
@@ -1,83 +1,83 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef PERF_H_83947184145342652456
-#define PERF_H_83947184145342652456
-
-#include <chrono>
-#include "deprecate.h"
-#include "scope_guard.h"
-
- #include <iostream>
-
-
-//############# two macros for quick performance measurements ###############
-#define PERF_START zen::PerfTimer perfTest;
-#define PERF_STOP perfTest.showResult();
-//###########################################################################
-
-namespace zen
-{
-class PerfTimer
-{
-public:
- ZEN_DEPRECATE PerfTimer() {}
-
- ~PerfTimer() { if (!resultShown_) showResult(); }
-
- void pause()
- {
- if (!paused_)
- {
- paused_ = true;
- elapsedUntilPause_ += std::chrono::steady_clock::now() - startTime_; //ignore potential ::QueryPerformanceCounter() wrap-around!
- }
- }
-
- void resume()
- {
- if (paused_)
- {
- paused_ = false;
- startTime_ = std::chrono::steady_clock::now();
- }
- }
-
- void restart()
- {
- paused_ = false;
- startTime_ = std::chrono::steady_clock::now();
- elapsedUntilPause_ = std::chrono::nanoseconds::zero();
- }
-
- int64_t timeMs() const
- {
- auto elapsedTotal = elapsedUntilPause_;
- if (!paused_)
- elapsedTotal += std::chrono::steady_clock::now() - startTime_;
-
- return std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTotal).count();
- }
-
- void showResult()
- {
- const bool wasRunning = !paused_;
- if (wasRunning) pause(); //don't include call to MessageBox()!
- ZEN_ON_SCOPE_EXIT(if (wasRunning) resume());
-
- std::clog << "Perf: duration: " << timeMs() << " ms\n";
- resultShown_ = true;
- }
-
-private:
- bool resultShown_ = false;
- bool paused_ = false;
- std::chrono::steady_clock::time_point startTime_ = std::chrono::steady_clock::now(); //uses ::QueryPerformanceCounter()
- std::chrono::nanoseconds elapsedUntilPause_{}; //std::chrono::duration is uninitialized by default! WTF! When will this stupidity end???
-};
-}
-
-#endif //PERF_H_83947184145342652456
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef PERF_H_83947184145342652456
+#define PERF_H_83947184145342652456
+
+#include <chrono>
+#include "deprecate.h"
+#include "scope_guard.h"
+
+ #include <iostream>
+
+
+//############# two macros for quick performance measurements ###############
+#define PERF_START zen::PerfTimer perfTest;
+#define PERF_STOP perfTest.showResult();
+//###########################################################################
+
+namespace zen
+{
+class PerfTimer
+{
+public:
+ ZEN_DEPRECATE PerfTimer() {}
+
+ ~PerfTimer() { if (!resultShown_) showResult(); }
+
+ void pause()
+ {
+ if (!paused_)
+ {
+ paused_ = true;
+ elapsedUntilPause_ += std::chrono::steady_clock::now() - startTime_; //ignore potential ::QueryPerformanceCounter() wrap-around!
+ }
+ }
+
+ void resume()
+ {
+ if (paused_)
+ {
+ paused_ = false;
+ startTime_ = std::chrono::steady_clock::now();
+ }
+ }
+
+ void restart()
+ {
+ paused_ = false;
+ startTime_ = std::chrono::steady_clock::now();
+ elapsedUntilPause_ = std::chrono::nanoseconds::zero();
+ }
+
+ int64_t timeMs() const
+ {
+ auto elapsedTotal = elapsedUntilPause_;
+ if (!paused_)
+ elapsedTotal += std::chrono::steady_clock::now() - startTime_;
+
+ return std::chrono::duration_cast<std::chrono::milliseconds>(elapsedTotal).count();
+ }
+
+ void showResult()
+ {
+ const bool wasRunning = !paused_;
+ if (wasRunning) pause(); //don't include call to MessageBox()!
+ ZEN_ON_SCOPE_EXIT(if (wasRunning) resume());
+
+ std::clog << "Perf: duration: " << timeMs() << " ms\n";
+ resultShown_ = true;
+ }
+
+private:
+ bool resultShown_ = false;
+ bool paused_ = false;
+ std::chrono::steady_clock::time_point startTime_ = std::chrono::steady_clock::now(); //uses ::QueryPerformanceCounter()
+ std::chrono::nanoseconds elapsedUntilPause_{}; //std::chrono::duration is uninitialized by default! WTF! When will this stupidity end???
+};
+}
+
+#endif //PERF_H_83947184145342652456
diff --git a/zen/process_priority.cpp b/zen/process_priority.cpp
index 50c975fe..5767e60c 100755
--- a/zen/process_priority.cpp
+++ b/zen/process_priority.cpp
@@ -1,54 +1,54 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "process_priority.h"
-#include "i18n.h"
-
-
-using namespace zen;
-
-
-struct PreventStandby::Impl {};
-PreventStandby::PreventStandby() {}
-PreventStandby::~PreventStandby() {}
-
-//solution for GNOME?: http://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Inhibit
-
-struct ScheduleForBackgroundProcessing::Impl {};
-ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() {};
-ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() {};
-
-/*
-struct ScheduleForBackgroundProcessing
-{
- - required functions ioprio_get/ioprio_set are not part of glibc: http://linux.die.net/man/2/ioprio_set
- - and probably never will: http://sourceware.org/bugzilla/show_bug.cgi?id=4464
- - /usr/include/linux/ioprio.h not available on Ubuntu, so we can't use it instead
-
- ScheduleForBackgroundProcessing() : oldIoPrio(getIoPriority(IOPRIO_WHO_PROCESS, ::getpid()))
- {
- if (oldIoPrio != -1)
- setIoPriority(::getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
- }
- ~ScheduleForBackgroundProcessing()
- {
- if (oldIoPrio != -1)
- setIoPriority(::getpid(), oldIoPrio);
- }
-
-private:
- static int getIoPriority(pid_t pid)
- {
- return ::syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid);
- }
- static int setIoPriority(pid_t pid, int ioprio)
- {
- return ::syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio);
- }
-
- const int oldIoPrio;
-};
-*/
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "process_priority.h"
+#include "i18n.h"
+
+
+using namespace zen;
+
+
+struct PreventStandby::Impl {};
+PreventStandby::PreventStandby() {}
+PreventStandby::~PreventStandby() {}
+
+//solution for GNOME?: http://people.gnome.org/~mccann/gnome-session/docs/gnome-session.html#org.gnome.SessionManager.Inhibit
+
+struct ScheduleForBackgroundProcessing::Impl {};
+ScheduleForBackgroundProcessing::ScheduleForBackgroundProcessing() {};
+ScheduleForBackgroundProcessing::~ScheduleForBackgroundProcessing() {};
+
+/*
+struct ScheduleForBackgroundProcessing
+{
+ - required functions ioprio_get/ioprio_set are not part of glibc: http://linux.die.net/man/2/ioprio_set
+ - and probably never will: http://sourceware.org/bugzilla/show_bug.cgi?id=4464
+ - /usr/include/linux/ioprio.h not available on Ubuntu, so we can't use it instead
+
+ ScheduleForBackgroundProcessing() : oldIoPrio(getIoPriority(IOPRIO_WHO_PROCESS, ::getpid()))
+ {
+ if (oldIoPrio != -1)
+ setIoPriority(::getpid(), IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0));
+ }
+ ~ScheduleForBackgroundProcessing()
+ {
+ if (oldIoPrio != -1)
+ setIoPriority(::getpid(), oldIoPrio);
+ }
+
+private:
+ static int getIoPriority(pid_t pid)
+ {
+ return ::syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid);
+ }
+ static int setIoPriority(pid_t pid, int ioprio)
+ {
+ return ::syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio);
+ }
+
+ const int oldIoPrio;
+};
+*/
diff --git a/zen/process_priority.h b/zen/process_priority.h
index b876dc92..07679b0c 100755
--- a/zen/process_priority.h
+++ b/zen/process_priority.h
@@ -1,38 +1,38 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-#ifndef PROCESS_PRIORITY_H_83421759082143245
-#define PROCESS_PRIORITY_H_83421759082143245
-
-#include <memory>
-#include "file_error.h"
-
-
-namespace zen
-{
-//signal a "busy" state to the operating system
-class PreventStandby
-{
-public:
- PreventStandby(); //throw FileError
- ~PreventStandby();
-private:
- struct Impl;
- const std::unique_ptr<Impl> pimpl;
-};
-
-//lower CPU and file I/O priorities
-class ScheduleForBackgroundProcessing
-{
-public:
- ScheduleForBackgroundProcessing(); //throw FileError
- ~ScheduleForBackgroundProcessing();
-private:
- struct Impl;
- const std::unique_ptr<Impl> pimpl;
-};
-}
-
-#endif //PROCESS_PRIORITY_H_83421759082143245
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+#ifndef PROCESS_PRIORITY_H_83421759082143245
+#define PROCESS_PRIORITY_H_83421759082143245
+
+#include <memory>
+#include "file_error.h"
+
+
+namespace zen
+{
+//signal a "busy" state to the operating system
+class PreventStandby
+{
+public:
+ PreventStandby(); //throw FileError
+ ~PreventStandby();
+private:
+ struct Impl;
+ const std::unique_ptr<Impl> pimpl;
+};
+
+//lower CPU and file I/O priorities
+class ScheduleForBackgroundProcessing
+{
+public:
+ ScheduleForBackgroundProcessing(); //throw FileError
+ ~ScheduleForBackgroundProcessing();
+private:
+ struct Impl;
+ const std::unique_ptr<Impl> pimpl;
+};
+}
+
+#endif //PROCESS_PRIORITY_H_83421759082143245
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 0c71bf3b..c84ef8f3 100755
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -1,72 +1,72 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "recycler.h"
-#include "file_access.h"
-
- #include <sys/stat.h>
- #include <gio/gio.h>
- #include "scope_guard.h"
-
-
-using namespace zen;
-
-
-
-
-bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError
-{
- GFile* file = ::g_file_new_for_path(itemPath.c_str()); //never fails according to docu
- ZEN_ON_SCOPE_EXIT(g_object_unref(file);)
-
- GError* error = nullptr;
- ZEN_ON_SCOPE_EXIT(if (error) ::g_error_free(error););
-
- if (!::g_file_trash(file, nullptr, &error))
- {
- const Opt<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError
- if (!type)
- return false;
-
- const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itemPath));
- if (!error)
- throw FileError(errorMsg, L"g_file_trash: unknown error."); //user should never see this
-
- //implement same behavior as in Windows: if recycler is not existing, delete permanently
- if (error->code == G_IO_ERROR_NOT_SUPPORTED)
- {
- if (*type == ItemType::FOLDER)
- removeDirectoryPlainRecursion(itemPath); //throw FileError
- else
- removeFilePlain(itemPath); //throw FileError
- return true;
- }
-
- throw FileError(errorMsg, formatSystemError(L"g_file_trash", L"Glib Error Code " + numberTo<std::wstring>(error->code), utfTo<std::wstring>(error->message)));
- //g_quark_to_string(error->domain)
- }
- return true;
-
-}
-
-
-/*
-We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not!
-
-The following function looks perfect, alas it is restricted to local files and to the implementation of GIO only:
-
- gboolean _g_local_file_has_trash_dir(const char* dirpath, dev_t dir_dev);
- See: http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.h
-
- Just checking for "G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH" is not correct, since we find in
- http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.c
-
- g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
- writable && parent_info->has_trash_dir);
-
- => We're NOT interested in whether the specified folder can be trashed, but whether it supports thrashing its child elements! (Only support, not actual write access!)
- This renders G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH useless for this purpose.
-*/
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "recycler.h"
+#include "file_access.h"
+
+ #include <sys/stat.h>
+ #include <gio/gio.h>
+ #include "scope_guard.h"
+
+
+using namespace zen;
+
+
+
+
+bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError
+{
+ GFile* file = ::g_file_new_for_path(itemPath.c_str()); //never fails according to docu
+ ZEN_ON_SCOPE_EXIT(g_object_unref(file);)
+
+ GError* error = nullptr;
+ ZEN_ON_SCOPE_EXIT(if (error) ::g_error_free(error););
+
+ if (!::g_file_trash(file, nullptr, &error))
+ {
+ const Opt<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError
+ if (!type)
+ return false;
+
+ const std::wstring errorMsg = replaceCpy(_("Unable to move %x to the recycle bin."), L"%x", fmtPath(itemPath));
+ if (!error)
+ throw FileError(errorMsg, L"g_file_trash: unknown error."); //user should never see this
+
+ //implement same behavior as in Windows: if recycler is not existing, delete permanently
+ if (error->code == G_IO_ERROR_NOT_SUPPORTED)
+ {
+ if (*type == ItemType::FOLDER)
+ removeDirectoryPlainRecursion(itemPath); //throw FileError
+ else
+ removeFilePlain(itemPath); //throw FileError
+ return true;
+ }
+
+ throw FileError(errorMsg, formatSystemError(L"g_file_trash", L"Glib Error Code " + numberTo<std::wstring>(error->code), utfTo<std::wstring>(error->message)));
+ //g_quark_to_string(error->domain)
+ }
+ return true;
+
+}
+
+
+/*
+We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not!
+
+The following function looks perfect, alas it is restricted to local files and to the implementation of GIO only:
+
+ gboolean _g_local_file_has_trash_dir(const char* dirpath, dev_t dir_dev);
+ See: http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.h
+
+ Just checking for "G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH" is not correct, since we find in
+ http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.c
+
+ g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
+ writable && parent_info->has_trash_dir);
+
+ => We're NOT interested in whether the specified folder can be trashed, but whether it supports thrashing its child elements! (Only support, not actual write access!)
+ This renders G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH useless for this purpose.
+*/
diff --git a/zen/recycler.h b/zen/recycler.h
index 0f1b0023..54dd75ca 100755
--- a/zen/recycler.h
+++ b/zen/recycler.h
@@ -1,43 +1,43 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef RECYCLER_H_18345067341545
-#define RECYCLER_H_18345067341545
-
-#include <vector>
-#include <functional>
-#include "file_error.h"
-
-
-namespace zen
-{
-/*
---------------------
-|Recycle Bin Access|
---------------------
-
-Windows
--------
--> Recycler API always available: during runtime either SHFileOperation or IFileOperation (since Vista) will be dynamically selected
--> COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
-
-Linux
------
-Compiler flags: `pkg-config --cflags gio-2.0`
-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 recycler is not available) -> crappy semantics, but we have no choice thanks to Windows' design
-bool recycleOrDeleteIfExists(const Zstring& itemPath); //throw FileError, return "true" if file/dir was actually deleted
-
-
-}
-
-#endif //RECYCLER_H_18345067341545
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef RECYCLER_H_18345067341545
+#define RECYCLER_H_18345067341545
+
+#include <vector>
+#include <functional>
+#include "file_error.h"
+
+
+namespace zen
+{
+/*
+--------------------
+|Recycle Bin Access|
+--------------------
+
+Windows
+-------
+-> Recycler API always available: during runtime either SHFileOperation or IFileOperation (since Vista) will be dynamically selected
+-> COM needs to be initialized before calling any of these functions! CoInitializeEx/CoUninitialize
+
+Linux
+-----
+Compiler flags: `pkg-config --cflags gio-2.0`
+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 recycler is not available) -> crappy semantics, but we have no choice thanks to Windows' design
+bool recycleOrDeleteIfExists(const Zstring& itemPath); //throw FileError, return "true" if file/dir was actually deleted
+
+
+}
+
+#endif //RECYCLER_H_18345067341545
diff --git a/zen/scope_guard.h b/zen/scope_guard.h
index 62552f7b..b336ad53 100755
--- a/zen/scope_guard.h
+++ b/zen/scope_guard.h
@@ -1,121 +1,121 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef SCOPE_GUARD_H_8971632487321434
-#define SCOPE_GUARD_H_8971632487321434
-
-#include <cassert>
-#include <exception>
-#include "type_tools.h"
-
-
-//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP
- static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
-
-namespace __cxxabiv1
-{
-struct __cxa_eh_globals;
-extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
-}
-
-inline int getUncaughtExceptionCount()
-{
- return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
-}
-
-//best of Zen, Loki and C++17
-
-namespace zen
-{
-//Scope Guard
-/*
- auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { ::CloseHandle(hDir); });
- ...
- guardAio.dismiss();
-
-Scope Exit:
- ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
- ZEN_ON_SCOPE_FAIL(UndoPreviousWork());
- ZEN_ON_SCOPE_SUCCESS(NotifySuccess());
-*/
-
-enum class ScopeGuardRunMode
-{
- ON_EXIT,
- ON_SUCCESS,
- ON_FAIL
-};
-
-
-//partially specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant"
-template <typename F> inline
-void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>)
-{
- try { fun(); }
- catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"!
-}
-
-
-template <typename F> inline
-void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>)
-{
- const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
- if (!failed)
- fun(); //throw X
-}
-
-
-template <typename F> inline
-void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>)
-{
- const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
- if (failed)
- try { fun(); }
- catch (...) { assert(false); }
-}
-
-
-template <ScopeGuardRunMode runMode, typename F>
-class ScopeGuard
-{
-public:
- explicit ScopeGuard(const F& fun) : fun_(fun) {}
- explicit ScopeGuard( F&& fun) : fun_(std::move(fun)) {}
-
- ScopeGuard(ScopeGuard&& other) : fun_(std::move(other.fun_)),
- exeptionCount_(other.exeptionCount_),
- dismissed_(other.dismissed_) { other.dismissed_ = true; }
-
- ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS)
- {
- if (!dismissed_)
- runScopeGuardDestructor(fun_, exeptionCount_, StaticEnum<ScopeGuardRunMode, runMode>());
- }
-
- void dismiss() { dismissed_ = true; }
-
-private:
- ScopeGuard (const ScopeGuard&) = delete;
- ScopeGuard& operator=(const ScopeGuard&) = delete;
-
- F fun_;
- const int exeptionCount_ = getUncaughtExceptionCount();
- bool dismissed_ = false;
-};
-
-
-template <ScopeGuardRunMode runMode, class F> inline
-auto makeGuard(F&& fun) { return ScopeGuard<runMode, std::decay_t<F>>(std::forward<F>(fun)); }
-}
-
-#define ZEN_CONCAT_SUB(X, Y) X ## Y
-#define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y)
-
-#define ZEN_ON_SCOPE_EXIT(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_EXIT >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
-#define ZEN_ON_SCOPE_FAIL(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_FAIL >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
-#define ZEN_ON_SCOPE_SUCCESS(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_SUCCESS>([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
-
-#endif //SCOPE_GUARD_H_8971632487321434
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef SCOPE_GUARD_H_8971632487321434
+#define SCOPE_GUARD_H_8971632487321434
+
+#include <cassert>
+#include <exception>
+#include "type_tools.h"
+
+
+//std::uncaught_exceptions() currently unsupported on GCC and Clang => clean up ASAP
+ static_assert(__GNUC__ < 6 || (__GNUC__ == 6 && (__GNUC_MINOR__ < 3 || (__GNUC_MINOR__ == 3 && __GNUC_PATCHLEVEL__ <= 1))), "check std::uncaught_exceptions support");
+
+namespace __cxxabiv1
+{
+struct __cxa_eh_globals;
+extern "C" __cxa_eh_globals* __cxa_get_globals() noexcept;
+}
+
+inline int getUncaughtExceptionCount()
+{
+ return *(reinterpret_cast<unsigned int*>(static_cast<char*>(static_cast<void*>(__cxxabiv1::__cxa_get_globals())) + sizeof(void*)));
+}
+
+//best of Zen, Loki and C++17
+
+namespace zen
+{
+//Scope Guard
+/*
+ auto guardAio = zen::makeGuard<ScopeGuardRunMode::ON_EXIT>([&] { ::CloseHandle(hDir); });
+ ...
+ guardAio.dismiss();
+
+Scope Exit:
+ ZEN_ON_SCOPE_EXIT(::CloseHandle(hDir));
+ ZEN_ON_SCOPE_FAIL(UndoPreviousWork());
+ ZEN_ON_SCOPE_SUCCESS(NotifySuccess());
+*/
+
+enum class ScopeGuardRunMode
+{
+ ON_EXIT,
+ ON_SUCCESS,
+ ON_FAIL
+};
+
+
+//partially specialize scope guard destructor code and get rid of those pesky MSVC "4127 conditional expression is constant"
+template <typename F> inline
+void runScopeGuardDestructor(F& fun, int /*exeptionCountOld*/, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_EXIT>)
+{
+ try { fun(); }
+ catch (...) { assert(false); } //consistency: don't expect exceptions for ON_EXIT even if "!failed"!
+}
+
+
+template <typename F> inline
+void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_SUCCESS>)
+{
+ const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
+ if (!failed)
+ fun(); //throw X
+}
+
+
+template <typename F> inline
+void runScopeGuardDestructor(F& fun, int exeptionCountOld, StaticEnum<ScopeGuardRunMode, ScopeGuardRunMode::ON_FAIL>)
+{
+ const bool failed = getUncaughtExceptionCount() > exeptionCountOld;
+ if (failed)
+ try { fun(); }
+ catch (...) { assert(false); }
+}
+
+
+template <ScopeGuardRunMode runMode, typename F>
+class ScopeGuard
+{
+public:
+ explicit ScopeGuard(const F& fun) : fun_(fun) {}
+ explicit ScopeGuard( F&& fun) : fun_(std::move(fun)) {}
+
+ ScopeGuard(ScopeGuard&& other) : fun_(std::move(other.fun_)),
+ exeptionCount_(other.exeptionCount_),
+ dismissed_(other.dismissed_) { other.dismissed_ = true; }
+
+ ~ScopeGuard() noexcept(runMode != ScopeGuardRunMode::ON_SUCCESS)
+ {
+ if (!dismissed_)
+ runScopeGuardDestructor(fun_, exeptionCount_, StaticEnum<ScopeGuardRunMode, runMode>());
+ }
+
+ void dismiss() { dismissed_ = true; }
+
+private:
+ ScopeGuard (const ScopeGuard&) = delete;
+ ScopeGuard& operator=(const ScopeGuard&) = delete;
+
+ F fun_;
+ const int exeptionCount_ = getUncaughtExceptionCount();
+ bool dismissed_ = false;
+};
+
+
+template <ScopeGuardRunMode runMode, class F> inline
+auto makeGuard(F&& fun) { return ScopeGuard<runMode, std::decay_t<F>>(std::forward<F>(fun)); }
+}
+
+#define ZEN_CONCAT_SUB(X, Y) X ## Y
+#define ZEN_CONCAT(X, Y) ZEN_CONCAT_SUB(X, Y)
+
+#define ZEN_ON_SCOPE_EXIT(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_EXIT >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
+#define ZEN_ON_SCOPE_FAIL(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_FAIL >([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
+#define ZEN_ON_SCOPE_SUCCESS(X) auto ZEN_CONCAT(dummy, __LINE__) = zen::makeGuard<zen::ScopeGuardRunMode::ON_SUCCESS>([&]{ X; }); (void)ZEN_CONCAT(dummy, __LINE__);
+
+#endif //SCOPE_GUARD_H_8971632487321434
diff --git a/zen/serialize.h b/zen/serialize.h
index c8dfb96d..81d2d1ef 100755
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -1,281 +1,281 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef SERIALIZE_H_839405783574356
-#define SERIALIZE_H_839405783574356
-
-#include <functional>
-#include <cstdint>
-#include "string_base.h"
-//keep header clean from specific stream implementations! (e.g.file_io.h)! used by abstract.h!
-
-
-namespace zen
-{
-//high-performance unformatted serialization (avoiding wxMemoryOutputStream/wxMemoryInputStream inefficiencies)
-
-/*
---------------------------
-|Binary Container Concept|
---------------------------
-binary container for data storage: must support "basic" std::vector interface (e.g. std::vector<char>, std::string, Zbase<char>)
-*/
-
-//binary container reference implementations
-using Utf8String = Zbase<char>; //ref-counted + COW text stream + guaranteed performance: exponential growth
-class ByteArray; //ref-counted byte stream + guaranteed performance: exponential growth -> no COW, but 12% faster than Utf8String (due to no null-termination?)
-
-
-class ByteArray //essentially a std::vector<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite
-{
-public:
- using value_type = std::vector<char>::value_type;
- using iterator = std::vector<char>::iterator;
- using const_iterator = std::vector<char>::const_iterator;
-
- iterator begin() { return buffer->begin(); }
- iterator end () { return buffer->end (); }
-
- const_iterator begin() const { return buffer->begin(); }
- const_iterator end () const { return buffer->end (); }
-
- void resize(size_t len) { buffer->resize(len); }
- size_t size() const { return buffer->size(); }
- bool empty() const { return buffer->empty(); }
-
- inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer == *rhs.buffer; }
-
-private:
- std::shared_ptr<std::vector<char>> buffer { std::make_shared<std::vector<char>>() }; //always bound!
- //perf: shared_ptr indirection irrelevant: less than 1% slower!
-};
-
-/*
--------------------------------
-|Buffered Input Stream Concept|
--------------------------------
-struct BufferedInputStream
-{
- size_t read(void* buffer, size_t bytesToRead); //throw X; return "bytesToRead" bytes unless end of stream!
-
-Optional: support stream-copying
---------------------------------
- size_t getBlockSize() const;
- const IOCallback& notifyUnbufferedIO
-};
-
---------------------------------
-|Buffered Output Stream Concept|
---------------------------------
-struct BufferedOutputStream
-{
- void write(const void* buffer, size_t bytesToWrite); //throw X
-
-Optional: support stream-copying
---------------------------------
- const IOCallback& notifyUnbufferedIO
-};
-*/
-using IOCallback = std::function<void(int64_t bytesDelta)>; //throw X
-
-
-//functions based on buffered stream abstraction
-template <class BufferedInputStream, class BufferedOutputStream>
-void bufferedStreamCopy(BufferedInputStream& streamIn, BufferedOutputStream& streamOut); //throw X
-
-template <class BinContainer, class BufferedInputStream> BinContainer
-bufferedLoad(BufferedInputStream& streamIn); //throw X
-
-template <class N, class BufferedOutputStream> void writeNumber (BufferedOutputStream& stream, const N& num); //
-template <class C, class BufferedOutputStream> void writeContainer(BufferedOutputStream& stream, const C& str); //throw ()
-template < class BufferedOutputStream> void writeArray (BufferedOutputStream& stream, const void* buffer, size_t len); //
-//----------------------------------------------------------------------
-class UnexpectedEndOfStreamError {};
-template <class N, class BufferedInputStream> N readNumber (BufferedInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data)
-template <class C, class BufferedInputStream> C readContainer(BufferedInputStream& stream); //
-template < class BufferedInputStream> void readArray (BufferedInputStream& stream, void* buffer, size_t len); //
-
-
-struct IOCallbackDivider
-{
- IOCallbackDivider(const IOCallback& notifyUnbufferedIO, int64_t& totalUnbufferedIO) : totalUnbufferedIO_(totalUnbufferedIO), notifyUnbufferedIO_(notifyUnbufferedIO) {}
-
- void operator()(int64_t bytesDelta)
- {
- if (notifyUnbufferedIO_) notifyUnbufferedIO_((totalUnbufferedIO_ - totalUnbufferedIO_ / 2 * 2 + bytesDelta) / 2); //throw X!
- totalUnbufferedIO_ += bytesDelta;
- }
-
-private:
- int64_t& totalUnbufferedIO_;
- const IOCallback& notifyUnbufferedIO_;
-};
-
-//buffered input/output stream reference implementations:
-template <class BinContainer>
-struct MemoryStreamIn
-{
- MemoryStreamIn(const BinContainer& cont) : buffer_(cont) {} //this better be cheap!
-
- size_t read(void* buffer, size_t bytesToRead) //return "bytesToRead" bytes unless end of stream!
- {
- static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
- const size_t bytesRead = std::min(bytesToRead, buffer_.size() - pos_);
- auto itFirst = buffer_.begin() + pos_;
- std::copy(itFirst, itFirst + bytesRead, static_cast<char*>(buffer));
- pos_ += bytesRead;
- return bytesRead;
- }
-
- size_t pos() const { return pos_; }
-
-private:
- MemoryStreamIn (const MemoryStreamIn&) = delete;
- MemoryStreamIn& operator=(const MemoryStreamIn&) = delete;
-
- const BinContainer buffer_;
- size_t pos_ = 0;
-};
-
-template <class BinContainer>
-struct MemoryStreamOut
-{
- MemoryStreamOut() = default;
-
- void write(const void* buffer, size_t bytesToWrite)
- {
- static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
- const size_t oldSize = buffer_.size();
- buffer_.resize(oldSize + bytesToWrite);
- std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.begin() + oldSize);
- }
-
- const BinContainer& ref() const { return buffer_; }
-
-private:
- MemoryStreamOut (const MemoryStreamOut&) = delete;
- MemoryStreamOut& operator=(const MemoryStreamOut&) = delete;
-
- BinContainer buffer_;
-};
-
-
-
-
-
-
-
-
-//-----------------------implementation-------------------------------
-template <class BufferedInputStream, class BufferedOutputStream> inline
-void bufferedStreamCopy(BufferedInputStream& streamIn, //throw X
- BufferedOutputStream& streamOut) //
-{
- const size_t blockSize = streamIn.getBlockSize();
- if (blockSize == 0)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-
- std::vector<char> buffer(blockSize);
- for (;;)
- {
- const size_t bytesRead = streamIn.read(&buffer[0], blockSize); //throw X; return "bytesToRead" bytes unless end of stream!
- streamOut.write(&buffer[0], bytesRead); //throw X
-
- if (bytesRead < blockSize) //end of file
- break;
- }
-}
-
-
-template <class BinContainer, class BufferedInputStream> inline
-BinContainer bufferedLoad(BufferedInputStream& streamIn) //throw X
-{
- static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
-
- const size_t blockSize = streamIn.getBlockSize();
- if (blockSize == 0)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-
- BinContainer buffer;
- for (;;)
- {
- buffer.resize(buffer.size() + blockSize);
- const size_t bytesRead = streamIn.read(&*(buffer.end() - blockSize), blockSize); //throw X; return "bytesToRead" bytes unless end of stream!
- buffer.resize(buffer.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
-
- if (bytesRead < blockSize) //end of file
- return buffer;
- }
-}
-
-
-template <class BufferedOutputStream> inline
-void writeArray(BufferedOutputStream& stream, const void* buffer, size_t len)
-{
- stream.write(buffer, len);
-}
-
-
-template <class N, class BufferedOutputStream> inline
-void writeNumber(BufferedOutputStream& stream, const N& num)
-{
- static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "not a number!");
- writeArray(stream, &num, sizeof(N));
-}
-
-
-template <class C, class BufferedOutputStream> inline
-void writeContainer(BufferedOutputStream& stream, const C& cont) //don't even consider UTF8 conversions here, we're handling arbitrary binary data!
-{
- const auto len = cont.size();
- writeNumber(stream, static_cast<uint32_t>(len));
- if (len > 0)
- writeArray(stream, &*cont.begin(), sizeof(typename C::value_type) * len); //don't use c_str(), but access uniformly via STL interface
-}
-
-
-template <class BufferedInputStream> inline
-void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw UnexpectedEndOfStreamError
-{
- const size_t bytesRead = stream.read(buffer, len);
- assert(bytesRead <= len); //buffer overflow otherwise not always detected!
- if (bytesRead < len)
- throw UnexpectedEndOfStreamError();
-}
-
-
-template <class N, class BufferedInputStream> inline
-N readNumber(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
-{
- static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "");
- N num = 0;
- readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError
- return num;
-}
-
-
-template <class C, class BufferedInputStream> inline
-C readContainer(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
-{
- C cont;
- auto strLength = readNumber<uint32_t>(stream); //throw UnexpectedEndOfStreamError
- if (strLength > 0)
- {
- try
- {
- cont.resize(strLength); //throw std::bad_alloc
- }
- catch (std::bad_alloc&) //most likely this is due to data corruption!
- {
- throw UnexpectedEndOfStreamError();
- }
- readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError
- }
- return cont;
-}
-}
-
-#endif //SERIALIZE_H_839405783574356
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef SERIALIZE_H_839405783574356
+#define SERIALIZE_H_839405783574356
+
+#include <functional>
+#include <cstdint>
+#include "string_base.h"
+//keep header clean from specific stream implementations! (e.g.file_io.h)! used by abstract.h!
+
+
+namespace zen
+{
+//high-performance unformatted serialization (avoiding wxMemoryOutputStream/wxMemoryInputStream inefficiencies)
+
+/*
+--------------------------
+|Binary Container Concept|
+--------------------------
+binary container for data storage: must support "basic" std::vector interface (e.g. std::vector<char>, std::string, Zbase<char>)
+*/
+
+//binary container reference implementations
+using Utf8String = Zbase<char>; //ref-counted + COW text stream + guaranteed performance: exponential growth
+class ByteArray; //ref-counted byte stream + guaranteed performance: exponential growth -> no COW, but 12% faster than Utf8String (due to no null-termination?)
+
+
+class ByteArray //essentially a std::vector<char> with ref-counted semantics, but no COW! => *almost* value type semantics, but not quite
+{
+public:
+ using value_type = std::vector<char>::value_type;
+ using iterator = std::vector<char>::iterator;
+ using const_iterator = std::vector<char>::const_iterator;
+
+ iterator begin() { return buffer->begin(); }
+ iterator end () { return buffer->end (); }
+
+ const_iterator begin() const { return buffer->begin(); }
+ const_iterator end () const { return buffer->end (); }
+
+ void resize(size_t len) { buffer->resize(len); }
+ size_t size() const { return buffer->size(); }
+ bool empty() const { return buffer->empty(); }
+
+ inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer == *rhs.buffer; }
+
+private:
+ std::shared_ptr<std::vector<char>> buffer { std::make_shared<std::vector<char>>() }; //always bound!
+ //perf: shared_ptr indirection irrelevant: less than 1% slower!
+};
+
+/*
+-------------------------------
+|Buffered Input Stream Concept|
+-------------------------------
+struct BufferedInputStream
+{
+ size_t read(void* buffer, size_t bytesToRead); //throw X; return "bytesToRead" bytes unless end of stream!
+
+Optional: support stream-copying
+--------------------------------
+ size_t getBlockSize() const;
+ const IOCallback& notifyUnbufferedIO
+};
+
+--------------------------------
+|Buffered Output Stream Concept|
+--------------------------------
+struct BufferedOutputStream
+{
+ void write(const void* buffer, size_t bytesToWrite); //throw X
+
+Optional: support stream-copying
+--------------------------------
+ const IOCallback& notifyUnbufferedIO
+};
+*/
+using IOCallback = std::function<void(int64_t bytesDelta)>; //throw X
+
+
+//functions based on buffered stream abstraction
+template <class BufferedInputStream, class BufferedOutputStream>
+void bufferedStreamCopy(BufferedInputStream& streamIn, BufferedOutputStream& streamOut); //throw X
+
+template <class BinContainer, class BufferedInputStream> BinContainer
+bufferedLoad(BufferedInputStream& streamIn); //throw X
+
+template <class N, class BufferedOutputStream> void writeNumber (BufferedOutputStream& stream, const N& num); //
+template <class C, class BufferedOutputStream> void writeContainer(BufferedOutputStream& stream, const C& str); //throw ()
+template < class BufferedOutputStream> void writeArray (BufferedOutputStream& stream, const void* buffer, size_t len); //
+//----------------------------------------------------------------------
+class UnexpectedEndOfStreamError {};
+template <class N, class BufferedInputStream> N readNumber (BufferedInputStream& stream); //throw UnexpectedEndOfStreamError (corrupted data)
+template <class C, class BufferedInputStream> C readContainer(BufferedInputStream& stream); //
+template < class BufferedInputStream> void readArray (BufferedInputStream& stream, void* buffer, size_t len); //
+
+
+struct IOCallbackDivider
+{
+ IOCallbackDivider(const IOCallback& notifyUnbufferedIO, int64_t& totalUnbufferedIO) : totalUnbufferedIO_(totalUnbufferedIO), notifyUnbufferedIO_(notifyUnbufferedIO) {}
+
+ void operator()(int64_t bytesDelta)
+ {
+ if (notifyUnbufferedIO_) notifyUnbufferedIO_((totalUnbufferedIO_ - totalUnbufferedIO_ / 2 * 2 + bytesDelta) / 2); //throw X!
+ totalUnbufferedIO_ += bytesDelta;
+ }
+
+private:
+ int64_t& totalUnbufferedIO_;
+ const IOCallback& notifyUnbufferedIO_;
+};
+
+//buffered input/output stream reference implementations:
+template <class BinContainer>
+struct MemoryStreamIn
+{
+ MemoryStreamIn(const BinContainer& cont) : buffer_(cont) {} //this better be cheap!
+
+ size_t read(void* buffer, size_t bytesToRead) //return "bytesToRead" bytes unless end of stream!
+ {
+ static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+ const size_t bytesRead = std::min(bytesToRead, buffer_.size() - pos_);
+ auto itFirst = buffer_.begin() + pos_;
+ std::copy(itFirst, itFirst + bytesRead, static_cast<char*>(buffer));
+ pos_ += bytesRead;
+ return bytesRead;
+ }
+
+ size_t pos() const { return pos_; }
+
+private:
+ MemoryStreamIn (const MemoryStreamIn&) = delete;
+ MemoryStreamIn& operator=(const MemoryStreamIn&) = delete;
+
+ const BinContainer buffer_;
+ size_t pos_ = 0;
+};
+
+template <class BinContainer>
+struct MemoryStreamOut
+{
+ MemoryStreamOut() = default;
+
+ void write(const void* buffer, size_t bytesToWrite)
+ {
+ static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+ const size_t oldSize = buffer_.size();
+ buffer_.resize(oldSize + bytesToWrite);
+ std::copy(static_cast<const char*>(buffer), static_cast<const char*>(buffer) + bytesToWrite, buffer_.begin() + oldSize);
+ }
+
+ const BinContainer& ref() const { return buffer_; }
+
+private:
+ MemoryStreamOut (const MemoryStreamOut&) = delete;
+ MemoryStreamOut& operator=(const MemoryStreamOut&) = delete;
+
+ BinContainer buffer_;
+};
+
+
+
+
+
+
+
+
+//-----------------------implementation-------------------------------
+template <class BufferedInputStream, class BufferedOutputStream> inline
+void bufferedStreamCopy(BufferedInputStream& streamIn, //throw X
+ BufferedOutputStream& streamOut) //
+{
+ const size_t blockSize = streamIn.getBlockSize();
+ if (blockSize == 0)
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ std::vector<char> buffer(blockSize);
+ for (;;)
+ {
+ const size_t bytesRead = streamIn.read(&buffer[0], blockSize); //throw X; return "bytesToRead" bytes unless end of stream!
+ streamOut.write(&buffer[0], bytesRead); //throw X
+
+ if (bytesRead < blockSize) //end of file
+ break;
+ }
+}
+
+
+template <class BinContainer, class BufferedInputStream> inline
+BinContainer bufferedLoad(BufferedInputStream& streamIn) //throw X
+{
+ static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes
+
+ const size_t blockSize = streamIn.getBlockSize();
+ if (blockSize == 0)
+ throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+
+ BinContainer buffer;
+ for (;;)
+ {
+ buffer.resize(buffer.size() + blockSize);
+ const size_t bytesRead = streamIn.read(&*(buffer.end() - blockSize), blockSize); //throw X; return "bytesToRead" bytes unless end of stream!
+ buffer.resize(buffer.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
+
+ if (bytesRead < blockSize) //end of file
+ return buffer;
+ }
+}
+
+
+template <class BufferedOutputStream> inline
+void writeArray(BufferedOutputStream& stream, const void* buffer, size_t len)
+{
+ stream.write(buffer, len);
+}
+
+
+template <class N, class BufferedOutputStream> inline
+void writeNumber(BufferedOutputStream& stream, const N& num)
+{
+ static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "not a number!");
+ writeArray(stream, &num, sizeof(N));
+}
+
+
+template <class C, class BufferedOutputStream> inline
+void writeContainer(BufferedOutputStream& stream, const C& cont) //don't even consider UTF8 conversions here, we're handling arbitrary binary data!
+{
+ const auto len = cont.size();
+ writeNumber(stream, static_cast<uint32_t>(len));
+ if (len > 0)
+ writeArray(stream, &*cont.begin(), sizeof(typename C::value_type) * len); //don't use c_str(), but access uniformly via STL interface
+}
+
+
+template <class BufferedInputStream> inline
+void readArray(BufferedInputStream& stream, void* buffer, size_t len) //throw UnexpectedEndOfStreamError
+{
+ const size_t bytesRead = stream.read(buffer, len);
+ assert(bytesRead <= len); //buffer overflow otherwise not always detected!
+ if (bytesRead < len)
+ throw UnexpectedEndOfStreamError();
+}
+
+
+template <class N, class BufferedInputStream> inline
+N readNumber(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
+{
+ static_assert(IsArithmetic<N>::value || IsSameType<N, bool>::value, "");
+ N num = 0;
+ readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError
+ return num;
+}
+
+
+template <class C, class BufferedInputStream> inline
+C readContainer(BufferedInputStream& stream) //throw UnexpectedEndOfStreamError
+{
+ C cont;
+ auto strLength = readNumber<uint32_t>(stream); //throw UnexpectedEndOfStreamError
+ if (strLength > 0)
+ {
+ try
+ {
+ cont.resize(strLength); //throw std::bad_alloc
+ }
+ catch (std::bad_alloc&) //most likely this is due to data corruption!
+ {
+ throw UnexpectedEndOfStreamError();
+ }
+ readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError
+ }
+ return cont;
+}
+}
+
+#endif //SERIALIZE_H_839405783574356
diff --git a/zen/shell_execute.h b/zen/shell_execute.h
index 5e4ddf1a..7dcd6653 100755
--- a/zen/shell_execute.h
+++ b/zen/shell_execute.h
@@ -1,52 +1,52 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef SHELL_EXECUTE_H_23482134578134134
-#define SHELL_EXECUTE_H_23482134578134134
-
-#include "file_error.h"
-
- #include "thread.h"
- #include <stdlib.h> //::system()
-
-
-namespace zen
-{
-//launch commandline and report errors via popup dialog
-//windows: COM needs to be initialized before calling this function!
-enum ExecutionType
-{
- EXEC_TYPE_SYNC,
- EXEC_TYPE_ASYNC
-};
-
-namespace
-{
-
-
-void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
-{
- /*
- we cannot use wxExecute due to various issues:
- - screws up encoding on OS X for non-ASCII characters
- - does not provide any reasonable error information
- - uses a zero-sized dummy window as a hack to keep focus which leaves a useless empty icon in ALT-TAB list in Windows
- */
-
- if (type == EXEC_TYPE_SYNC)
- {
- //Posix::system - execute a shell command
- int rv = ::system(command.c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect...
- if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)"
- throw FileError(_("Incorrect command line:") + L"\n" + utfTo<std::wstring>(command));
- }
- else
- runAsync([=] { int rv = ::system(command.c_str()); (void)rv; });
-}
-}
-}
-
-#endif //SHELL_EXECUTE_H_23482134578134134
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef SHELL_EXECUTE_H_23482134578134134
+#define SHELL_EXECUTE_H_23482134578134134
+
+#include "file_error.h"
+
+ #include "thread.h"
+ #include <stdlib.h> //::system()
+
+
+namespace zen
+{
+//launch commandline and report errors via popup dialog
+//windows: COM needs to be initialized before calling this function!
+enum ExecutionType
+{
+ EXEC_TYPE_SYNC,
+ EXEC_TYPE_ASYNC
+};
+
+namespace
+{
+
+
+void shellExecute(const Zstring& command, ExecutionType type) //throw FileError
+{
+ /*
+ we cannot use wxExecute due to various issues:
+ - screws up encoding on OS X for non-ASCII characters
+ - does not provide any reasonable error information
+ - uses a zero-sized dummy window as a hack to keep focus which leaves a useless empty icon in ALT-TAB list in Windows
+ */
+
+ if (type == EXEC_TYPE_SYNC)
+ {
+ //Posix::system - execute a shell command
+ int rv = ::system(command.c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect...
+ if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)"
+ throw FileError(_("Incorrect command line:") + L"\n" + utfTo<std::wstring>(command));
+ }
+ else
+ runAsync([=] { int rv = ::system(command.c_str()); (void)rv; });
+}
+}
+}
+
+#endif //SHELL_EXECUTE_H_23482134578134134
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index 74a2b360..b03d7533 100755
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -1,244 +1,244 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef STL_TOOLS_H_84567184321434
-#define STL_TOOLS_H_84567184321434
-
-#include <set>
-#include <map>
-#include <vector>
-#include <memory>
-#include <algorithm>
-#include "type_tools.h"
-#include "build_info.h"
-
-
-//enhancements for <algorithm>
-namespace zen
-{
-//erase selected elements from any container:
-template <class T, class Alloc, class Predicate>
-void erase_if(std::vector<T, Alloc>& v, Predicate p);
-
-template <class T, class LessType, class Alloc, class Predicate>
-void erase_if(std::set<T, LessType, Alloc>& s, Predicate p);
-
-template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate>
-void erase_if(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p);
-
-//append STL containers
-template <class T, class Alloc, class C>
-void append(std::vector<T, Alloc>& v, const C& c);
-
-template <class T, class LessType, class Alloc, class C>
-void append(std::set<T, LessType, Alloc>& s, const C& c);
-
-template <class KeyType, class ValueType, class LessType, class Alloc, class C>
-void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c);
-
-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)
-
-template <class T, class Alloc>
-void removeDuplicates(std::vector<T, Alloc>& v);
-
-//binary search returning an iterator
-template <class ForwardIterator, class T, typename CompLess>
-ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less);
-
-template <class BidirectionalIterator, class T>
-BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value);
-
-//replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name)
-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);
-
-template <class ByteIterator> size_t hashBytes (ByteIterator first, ByteIterator last);
-template <class ByteIterator> size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last);
-
-
-//support for custom string classes in std::unordered_set/map
-struct StringHash
-{
- template <class String>
- size_t operator()(const String& str) const
- {
- const auto* strFirst = strBegin(str);
- return hashBytes(reinterpret_cast<const char*>(strFirst),
- reinterpret_cast<const char*>(strFirst + strLength(str)));
- }
-};
-
-
-
-
-
-
-//######################## implementation ########################
-
-template <class T, class Alloc, class Predicate> inline
-void erase_if(std::vector<T, Alloc>& v, Predicate p)
-{
- v.erase(std::remove_if(v.begin(), v.end(), p), v.end());
-}
-
-
-namespace impl
-{
-template <class S, class Predicate> inline
-void set_or_map_erase_if(S& s, Predicate p)
-{
- for (auto it = s.begin(); it != s.end();)
- if (p(*it))
- s.erase(it++);
- else
- ++it;
-}
-}
-
-
-template <class T, class LessType, class Alloc, class Predicate> inline
-void erase_if(std::set<T, LessType, Alloc>& s, Predicate p) { impl::set_or_map_erase_if(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!!
-
-
-template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate> inline
-void erase_if(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p) { impl::set_or_map_erase_if(m, p); }
-
-
-template <class T, class Alloc, class C> inline
-void append(std::vector<T, Alloc>& v, const C& c) { v.insert(v.end(), c.begin(), c.end()); }
-
-
-template <class T, class LessType, class Alloc, class C> inline
-void append(std::set<T, LessType, Alloc>& s, const C& c) { s.insert(c.begin(), c.end()); }
-
-
-template <class KeyType, class ValueType, class LessType, class Alloc, class C> inline
-void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c) { m.insert(c.begin(), c.end()); }
-
-
-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 it = map.lower_bound(key);
- if (it != map.end() && !(map.key_comp()(key, it->first)))
- {
- it->second = value;
- return it->second;
- }
- else
- return map.insert(it, typename M::value_type(key, value))->second;
-}
-
-
-template <class T, class Alloc> inline
-void removeDuplicates(std::vector<T, Alloc>& v)
-{
- std::sort(v.begin(), v.end());
- v.erase(std::unique(v.begin(), v.end()), v.end());
-}
-
-
-template <class ForwardIterator, class T, typename CompLess> inline
-ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less)
-{
- first = std::lower_bound(first, last, value, less);
- if (first != last && !less(value, *first))
- return first;
- else
- return last;
-}
-
-
-template <class BidirectionalIterator, class T> inline
-BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value)
-{
- for (BidirectionalIterator it = last; it != first;) //reverse iteration: 1. check 2. decrement 3. evaluate
- {
- --it; //
-
- if (*it == value)
- return it;
- }
- return last;
-}
-
-
-template <class BidirectionalIterator1, class BidirectionalIterator2> inline
-BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
- const BidirectionalIterator2 first2, const BidirectionalIterator2 last2)
-{
- const BidirectionalIterator1 itNotFound = last1;
-
- //reverse iteration: 1. check 2. decrement 3. evaluate
- for (;;)
- {
- BidirectionalIterator1 it1 = last1;
- BidirectionalIterator2 it2 = last2;
-
- for (;;)
- {
- if (it2 == first2) return it1;
- if (it1 == first1) return itNotFound;
-
- --it1;
- --it2;
-
- if (*it1 != *it2) break;
- }
- --last1;
- }
-}
-
-
-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);
-}
-
-
-
-
-template <class ByteIterator> inline
-size_t hashBytes(ByteIterator first, ByteIterator last)
-{
- //FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
-#ifdef ZEN_BUILD_32BIT
- const size_t basis = 2166136261U;
-#elif defined ZEN_BUILD_64BIT
- const size_t basis = 14695981039346656037ULL;
-#endif
- return hashBytesAppend(basis, first, last);
-}
-
-
-template <class ByteIterator> inline
-size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last)
-{
-#ifdef ZEN_BUILD_32BIT
- const size_t prime = 16777619U;
-#elif defined ZEN_BUILD_64BIT
- const size_t prime = 1099511628211ULL;
-#endif
- static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
-
- for (; first != last; ++first)
- {
- hashVal ^= static_cast<size_t>(*first);
- hashVal *= prime;
- }
- return hashVal;
-}
-}
-
-#endif //STL_TOOLS_H_84567184321434
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef STL_TOOLS_H_84567184321434
+#define STL_TOOLS_H_84567184321434
+
+#include <set>
+#include <map>
+#include <vector>
+#include <memory>
+#include <algorithm>
+#include "type_tools.h"
+#include "build_info.h"
+
+
+//enhancements for <algorithm>
+namespace zen
+{
+//erase selected elements from any container:
+template <class T, class Alloc, class Predicate>
+void erase_if(std::vector<T, Alloc>& v, Predicate p);
+
+template <class T, class LessType, class Alloc, class Predicate>
+void erase_if(std::set<T, LessType, Alloc>& s, Predicate p);
+
+template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate>
+void erase_if(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p);
+
+//append STL containers
+template <class T, class Alloc, class C>
+void append(std::vector<T, Alloc>& v, const C& c);
+
+template <class T, class LessType, class Alloc, class C>
+void append(std::set<T, LessType, Alloc>& s, const C& c);
+
+template <class KeyType, class ValueType, class LessType, class Alloc, class C>
+void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c);
+
+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)
+
+template <class T, class Alloc>
+void removeDuplicates(std::vector<T, Alloc>& v);
+
+//binary search returning an iterator
+template <class ForwardIterator, class T, typename CompLess>
+ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less);
+
+template <class BidirectionalIterator, class T>
+BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value);
+
+//replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name)
+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);
+
+template <class ByteIterator> size_t hashBytes (ByteIterator first, ByteIterator last);
+template <class ByteIterator> size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last);
+
+
+//support for custom string classes in std::unordered_set/map
+struct StringHash
+{
+ template <class String>
+ size_t operator()(const String& str) const
+ {
+ const auto* strFirst = strBegin(str);
+ return hashBytes(reinterpret_cast<const char*>(strFirst),
+ reinterpret_cast<const char*>(strFirst + strLength(str)));
+ }
+};
+
+
+
+
+
+
+//######################## implementation ########################
+
+template <class T, class Alloc, class Predicate> inline
+void erase_if(std::vector<T, Alloc>& v, Predicate p)
+{
+ v.erase(std::remove_if(v.begin(), v.end(), p), v.end());
+}
+
+
+namespace impl
+{
+template <class S, class Predicate> inline
+void set_or_map_erase_if(S& s, Predicate p)
+{
+ for (auto it = s.begin(); it != s.end();)
+ if (p(*it))
+ s.erase(it++);
+ else
+ ++it;
+}
+}
+
+
+template <class T, class LessType, class Alloc, class Predicate> inline
+void erase_if(std::set<T, LessType, Alloc>& s, Predicate p) { impl::set_or_map_erase_if(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!!
+
+
+template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate> inline
+void erase_if(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p) { impl::set_or_map_erase_if(m, p); }
+
+
+template <class T, class Alloc, class C> inline
+void append(std::vector<T, Alloc>& v, const C& c) { v.insert(v.end(), c.begin(), c.end()); }
+
+
+template <class T, class LessType, class Alloc, class C> inline
+void append(std::set<T, LessType, Alloc>& s, const C& c) { s.insert(c.begin(), c.end()); }
+
+
+template <class KeyType, class ValueType, class LessType, class Alloc, class C> inline
+void append(std::map<KeyType, ValueType, LessType, Alloc>& m, const C& c) { m.insert(c.begin(), c.end()); }
+
+
+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 it = map.lower_bound(key);
+ if (it != map.end() && !(map.key_comp()(key, it->first)))
+ {
+ it->second = value;
+ return it->second;
+ }
+ else
+ return map.insert(it, typename M::value_type(key, value))->second;
+}
+
+
+template <class T, class Alloc> inline
+void removeDuplicates(std::vector<T, Alloc>& v)
+{
+ std::sort(v.begin(), v.end());
+ v.erase(std::unique(v.begin(), v.end()), v.end());
+}
+
+
+template <class ForwardIterator, class T, typename CompLess> inline
+ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less)
+{
+ first = std::lower_bound(first, last, value, less);
+ if (first != last && !less(value, *first))
+ return first;
+ else
+ return last;
+}
+
+
+template <class BidirectionalIterator, class T> inline
+BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value)
+{
+ for (BidirectionalIterator it = last; it != first;) //reverse iteration: 1. check 2. decrement 3. evaluate
+ {
+ --it; //
+
+ if (*it == value)
+ return it;
+ }
+ return last;
+}
+
+
+template <class BidirectionalIterator1, class BidirectionalIterator2> inline
+BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
+ const BidirectionalIterator2 first2, const BidirectionalIterator2 last2)
+{
+ const BidirectionalIterator1 itNotFound = last1;
+
+ //reverse iteration: 1. check 2. decrement 3. evaluate
+ for (;;)
+ {
+ BidirectionalIterator1 it1 = last1;
+ BidirectionalIterator2 it2 = last2;
+
+ for (;;)
+ {
+ if (it2 == first2) return it1;
+ if (it1 == first1) return itNotFound;
+
+ --it1;
+ --it2;
+
+ if (*it1 != *it2) break;
+ }
+ --last1;
+ }
+}
+
+
+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);
+}
+
+
+
+
+template <class ByteIterator> inline
+size_t hashBytes(ByteIterator first, ByteIterator last)
+{
+ //FNV-1a: http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+#ifdef ZEN_BUILD_32BIT
+ const size_t basis = 2166136261U;
+#elif defined ZEN_BUILD_64BIT
+ const size_t basis = 14695981039346656037ULL;
+#endif
+ return hashBytesAppend(basis, first, last);
+}
+
+
+template <class ByteIterator> inline
+size_t hashBytesAppend(size_t hashVal, ByteIterator first, ByteIterator last)
+{
+#ifdef ZEN_BUILD_32BIT
+ const size_t prime = 16777619U;
+#elif defined ZEN_BUILD_64BIT
+ const size_t prime = 1099511628211ULL;
+#endif
+ static_assert(sizeof(typename std::iterator_traits<ByteIterator>::value_type) == 1, "");
+
+ for (; first != last; ++first)
+ {
+ hashVal ^= static_cast<size_t>(*first);
+ hashVal *= prime;
+ }
+ return hashVal;
+}
+}
+
+#endif //STL_TOOLS_H_84567184321434
diff --git a/zen/string_base.h b/zen/string_base.h
index b5e45c0e..2ec2e894 100755
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -1,613 +1,613 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef STRING_BASE_H_083217454562342526
-#define STRING_BASE_H_083217454562342526
-
-#include <algorithm>
-#include <cassert>
-#include <cstdint>
-#include <atomic>
-#include "string_tools.h"
-
-//Zbase - a policy based string class optimizing performance and flexibility
-
-namespace zen
-{
-/*
-Allocator Policy:
------------------
- void* allocate(size_t size) //throw std::bad_alloc
- void deallocate(void* ptr)
- size_t calcCapacity(size_t length)
-*/
-class AllocatorOptimalSpeed //exponential growth + min size
-{
-protected:
- //::operator new/ ::operator delete show same performance characterisics like malloc()/free()!
- static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
- static void deallocate(void* ptr) { ::free(ptr); }
- static size_t calcCapacity(size_t length) { return std::max<size_t>(16, std::max(length + length / 2, length)); }
- //- size_t might overflow! => better catch here than return a too small size covering up the real error: a way too large length!
- //- any growth rate should not exceed golden ratio: 1.618033989
-};
-
-
-class AllocatorOptimalMemory //no wasted memory, but more reallocations required when manipulating string
-{
-protected:
- static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
- static void deallocate(void* ptr) { ::free(ptr); }
- static size_t calcCapacity(size_t length) { return length; }
-};
-
-/*
-Storage Policy:
----------------
-template <typename Char, //Character Type
- class AP> //Allocator Policy
-
- Char* create(size_t size)
- Char* create(size_t size, size_t minCapacity)
- Char* clone(Char* ptr)
- void destroy(Char* ptr) //must handle "destroy(nullptr)"!
- bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr"
- size_t length(const Char* ptr)
- void setLength(Char* ptr, size_t newLength)
-*/
-
-template <class Char, //Character Type
- class AP> //Allocator Policy
-class StorageDeepCopy : public AP
-{
-protected:
- ~StorageDeepCopy() {}
-
- Char* create(size_t size) { return create(size, size); }
- Char* create(size_t size, size_t minCapacity)
- {
- assert(size <= minCapacity);
- const size_t newCapacity = AP::calcCapacity(minCapacity);
- assert(newCapacity >= minCapacity);
-
- Descriptor* const newDescr = static_cast<Descriptor*>(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc
- new (newDescr) Descriptor(size, newCapacity);
-
- return reinterpret_cast<Char*>(newDescr + 1); //alignment note: "newDescr + 1" is Descriptor-aligned, which is larger than alignment for Char-array! => no problem!
- }
-
- Char* clone(Char* ptr)
- {
- const size_t len = length(ptr);
- Char* newData = create(len); //throw std::bad_alloc
- std::copy(ptr, ptr + len + 1, newData);
- return newData;
- }
-
- void destroy(Char* ptr)
- {
- if (!ptr) return; //support "destroy(nullptr)"
-
- Descriptor* const d = descr(ptr);
- d->~Descriptor();
- this->deallocate(d);
- }
-
- //this needs to be checked before writing to "ptr"
- static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; }
- static size_t length(const Char* ptr) { return descr(ptr)->length; }
-
- static void setLength(Char* ptr, size_t newLength)
- {
- assert(canWrite(ptr, newLength));
- descr(ptr)->length = newLength;
- }
-
-private:
- struct Descriptor
- {
- Descriptor(size_t len, size_t cap) :
- length (static_cast<uint32_t>(len)),
- capacity(static_cast<uint32_t>(cap)) {}
-
- uint32_t length;
- uint32_t capacity; //allocated size without null-termination
- };
-
- static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
- static const Descriptor* descr(const Char* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; }
-};
-
-
-template <class Char, //Character Type
- class AP> //Allocator Policy
-class StorageRefCountThreadSafe : public AP
-{
-protected:
- ~StorageRefCountThreadSafe() {}
-
- Char* create(size_t size) { return create(size, size); }
- Char* create(size_t size, size_t minCapacity)
- {
- assert(size <= minCapacity);
-
- const size_t newCapacity = AP::calcCapacity(minCapacity);
- assert(newCapacity >= minCapacity);
-
- Descriptor* const newDescr = static_cast<Descriptor*>(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc
- new (newDescr) Descriptor(size, newCapacity);
-
- return reinterpret_cast<Char*>(newDescr + 1);
- }
-
- static Char* clone(Char* ptr)
- {
- ++descr(ptr)->refCount;
- return ptr;
- }
-
- void destroy(Char* ptr)
- {
- assert(ptr != reinterpret_cast<Char*>(0x1)); //detect double-deletion
-
- if (!ptr) //support "destroy(nullptr)"
- {
- return;
- }
-
- Descriptor* const d = descr(ptr);
-
- if (--(d->refCount) == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation!
- {
- d->~Descriptor();
- this->deallocate(d);
- }
- }
-
- static bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr"
- {
- const Descriptor* const d = descr(ptr);
- assert(d->refCount > 0);
- return d->refCount == 1 && minCapacity <= d->capacity;
- }
-
- static size_t length(const Char* ptr) { return descr(ptr)->length; }
-
- static void setLength(Char* ptr, size_t newLength)
- {
- assert(canWrite(ptr, newLength));
- descr(ptr)->length = static_cast<uint32_t>(newLength);
- }
-
-private:
- struct Descriptor
- {
- Descriptor(size_t len, size_t cap) :
- length (static_cast<uint32_t>(len)),
- capacity(static_cast<uint32_t>(cap)) { static_assert(ATOMIC_INT_LOCK_FREE == 2, ""); } //2: "The atomic type is always lock-free"
-
- std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default!
- uint32_t length;
- uint32_t capacity; //allocated size without null-termination
- };
-
- static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
- static const Descriptor* descr(const Char* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; }
-};
-
-
-template <class Char>
-using DefaultStoragePolicy = StorageRefCountThreadSafe<Char, AllocatorOptimalSpeed>;
-
-
-//################################################################################################################################################################
-
-//perf note: interestingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison
-
-template <class Char, //Character Type
- template <class> class SP = DefaultStoragePolicy> //Storage Policy
-class Zbase : public SP<Char>
-{
-public:
- Zbase();
- Zbase(const Char* str) : Zbase(str, str + strLength(str)) {} //implicit conversion from a C-string!
- Zbase(const Char* str, size_t len) : Zbase(str, str + len) {}
- Zbase(const Zbase& str);
- Zbase(Zbase&& tmp) noexcept;
- template <class InputIterator>
- Zbase(InputIterator first, InputIterator last);
- //explicit Zbase(Char ch); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error! //-> non-standard extension!!!
-
- ~Zbase();
-
- //operator const Char* () const; //NO implicit conversion to a C-string!! Many problems... one of them: if we forget to provide operator overloads, it'll just work with a Char*...
-
- //STL accessors
- using iterator = Char*;
- using const_iterator = const Char*;
- using reference = Char&;
- using const_reference = const Char&;
- using value_type = Char;
-
- iterator begin();
- iterator end ();
- const_iterator begin () const { return rawStr_; }
- const_iterator end () const { return rawStr_ + length(); }
- const_iterator cbegin() const { return begin(); }
- const_iterator cend () const { return end (); }
-
- //std::string functions
- size_t length() const;
- size_t size () const { return length(); }
- const Char* c_str() const { return rawStr_; } //C-string format with 0-termination
- const Char operator[](size_t pos) const;
- bool empty() const { return length() == 0; }
- void clear();
- size_t find (const Zbase& str, size_t pos = 0) const; //
- size_t find (const Char* str, size_t pos = 0) const; //
- 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);
- void reserve(size_t minCapacity);
- Zbase& assign(const Char* str, size_t len) { return assign(str, str + len); }
- Zbase& append(const Char* str, size_t len) { return append(str, str + len); }
-
- template <class InputIterator> Zbase& assign(InputIterator first, InputIterator last);
- template <class InputIterator> Zbase& append(InputIterator first, InputIterator last);
-
- void resize(size_t newSize, Char fillChar = 0);
- void swap(Zbase& str) { std::swap(rawStr_, str.rawStr_); }
- void push_back(Char val) { operator+=(val); } //STL access
- void pop_back();
-
- Zbase& operator=(Zbase&& tmp) noexcept;
- Zbase& operator=(const Zbase& str);
- Zbase& operator=(const Char* str) { return assign(str, strLength(str)); }
- Zbase& operator=(Char ch) { return assign(&ch, 1); }
- Zbase& operator+=(const Zbase& str) { return append(str.c_str(), str.length()); }
- Zbase& operator+=(const Char* str) { return append(str, strLength(str)); }
- Zbase& operator+=(Char ch) { return append(&ch, 1); }
-
- static const size_t npos = static_cast<size_t>(-1);
-
-private:
- Zbase (int) = delete; //
- Zbase& operator= (int) = delete; //detect usage errors by creating an intentional ambiguity with "Char"
- Zbase& operator+=(int) = delete; //
- void push_back (int) = delete; //
-
- Char* rawStr_;
-};
-
-template <class Char, template <class> class SP> bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs);
-template <class Char, template <class> class SP> bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs);
-template <class Char, template <class> class SP> inline bool operator==(const Char* lhs, const Zbase<Char, SP>& rhs) { return operator==(rhs, lhs); }
-
-template <class Char, template <class> class SP> inline bool operator!=(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return !operator==(lhs, rhs); }
-template <class Char, template <class> class SP> inline bool operator!=(const Zbase<Char, SP>& lhs, const Char* rhs) { return !operator==(lhs, rhs); }
-template <class Char, template <class> class SP> inline bool operator!=(const Char* lhs, const Zbase<Char, SP>& rhs) { return !operator==(lhs, rhs); }
-
-template <class Char, template <class> class SP> bool operator<(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs);
-template <class Char, template <class> class SP> bool operator<(const Zbase<Char, SP>& lhs, const Char* rhs);
-template <class Char, template <class> class SP> bool operator<(const Char* lhs, const Zbase<Char, SP>& rhs);
-
-template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs) += rhs; }
-template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Char* rhs) { return Zbase<Char, SP>(lhs) += rhs; }
-template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, Char rhs) { return Zbase<Char, SP>(lhs) += rhs; }
-
-//don't use unified first argument but save one move-construction in the r-value case instead!
-template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, const Zbase<Char, SP>& rhs) { return std::move(lhs += rhs); } //the move *is* needed!!!
-template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, const Char* rhs) { return std::move(lhs += rhs); } //lhs, is an l-value parameter...
-template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, Char rhs) { return std::move(lhs += rhs); } //and not a local variable => no copy elision
-
-template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+( Char lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(&lhs, 1) += rhs; }
-template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Char* lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs ) += rhs; }
-
-
-
-
-
-
-
-
-
-
-
-
-
-//################################# implementation ########################################
-template <class Char, template <class> class SP> inline
-Zbase<Char, SP>::Zbase()
-{
- //resist the temptation to avoid this allocation by referening a static global: NO performance advantage, MT issues!
- rawStr_ = this->create(0);
- rawStr_[0] = 0;
-}
-
-
-template <class Char, template <class> class SP>
-template <class InputIterator> inline
-Zbase<Char, SP>::Zbase(InputIterator first, InputIterator last)
-{
- rawStr_ = this->create(std::distance(first, last));
- *std::copy(first, last, rawStr_) = 0;
-}
-
-
-template <class Char, template <class> class SP> inline
-Zbase<Char, SP>::Zbase(const Zbase<Char, SP>& str)
-{
- rawStr_ = this->clone(str.rawStr_);
-}
-
-
-template <class Char, template <class> class SP> inline
-Zbase<Char, SP>::Zbase(Zbase<Char, SP>&& tmp) noexcept
-{
- rawStr_ = tmp.rawStr_;
- tmp.rawStr_ = nullptr; //usually nullptr would violate the class invarants, but it is good enough for the destructor!
- //caveat: do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory!
-}
-
-
-template <class Char, template <class> class SP> inline
-Zbase<Char, SP>::~Zbase()
-{
- static_assert(noexcept(this->~Zbase()), ""); //has exception spec of compiler-generated destructor by default
-
- this->destroy(rawStr_); //rawStr_ may be nullptr; see move constructor!
-}
-
-
-template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::find(const Zbase& str, size_t pos) const
-{
- assert(pos <= length());
- const size_t len = length();
- const Char* thisEnd = begin() + len; //respect embedded 0
- const Char* it = std::search(begin() + std::min(pos, len), thisEnd,
- str.begin(), str.end());
- return it == thisEnd ? npos : it - begin();
-}
-
-
-template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::find(const Char* str, size_t pos) const
-{
- assert(pos <= length());
- const size_t len = length();
- const Char* thisEnd = begin() + len; //respect embedded 0
- const Char* it = std::search(begin() + std::min(pos, len), thisEnd,
- str, str + strLength(str));
- return it == thisEnd ? npos : it - begin();
-}
-
-
-template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::find(Char ch, size_t pos) const
-{
- assert(pos <= length());
- const size_t len = length();
- const Char* thisEnd = begin() + len; //respect embedded 0
- const Char* it = std::find(begin() + std::min(pos, len), thisEnd, ch);
- return it == thisEnd ? npos : it - begin();
-}
-
-
-template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::rfind(Char ch, size_t pos) const
-{
- assert(pos == npos || pos <= length());
- const size_t len = length();
- const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + 1, len));
- const Char* it = find_last(begin(), currEnd, ch);
- return it == currEnd ? npos : it - begin();
-}
-
-
-template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const
-{
- assert(pos == npos || pos <= length());
- const size_t strLen = strLength(str);
- const size_t len = length();
- const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + strLen, len));
- const Char* it = search_last(begin(), currEnd,
- str, str + strLen);
- return it == currEnd ? npos : it - begin();
-}
-
-
-template <class Char, template <class> class SP> inline
-void Zbase<Char, SP>::resize(size_t newSize, Char fillChar)
-{
- const size_t oldSize = length();
- if (this->canWrite(rawStr_, newSize))
- {
- if (oldSize < newSize)
- std::fill(rawStr_ + oldSize, rawStr_ + newSize, fillChar);
- rawStr_[newSize] = 0;
- this->setLength(rawStr_, newSize);
- }
- else
- {
- Char* newStr = this->create(newSize);
- if (oldSize < newSize)
- {
- std::copy(rawStr_, rawStr_ + oldSize, newStr);
- std::fill(newStr + oldSize, newStr + newSize, fillChar);
- }
- else
- std::copy(rawStr_, rawStr_ + newSize, newStr);
- newStr[newSize] = 0;
-
- this->destroy(rawStr_);
- rawStr_ = newStr;
- }
-}
-
-
-template <class Char, template <class> class SP> inline
-bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
-{
- return lhs.length() == rhs.length() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); //respect embedded 0
-}
-
-
-template <class Char, template <class> class SP> inline
-bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs)
-{
- return lhs.length() == strLength(rhs) && std::equal(lhs.begin(), lhs.end(), rhs); //respect embedded 0
-}
-
-
-template <class Char, template <class> class SP> inline
-bool operator<(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
-{
- return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0
- rhs.begin(), rhs.end());
-}
-
-
-template <class Char, template <class> class SP> inline
-bool operator<(const Zbase<Char, SP>& lhs, const Char* rhs)
-{
- return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0
- rhs, rhs + strLength(rhs));
-}
-
-
-template <class Char, template <class> class SP> inline
-bool operator<(const Char* lhs, const Zbase<Char, SP>& rhs)
-{
- return std::lexicographical_compare(lhs, lhs + strLength(lhs), //respect embedded 0
- rhs.begin(), rhs.end());
-}
-
-
-template <class Char, template <class> class SP> inline
-size_t Zbase<Char, SP>::length() const
-{
- return SP<Char>::length(rawStr_);
-}
-
-
-template <class Char, template <class> class SP> inline
-const Char Zbase<Char, SP>::operator[](size_t pos) const
-{
- assert(pos < length()); //design by contract! no runtime check!
- return rawStr_[pos];
-}
-
-
-template <class Char, template <class> class SP> inline
-auto Zbase<Char, SP>::begin() -> iterator
-{
- reserve(length()); //make unshared!
- return rawStr_;
-}
-
-
-template <class Char, template <class> class SP> inline
-auto Zbase<Char, SP>::end() -> iterator
-{
- return begin() + length();
-}
-
-
-template <class Char, template <class> class SP> inline
-void Zbase<Char, SP>::clear()
-{
- if (!empty())
- {
- if (this->canWrite(rawStr_, 0))
- {
- rawStr_[0] = 0; //keep allocated memory
- this->setLength(rawStr_, 0); //
- }
- else
- *this = Zbase();
- }
-}
-
-
-template <class Char, template <class> class SP> inline
-void Zbase<Char, SP>::reserve(size_t minCapacity) //make unshared and check capacity
-{
- if (!this->canWrite(rawStr_, minCapacity))
- {
- //allocate a new string
- const size_t len = length();
- Char* newStr = this->create(len, std::max(len, minCapacity)); //reserve() must NEVER shrink the string: logical const!
- std::copy(rawStr_, rawStr_ + len + 1, newStr); //include 0-termination
-
- this->destroy(rawStr_);
- rawStr_ = newStr;
- }
-}
-
-
-template <class Char, template <class> class SP>
-template <class InputIterator> inline
-Zbase<Char, SP>& Zbase<Char, SP>::assign(InputIterator first, InputIterator last)
-{
- const size_t len = std::distance(first, last);
- if (this->canWrite(rawStr_, len))
- {
- *std::copy(first, last, rawStr_) = 0;
- this->setLength(rawStr_, len);
- }
- else
- *this = Zbase(first, last);
-
- return *this;
-}
-
-
-template <class Char, template <class> class SP>
-template <class InputIterator> inline
-Zbase<Char, SP>& Zbase<Char, SP>::append(InputIterator first, InputIterator last)
-{
- const size_t len = std::distance(first, last);
- if (len > 0) //avoid making this string unshared for no reason
- {
- const size_t thisLen = length();
- reserve(thisLen + len); //make unshared and check capacity
-
- *std::copy(first, last, rawStr_ + thisLen) = 0;
- this->setLength(rawStr_, thisLen + len);
- }
- return *this;
-}
-
-
-template <class Char, template <class> class SP> inline
-Zbase<Char, SP>& Zbase<Char, SP>::operator=(const Zbase<Char, SP>& str)
-{
- Zbase<Char, SP>(str).swap(*this);
- return *this;
-}
-
-
-template <class Char, template <class> class SP> inline
-Zbase<Char, SP>& Zbase<Char, SP>::operator=(Zbase<Char, SP>&& tmp) noexcept
-{
- swap(tmp); //don't use unifying assignment but save one move-construction in the r-value case instead!
- return *this;
-}
-
-template <class Char, template <class> class SP> inline
-void Zbase<Char, SP>::pop_back()
-{
- const size_t len = length();
- assert(len > 0);
- if (len > 0)
- resize(len - 1);
-}
-}
-
-#endif //STRING_BASE_H_083217454562342526
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef STRING_BASE_H_083217454562342526
+#define STRING_BASE_H_083217454562342526
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <atomic>
+#include "string_tools.h"
+
+//Zbase - a policy based string class optimizing performance and flexibility
+
+namespace zen
+{
+/*
+Allocator Policy:
+-----------------
+ void* allocate(size_t size) //throw std::bad_alloc
+ void deallocate(void* ptr)
+ size_t calcCapacity(size_t length)
+*/
+class AllocatorOptimalSpeed //exponential growth + min size
+{
+protected:
+ //::operator new/ ::operator delete show same performance characterisics like malloc()/free()!
+ static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
+ static void deallocate(void* ptr) { ::free(ptr); }
+ static size_t calcCapacity(size_t length) { return std::max<size_t>(16, std::max(length + length / 2, length)); }
+ //- size_t might overflow! => better catch here than return a too small size covering up the real error: a way too large length!
+ //- any growth rate should not exceed golden ratio: 1.618033989
+};
+
+
+class AllocatorOptimalMemory //no wasted memory, but more reallocations required when manipulating string
+{
+protected:
+ static void* allocate(size_t size) { return ::malloc(size); } //throw std::bad_alloc
+ static void deallocate(void* ptr) { ::free(ptr); }
+ static size_t calcCapacity(size_t length) { return length; }
+};
+
+/*
+Storage Policy:
+---------------
+template <typename Char, //Character Type
+ class AP> //Allocator Policy
+
+ Char* create(size_t size)
+ Char* create(size_t size, size_t minCapacity)
+ Char* clone(Char* ptr)
+ void destroy(Char* ptr) //must handle "destroy(nullptr)"!
+ bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr"
+ size_t length(const Char* ptr)
+ void setLength(Char* ptr, size_t newLength)
+*/
+
+template <class Char, //Character Type
+ class AP> //Allocator Policy
+class StorageDeepCopy : public AP
+{
+protected:
+ ~StorageDeepCopy() {}
+
+ Char* create(size_t size) { return create(size, size); }
+ Char* create(size_t size, size_t minCapacity)
+ {
+ assert(size <= minCapacity);
+ const size_t newCapacity = AP::calcCapacity(minCapacity);
+ assert(newCapacity >= minCapacity);
+
+ Descriptor* const newDescr = static_cast<Descriptor*>(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc
+ new (newDescr) Descriptor(size, newCapacity);
+
+ return reinterpret_cast<Char*>(newDescr + 1); //alignment note: "newDescr + 1" is Descriptor-aligned, which is larger than alignment for Char-array! => no problem!
+ }
+
+ Char* clone(Char* ptr)
+ {
+ const size_t len = length(ptr);
+ Char* newData = create(len); //throw std::bad_alloc
+ std::copy(ptr, ptr + len + 1, newData);
+ return newData;
+ }
+
+ void destroy(Char* ptr)
+ {
+ if (!ptr) return; //support "destroy(nullptr)"
+
+ Descriptor* const d = descr(ptr);
+ d->~Descriptor();
+ this->deallocate(d);
+ }
+
+ //this needs to be checked before writing to "ptr"
+ static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; }
+ static size_t length(const Char* ptr) { return descr(ptr)->length; }
+
+ static void setLength(Char* ptr, size_t newLength)
+ {
+ assert(canWrite(ptr, newLength));
+ descr(ptr)->length = newLength;
+ }
+
+private:
+ struct Descriptor
+ {
+ Descriptor(size_t len, size_t cap) :
+ length (static_cast<uint32_t>(len)),
+ capacity(static_cast<uint32_t>(cap)) {}
+
+ uint32_t length;
+ uint32_t capacity; //allocated size without null-termination
+ };
+
+ static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
+ static const Descriptor* descr(const Char* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; }
+};
+
+
+template <class Char, //Character Type
+ class AP> //Allocator Policy
+class StorageRefCountThreadSafe : public AP
+{
+protected:
+ ~StorageRefCountThreadSafe() {}
+
+ Char* create(size_t size) { return create(size, size); }
+ Char* create(size_t size, size_t minCapacity)
+ {
+ assert(size <= minCapacity);
+
+ const size_t newCapacity = AP::calcCapacity(minCapacity);
+ assert(newCapacity >= minCapacity);
+
+ Descriptor* const newDescr = static_cast<Descriptor*>(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc
+ new (newDescr) Descriptor(size, newCapacity);
+
+ return reinterpret_cast<Char*>(newDescr + 1);
+ }
+
+ static Char* clone(Char* ptr)
+ {
+ ++descr(ptr)->refCount;
+ return ptr;
+ }
+
+ void destroy(Char* ptr)
+ {
+ assert(ptr != reinterpret_cast<Char*>(0x1)); //detect double-deletion
+
+ if (!ptr) //support "destroy(nullptr)"
+ {
+ return;
+ }
+
+ Descriptor* const d = descr(ptr);
+
+ if (--(d->refCount) == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation!
+ {
+ d->~Descriptor();
+ this->deallocate(d);
+ }
+ }
+
+ static bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr"
+ {
+ const Descriptor* const d = descr(ptr);
+ assert(d->refCount > 0);
+ return d->refCount == 1 && minCapacity <= d->capacity;
+ }
+
+ static size_t length(const Char* ptr) { return descr(ptr)->length; }
+
+ static void setLength(Char* ptr, size_t newLength)
+ {
+ assert(canWrite(ptr, newLength));
+ descr(ptr)->length = static_cast<uint32_t>(newLength);
+ }
+
+private:
+ struct Descriptor
+ {
+ Descriptor(size_t len, size_t cap) :
+ length (static_cast<uint32_t>(len)),
+ capacity(static_cast<uint32_t>(cap)) { static_assert(ATOMIC_INT_LOCK_FREE == 2, ""); } //2: "The atomic type is always lock-free"
+
+ std::atomic<unsigned int> refCount { 1 }; //std:atomic is uninitialized by default!
+ uint32_t length;
+ uint32_t capacity; //allocated size without null-termination
+ };
+
+ static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; }
+ static const Descriptor* descr(const Char* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; }
+};
+
+
+template <class Char>
+using DefaultStoragePolicy = StorageRefCountThreadSafe<Char, AllocatorOptimalSpeed>;
+
+
+//################################################################################################################################################################
+
+//perf note: interestingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison
+
+template <class Char, //Character Type
+ template <class> class SP = DefaultStoragePolicy> //Storage Policy
+class Zbase : public SP<Char>
+{
+public:
+ Zbase();
+ Zbase(const Char* str) : Zbase(str, str + strLength(str)) {} //implicit conversion from a C-string!
+ Zbase(const Char* str, size_t len) : Zbase(str, str + len) {}
+ Zbase(const Zbase& str);
+ Zbase(Zbase&& tmp) noexcept;
+ template <class InputIterator>
+ Zbase(InputIterator first, InputIterator last);
+ //explicit Zbase(Char ch); //dangerous if implicit: Char buffer[]; return buffer[0]; ups... forgot &, but not a compiler error! //-> non-standard extension!!!
+
+ ~Zbase();
+
+ //operator const Char* () const; //NO implicit conversion to a C-string!! Many problems... one of them: if we forget to provide operator overloads, it'll just work with a Char*...
+
+ //STL accessors
+ using iterator = Char*;
+ using const_iterator = const Char*;
+ using reference = Char&;
+ using const_reference = const Char&;
+ using value_type = Char;
+
+ iterator begin();
+ iterator end ();
+ const_iterator begin () const { return rawStr_; }
+ const_iterator end () const { return rawStr_ + length(); }
+ const_iterator cbegin() const { return begin(); }
+ const_iterator cend () const { return end (); }
+
+ //std::string functions
+ size_t length() const;
+ size_t size () const { return length(); }
+ const Char* c_str() const { return rawStr_; } //C-string format with 0-termination
+ const Char operator[](size_t pos) const;
+ bool empty() const { return length() == 0; }
+ void clear();
+ size_t find (const Zbase& str, size_t pos = 0) const; //
+ size_t find (const Char* str, size_t pos = 0) const; //
+ 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);
+ void reserve(size_t minCapacity);
+ Zbase& assign(const Char* str, size_t len) { return assign(str, str + len); }
+ Zbase& append(const Char* str, size_t len) { return append(str, str + len); }
+
+ template <class InputIterator> Zbase& assign(InputIterator first, InputIterator last);
+ template <class InputIterator> Zbase& append(InputIterator first, InputIterator last);
+
+ void resize(size_t newSize, Char fillChar = 0);
+ void swap(Zbase& str) { std::swap(rawStr_, str.rawStr_); }
+ void push_back(Char val) { operator+=(val); } //STL access
+ void pop_back();
+
+ Zbase& operator=(Zbase&& tmp) noexcept;
+ Zbase& operator=(const Zbase& str);
+ Zbase& operator=(const Char* str) { return assign(str, strLength(str)); }
+ Zbase& operator=(Char ch) { return assign(&ch, 1); }
+ Zbase& operator+=(const Zbase& str) { return append(str.c_str(), str.length()); }
+ Zbase& operator+=(const Char* str) { return append(str, strLength(str)); }
+ Zbase& operator+=(Char ch) { return append(&ch, 1); }
+
+ static const size_t npos = static_cast<size_t>(-1);
+
+private:
+ Zbase (int) = delete; //
+ Zbase& operator= (int) = delete; //detect usage errors by creating an intentional ambiguity with "Char"
+ Zbase& operator+=(int) = delete; //
+ void push_back (int) = delete; //
+
+ Char* rawStr_;
+};
+
+template <class Char, template <class> class SP> bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs);
+template <class Char, template <class> class SP> bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs);
+template <class Char, template <class> class SP> inline bool operator==(const Char* lhs, const Zbase<Char, SP>& rhs) { return operator==(rhs, lhs); }
+
+template <class Char, template <class> class SP> inline bool operator!=(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return !operator==(lhs, rhs); }
+template <class Char, template <class> class SP> inline bool operator!=(const Zbase<Char, SP>& lhs, const Char* rhs) { return !operator==(lhs, rhs); }
+template <class Char, template <class> class SP> inline bool operator!=(const Char* lhs, const Zbase<Char, SP>& rhs) { return !operator==(lhs, rhs); }
+
+template <class Char, template <class> class SP> bool operator<(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs);
+template <class Char, template <class> class SP> bool operator<(const Zbase<Char, SP>& lhs, const Char* rhs);
+template <class Char, template <class> class SP> bool operator<(const Char* lhs, const Zbase<Char, SP>& rhs);
+
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs) += rhs; }
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, const Char* rhs) { return Zbase<Char, SP>(lhs) += rhs; }
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Zbase<Char, SP>& lhs, Char rhs) { return Zbase<Char, SP>(lhs) += rhs; }
+
+//don't use unified first argument but save one move-construction in the r-value case instead!
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, const Zbase<Char, SP>& rhs) { return std::move(lhs += rhs); } //the move *is* needed!!!
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, const Char* rhs) { return std::move(lhs += rhs); } //lhs, is an l-value parameter...
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(Zbase<Char, SP>&& lhs, Char rhs) { return std::move(lhs += rhs); } //and not a local variable => no copy elision
+
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+( Char lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(&lhs, 1) += rhs; }
+template <class Char, template <class> class SP> inline Zbase<Char, SP> operator+(const Char* lhs, const Zbase<Char, SP>& rhs) { return Zbase<Char, SP>(lhs ) += rhs; }
+
+
+
+
+
+
+
+
+
+
+
+
+
+//################################# implementation ########################################
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>::Zbase()
+{
+ //resist the temptation to avoid this allocation by referening a static global: NO performance advantage, MT issues!
+ rawStr_ = this->create(0);
+ rawStr_[0] = 0;
+}
+
+
+template <class Char, template <class> class SP>
+template <class InputIterator> inline
+Zbase<Char, SP>::Zbase(InputIterator first, InputIterator last)
+{
+ rawStr_ = this->create(std::distance(first, last));
+ *std::copy(first, last, rawStr_) = 0;
+}
+
+
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>::Zbase(const Zbase<Char, SP>& str)
+{
+ rawStr_ = this->clone(str.rawStr_);
+}
+
+
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>::Zbase(Zbase<Char, SP>&& tmp) noexcept
+{
+ rawStr_ = tmp.rawStr_;
+ tmp.rawStr_ = nullptr; //usually nullptr would violate the class invarants, but it is good enough for the destructor!
+ //caveat: do not increment ref-count of an unshared string! We'd lose optimization opportunity of reusing its memory!
+}
+
+
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>::~Zbase()
+{
+ static_assert(noexcept(this->~Zbase()), ""); //has exception spec of compiler-generated destructor by default
+
+ this->destroy(rawStr_); //rawStr_ may be nullptr; see move constructor!
+}
+
+
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::find(const Zbase& str, size_t pos) const
+{
+ assert(pos <= length());
+ const size_t len = length();
+ const Char* thisEnd = begin() + len; //respect embedded 0
+ const Char* it = std::search(begin() + std::min(pos, len), thisEnd,
+ str.begin(), str.end());
+ return it == thisEnd ? npos : it - begin();
+}
+
+
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::find(const Char* str, size_t pos) const
+{
+ assert(pos <= length());
+ const size_t len = length();
+ const Char* thisEnd = begin() + len; //respect embedded 0
+ const Char* it = std::search(begin() + std::min(pos, len), thisEnd,
+ str, str + strLength(str));
+ return it == thisEnd ? npos : it - begin();
+}
+
+
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::find(Char ch, size_t pos) const
+{
+ assert(pos <= length());
+ const size_t len = length();
+ const Char* thisEnd = begin() + len; //respect embedded 0
+ const Char* it = std::find(begin() + std::min(pos, len), thisEnd, ch);
+ return it == thisEnd ? npos : it - begin();
+}
+
+
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::rfind(Char ch, size_t pos) const
+{
+ assert(pos == npos || pos <= length());
+ const size_t len = length();
+ const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + 1, len));
+ const Char* it = find_last(begin(), currEnd, ch);
+ return it == currEnd ? npos : it - begin();
+}
+
+
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const
+{
+ assert(pos == npos || pos <= length());
+ const size_t strLen = strLength(str);
+ const size_t len = length();
+ const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + strLen, len));
+ const Char* it = search_last(begin(), currEnd,
+ str, str + strLen);
+ return it == currEnd ? npos : it - begin();
+}
+
+
+template <class Char, template <class> class SP> inline
+void Zbase<Char, SP>::resize(size_t newSize, Char fillChar)
+{
+ const size_t oldSize = length();
+ if (this->canWrite(rawStr_, newSize))
+ {
+ if (oldSize < newSize)
+ std::fill(rawStr_ + oldSize, rawStr_ + newSize, fillChar);
+ rawStr_[newSize] = 0;
+ this->setLength(rawStr_, newSize);
+ }
+ else
+ {
+ Char* newStr = this->create(newSize);
+ if (oldSize < newSize)
+ {
+ std::copy(rawStr_, rawStr_ + oldSize, newStr);
+ std::fill(newStr + oldSize, newStr + newSize, fillChar);
+ }
+ else
+ std::copy(rawStr_, rawStr_ + newSize, newStr);
+ newStr[newSize] = 0;
+
+ this->destroy(rawStr_);
+ rawStr_ = newStr;
+ }
+}
+
+
+template <class Char, template <class> class SP> inline
+bool operator==(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
+{
+ return lhs.length() == rhs.length() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); //respect embedded 0
+}
+
+
+template <class Char, template <class> class SP> inline
+bool operator==(const Zbase<Char, SP>& lhs, const Char* rhs)
+{
+ return lhs.length() == strLength(rhs) && std::equal(lhs.begin(), lhs.end(), rhs); //respect embedded 0
+}
+
+
+template <class Char, template <class> class SP> inline
+bool operator<(const Zbase<Char, SP>& lhs, const Zbase<Char, SP>& rhs)
+{
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0
+ rhs.begin(), rhs.end());
+}
+
+
+template <class Char, template <class> class SP> inline
+bool operator<(const Zbase<Char, SP>& lhs, const Char* rhs)
+{
+ return std::lexicographical_compare(lhs.begin(), lhs.end(), //respect embedded 0
+ rhs, rhs + strLength(rhs));
+}
+
+
+template <class Char, template <class> class SP> inline
+bool operator<(const Char* lhs, const Zbase<Char, SP>& rhs)
+{
+ return std::lexicographical_compare(lhs, lhs + strLength(lhs), //respect embedded 0
+ rhs.begin(), rhs.end());
+}
+
+
+template <class Char, template <class> class SP> inline
+size_t Zbase<Char, SP>::length() const
+{
+ return SP<Char>::length(rawStr_);
+}
+
+
+template <class Char, template <class> class SP> inline
+const Char Zbase<Char, SP>::operator[](size_t pos) const
+{
+ assert(pos < length()); //design by contract! no runtime check!
+ return rawStr_[pos];
+}
+
+
+template <class Char, template <class> class SP> inline
+auto Zbase<Char, SP>::begin() -> iterator
+{
+ reserve(length()); //make unshared!
+ return rawStr_;
+}
+
+
+template <class Char, template <class> class SP> inline
+auto Zbase<Char, SP>::end() -> iterator
+{
+ return begin() + length();
+}
+
+
+template <class Char, template <class> class SP> inline
+void Zbase<Char, SP>::clear()
+{
+ if (!empty())
+ {
+ if (this->canWrite(rawStr_, 0))
+ {
+ rawStr_[0] = 0; //keep allocated memory
+ this->setLength(rawStr_, 0); //
+ }
+ else
+ *this = Zbase();
+ }
+}
+
+
+template <class Char, template <class> class SP> inline
+void Zbase<Char, SP>::reserve(size_t minCapacity) //make unshared and check capacity
+{
+ if (!this->canWrite(rawStr_, minCapacity))
+ {
+ //allocate a new string
+ const size_t len = length();
+ Char* newStr = this->create(len, std::max(len, minCapacity)); //reserve() must NEVER shrink the string: logical const!
+ std::copy(rawStr_, rawStr_ + len + 1, newStr); //include 0-termination
+
+ this->destroy(rawStr_);
+ rawStr_ = newStr;
+ }
+}
+
+
+template <class Char, template <class> class SP>
+template <class InputIterator> inline
+Zbase<Char, SP>& Zbase<Char, SP>::assign(InputIterator first, InputIterator last)
+{
+ const size_t len = std::distance(first, last);
+ if (this->canWrite(rawStr_, len))
+ {
+ *std::copy(first, last, rawStr_) = 0;
+ this->setLength(rawStr_, len);
+ }
+ else
+ *this = Zbase(first, last);
+
+ return *this;
+}
+
+
+template <class Char, template <class> class SP>
+template <class InputIterator> inline
+Zbase<Char, SP>& Zbase<Char, SP>::append(InputIterator first, InputIterator last)
+{
+ const size_t len = std::distance(first, last);
+ if (len > 0) //avoid making this string unshared for no reason
+ {
+ const size_t thisLen = length();
+ reserve(thisLen + len); //make unshared and check capacity
+
+ *std::copy(first, last, rawStr_ + thisLen) = 0;
+ this->setLength(rawStr_, thisLen + len);
+ }
+ return *this;
+}
+
+
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>& Zbase<Char, SP>::operator=(const Zbase<Char, SP>& str)
+{
+ Zbase<Char, SP>(str).swap(*this);
+ return *this;
+}
+
+
+template <class Char, template <class> class SP> inline
+Zbase<Char, SP>& Zbase<Char, SP>::operator=(Zbase<Char, SP>&& tmp) noexcept
+{
+ swap(tmp); //don't use unifying assignment but save one move-construction in the r-value case instead!
+ return *this;
+}
+
+template <class Char, template <class> class SP> inline
+void Zbase<Char, SP>::pop_back()
+{
+ const size_t len = length();
+ assert(len > 0);
+ if (len > 0)
+ resize(len - 1);
+}
+}
+
+#endif //STRING_BASE_H_083217454562342526
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 236f8df6..bfa14257 100755
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -1,763 +1,763 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef STRING_TOOLS_H_213458973046
-#define STRING_TOOLS_H_213458973046
-
-#include <cctype> //isspace
-#include <cwctype> //iswspace
-#include <cstdio> //sprintf
-#include <cwchar> //swprintf
-#include <algorithm>
-#include <cassert>
-#include <vector>
-#include <sstream>
-#include "stl_tools.h"
-#include "string_traits.h"
-
-
-//enhance arbitray string class with useful non-member functions:
-namespace zen
-{
-template <class Char> bool isWhiteSpace(Char ch);
-template <class Char> bool isDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only!
-template <class Char> bool isHexDigit (Char ch);
-template <class Char> bool isAsciiAlpha(Char ch);
-
-//case-sensitive comparison (compile-time correctness: use different number of arguments as STL comparison predicates!)
-struct CmpBinary { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; };
-
-//basic case-insensitive comparison (considering A-Z only!)
-struct CmpAsciiNoCase { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; };
-
-struct LessAsciiNoCase
-{
- template <class S> //don't support heterogenous input! => use as container predicate only!
- bool operator()(const S& lhs, const S& rhs) const { return CmpAsciiNoCase()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
-};
-
-//both S and T can be strings or char/wchar_t arrays or simple char/wchar_t
-template <class S, class T> bool contains(const S& str, const T& term);
-
-template <class S, class T> bool startsWith(const S& str, const T& prefix);
-template <class S, class T, class Function> bool startsWith(const S& str, const T& prefix, Function cmpStringFun);
-
-template <class S, class T> bool endsWith (const S& str, const T& postfix);
-template <class S, class T, class Function> bool endsWith (const S& str, const T& postfix, Function cmpStringFun);
-
-template <class S, class T> bool strEqual(const S& lhs, const T& rhs);
-template <class S, class T, class Function> bool strEqual(const S& lhs, const T& rhs, Function cmpStringFun);
-
-enum FailureReturnVal
-{
- IF_MISSING_RETURN_ALL,
- IF_MISSING_RETURN_NONE
-};
-
-template <class S, class T> S afterLast (const S& str, const T& term, FailureReturnVal rv);
-template <class S, class T> S beforeLast (const S& str, const T& term, FailureReturnVal rv);
-template <class S, class T> S afterFirst (const S& str, const T& term, FailureReturnVal rv);
-template <class S, class T> S beforeFirst(const S& str, const T& term, FailureReturnVal rv);
-
-enum class SplitType
-{
- ALLOW_EMPTY,
- SKIP_EMPTY
-};
-template <class S, class T> std::vector<S> split(const S& str, const T& delimiter, SplitType st);
-
-template <class S> S trimCpy(S str, bool fromLeft = true, bool fromRight = true);
-template <class S> void trim (S& str, bool fromLeft = true, bool fromRight = true);
-template <class S, class Function> void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar);
-
-template <class S, class T, class U> void replace ( S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true);
-template <class S, class T, class U> S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true);
-
-//high-performance conversion between numbers and strings
-template <class S, class Num> S numberTo(const Num& number);
-template <class Num, class S> Num stringTo(const S& str);
-
-std::pair<char, char> hexify (unsigned char c, bool upperCase = true);
-char unhexify(char high, char low);
-
-template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf()
-
-//string to string conversion: converts string-like type into char-compatible target string class
-template <class T, class S> T copyStringTo(S&& str);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//---------------------- implementation ----------------------
-template <> inline
-bool isWhiteSpace(char ch)
-{
- assert(ch != 0); //std C++ does not consider 0 as white space
- //caveat 1: std::isspace() takes an int, but expects an unsigned char
- //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
- return static_cast<unsigned char>(ch) < 128 &&
- std::isspace(static_cast<unsigned char>(ch)) != 0;
-}
-
-template <> inline
-bool isWhiteSpace(wchar_t ch)
-{
- assert(ch != 0); //std C++ does not consider 0 as white space
- return std::iswspace(ch) != 0;
-}
-
-
-template <class Char> inline
-bool isDigit(Char ch) //similar to implmenetation of std::isdigit()!
-{
- static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
- return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9');
-}
-
-
-template <class Char> inline
-bool isHexDigit(Char c)
-{
- static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
- return (static_cast<Char>('0') <= c && c <= static_cast<Char>('9')) ||
- (static_cast<Char>('A') <= c && c <= static_cast<Char>('F')) ||
- (static_cast<Char>('a') <= c && c <= static_cast<Char>('f'));
-}
-
-
-template <class Char> inline
-bool isAsciiAlpha(Char c)
-{
- static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
- return (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z')) ||
- (static_cast<Char>('a') <= c && c <= static_cast<Char>('z'));
-}
-
-
-template <class S, class T, class Function> inline
-bool startsWith(const S& str, const T& prefix, Function cmpStringFun)
-{
- const size_t pfLen = strLength(prefix);
- if (strLength(str) < pfLen)
- return false;
-
- return cmpStringFun(strBegin(str), pfLen,
- strBegin(prefix), pfLen) == 0;
-}
-
-
-template <class S, class T, class Function> inline
-bool endsWith(const S& str, const T& postfix, Function cmpStringFun)
-{
- const size_t strLen = strLength(str);
- const size_t pfLen = strLength(postfix);
- if (strLen < pfLen)
- return false;
-
- return cmpStringFun(strBegin(str) + strLen - pfLen, pfLen,
- strBegin(postfix), pfLen) == 0;
-}
-
-
-template <class S, class T, class Function> inline
-bool strEqual(const S& lhs, const T& rhs, Function cmpStringFun)
-{
- return cmpStringFun(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0;
-}
-
-
-template <class S, class T> inline bool startsWith(const S& str, const T& prefix ) { return startsWith(str, prefix, CmpBinary()); }
-template <class S, class T> inline bool endsWith (const S& str, const T& postfix) { return endsWith (str, postfix, CmpBinary()); }
-template <class S, class T> inline bool strEqual (const S& lhs, const T& rhs ) { return strEqual (lhs, rhs, CmpBinary()); }
-
-
-template <class S, class T> inline
-bool contains(const S& str, const T& term)
-{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- const size_t strLen = strLength(str);
- const size_t termLen = strLength(term);
- if (strLen < termLen)
- return false;
-
- const auto* const strFirst = strBegin(str);
- const auto* const strLast = strFirst + strLen;
- const auto* const termFirst = strBegin(term);
-
- return std::search(strFirst, strLast,
- termFirst, termFirst + termLen) != strLast;
-}
-
-
-template <class S, class T> inline
-S afterLast(const S& str, const T& term, FailureReturnVal rv)
-{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- const size_t termLen = strLength(term);
- assert(termLen > 0);
-
- const auto* const strFirst = strBegin(str);
- const auto* const strLast = strFirst + strLength(str);
- const auto* const termFirst = strBegin(term);
-
- const auto* it = search_last(strFirst, strLast,
- termFirst, termFirst + termLen);
- if (it == strLast)
- return rv == IF_MISSING_RETURN_ALL ? str : S();
-
- it += termLen;
- return S(it, strLast - it);
-}
-
-
-template <class S, class T> inline
-S beforeLast(const S& str, const T& term, FailureReturnVal rv)
-{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- const size_t termLen = strLength(term);
- assert(termLen > 0);
-
- const auto* const strFirst = strBegin(str);
- const auto* const strLast = strFirst + strLength(str);
- const auto* const termFirst = strBegin(term);
-
- const auto* it = search_last(strFirst, strLast,
- termFirst, termFirst + termLen);
- if (it == strLast)
- return rv == IF_MISSING_RETURN_ALL ? str : S();
-
- return S(strFirst, it - strFirst);
-}
-
-
-template <class S, class T> inline
-S afterFirst(const S& str, const T& term, FailureReturnVal rv)
-{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- const size_t termLen = strLength(term);
- assert(termLen > 0);
-
- const auto* const strFirst = strBegin(str);
- const auto* const strLast = strFirst + strLength(str);
- const auto* const termFirst = strBegin(term);
-
- const auto* it = std::search(strFirst, strLast,
- termFirst, termFirst + termLen);
- if (it == strLast)
- return rv == IF_MISSING_RETURN_ALL ? str : S();
-
- it += termLen;
- return S(it, strLast - it);
-}
-
-
-template <class S, class T> inline
-S beforeFirst(const S& str, const T& term, FailureReturnVal rv)
-{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- const size_t termLen = strLength(term);
- assert(termLen > 0);
-
- const auto* const strFirst = strBegin(str);
- const auto* const strLast = strFirst + strLength(str);
- const auto* const termFirst = strBegin(term);
-
- auto it = std::search(strFirst, strLast,
- termFirst, termFirst + termLen);
- if (it == strLast)
- return rv == IF_MISSING_RETURN_ALL ? str : S();
-
- return S(strFirst, it - strFirst);
-}
-
-
-template <class S, class T> inline
-std::vector<S> split(const S& str, const T& delimiter, SplitType st)
-{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- const size_t delimLen = strLength(delimiter);
- assert(delimLen > 0);
- if (delimLen == 0)
- {
- if (str.empty() && st == SplitType::SKIP_EMPTY)
- return {};
- return { str };
- }
-
- const auto* const delimFirst = strBegin(delimiter);
- const auto* const delimLast = delimFirst + delimLen;
-
- const auto* blockStart = strBegin(str);
- const auto* const strLast = blockStart + strLength(str);
-
- std::vector<S> output;
- for (;;)
- {
- const auto* const blockEnd = std::search(blockStart, strLast,
- delimFirst, delimLast);
- if (blockStart != blockEnd || st == SplitType::ALLOW_EMPTY)
- output.emplace_back(blockStart, blockEnd - blockStart);
-
- if (blockEnd == strLast)
- return output;
- blockStart = blockEnd + delimLen;
- }
-}
-
-
-namespace impl
-{
-ZEN_INIT_DETECT_MEMBER(append);
-
-//either call operator+=(S(str, len)) or append(str, len)
-template <class S, class InputIterator> inline
-typename EnableIf<HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); }
-
-template <class S, class InputIterator> inline
-typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); }
-}
-
-
-template <class S, class T, class U> inline
-S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
-{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, "");
- const size_t oldLen = strLength(oldTerm);
- if (oldLen == 0)
- return str;
-
- const auto* const oldBegin = strBegin(oldTerm);
- const auto* const oldEnd = oldBegin + oldLen;
-
- const auto* const newBegin = strBegin(newTerm);
- const auto* const newEnd = newBegin + strLength(newTerm);
-
- S output;
-
- for (auto it = str.begin();;)
- {
- const auto itFound = std::search(it, str.end(),
- oldBegin, oldEnd);
- if (itFound == str.end() && it == str.begin())
- return str; //optimize "oldTerm not found": return ref-counted copy
-
- impl::stringAppend(output, it, itFound);
- if (itFound == str.end())
- return output;
-
- impl::stringAppend(output, newBegin, newEnd);
- it = itFound + oldLen;
-
- if (!replaceAll)
- {
- impl::stringAppend(output, it, str.end());
- return output;
- }
- }
-}
-
-
-template <class S, class T, class U> inline
-void replace(S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
-{
- str = replaceCpy(str, oldTerm, newTerm, replaceAll);
-}
-
-
-template <class S, class Function> inline
-void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar)
-{
- assert(fromLeft || fromRight);
-
- const auto* const oldBegin = strBegin(str);
- const auto* newBegin = oldBegin;
- const auto* newEnd = oldBegin + strLength(str);
-
- if (fromRight)
- while (newBegin != newEnd && trimThisChar(newEnd[-1]))
- --newEnd;
-
- if (fromLeft)
- while (newBegin != newEnd && trimThisChar(*newBegin))
- ++newBegin;
-
- if (newBegin != oldBegin)
- str = S(newBegin, newEnd - newBegin); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only
- else
- str.resize(newEnd - newBegin);
-}
-
-
-template <class S> inline
-void trim(S& str, bool fromLeft, bool fromRight)
-{
- using CharType = typename GetCharType<S>::Type;
- trim(str, fromLeft, fromRight, [](CharType c) { return isWhiteSpace(c); });
-}
-
-
-template <class S> inline
-S trimCpy(S str, bool fromLeft, bool fromRight)
-{
- //implementing trimCpy() in terms of trim(), instead of the other way round, avoids memory allocations when trimming from right!
- trim(str, fromLeft, fromRight);
- return std::move(str); //"str" is an l-value parameter => no copy elision!
-}
-
-
-namespace impl
-{
-template <class S, class T>
-struct CopyStringToString
-{
- T copy(const S& src) const { return T(strBegin(src), strLength(src)); }
-};
-
-template <class T>
-struct CopyStringToString<T, T> //perf: we don't need a deep copy if string types match
-{
- template <class S>
- T copy(S&& str) const { return std::forward<S>(str); }
-};
-
-inline int strcmpWithNulls(const char* ptr1, const char* ptr2, size_t num) { return std::memcmp (ptr1, ptr2, num); }
-inline int strcmpWithNulls(const wchar_t* ptr1, const wchar_t* ptr2, size_t num) { return std::wmemcmp(ptr1, ptr2, num); }
-}
-
-template <class T, class S> inline
-T copyStringTo(S&& str) { return impl::CopyStringToString<std::decay_t<S>, T>().copy(std::forward<S>(str)); }
-
-
-template <class Char> inline
-int CmpBinary::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const
-{
- //support embedded 0, unlike strncmp/wcsncmp!
- const int rv = impl::strcmpWithNulls(lhs, rhs, std::min(lhsLen, rhsLen));
- if (rv != 0)
- return rv;
- return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
-}
-
-
-template <class Char> inline
-int CmpAsciiNoCase::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const
-{
- auto asciiToLower = [](Char c) //ordering: lower-case chars have higher code points than uppper-case
- {
- if (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z'))
- return static_cast<Char>(c - static_cast<Char>('A') + static_cast<Char>('a'));
- return c;
- };
-
- const auto* const lhsLast = lhs + std::min(lhsLen, rhsLen);
-
- while (lhs != lhsLast)
- {
- const Char charL = asciiToLower(*lhs++);
- const Char charR = asciiToLower(*rhs++);
- if (charL != charR)
- return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention!
- //unsigned underflow is well-defined!
- }
- return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
-}
-
-
-namespace impl
-{
-template <class Num> inline
-int saferPrintf(char* buffer, size_t bufferSize, const char* format, const Num& number) //there is no such thing as a "safe" printf ;)
-{
- return std::snprintf(buffer, bufferSize, format, number); //C99: returns number of chars written if successful, < 0 or >= bufferSize on failure
-}
-
-template <class Num> inline
-int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const Num& number)
-{
- return std::swprintf(buffer, bufferSize, format, number); //C99: returns number of chars written if successful, < 0 on failure (including buffer too small)
-}
-}
-
-template <class S, class T, class Num> inline
-S printNumber(const T& format, const Num& number) //format a single number using ::sprintf
-{
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- using CharType = typename GetCharType<S>::Type;
-
- const int BUFFER_SIZE = 128;
- CharType buffer[BUFFER_SIZE]; //zero-initialize?
- const int charsWritten = impl::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number);
-
- return 0 < charsWritten && charsWritten < BUFFER_SIZE ? S(buffer, charsWritten) : S();
-}
-
-
-namespace impl
-{
-enum NumberType
-{
- NUM_TYPE_SIGNED_INT,
- NUM_TYPE_UNSIGNED_INT,
- NUM_TYPE_FLOATING_POINT,
- NUM_TYPE_OTHER,
-};
-
-
-template <class S, class Num> inline
-S numberTo(const Num& number, Int2Type<NUM_TYPE_OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
-{
- using CharType = typename GetCharType<S>::Type;
-
- std::basic_ostringstream<CharType> ss;
- ss << number;
- return copyStringTo<S>(ss.str());
-}
-
-
-template <class S, class Num> inline S floatToString(const Num& number, char ) { return printNumber<S>( "%g", static_cast<double>(number)); }
-template <class S, class Num> inline S floatToString(const Num& number, wchar_t) { return printNumber<S>(L"%g", static_cast<double>(number)); }
-
-template <class S, class Num> inline
-S numberTo(const Num& number, Int2Type<NUM_TYPE_FLOATING_POINT>)
-{
- return floatToString<S>(number, typename GetCharType<S>::Type());
-}
-
-
-/*
-perf: integer to string: (executed 10 mio. times)
- std::stringstream - 14796 ms
- std::sprintf - 3086 ms
- formatInteger - 778 ms
-*/
-
-template <class OutputIterator, class Num> inline
-void formatNegativeInteger(Num n, OutputIterator& it)
-{
- assert(n < 0);
- using CharType = typename std::iterator_traits<OutputIterator>::value_type;
- do
- {
- const Num tmp = n / 10;
- *--it = static_cast<CharType>('0' + (tmp * 10 - n)); //8% faster than using modulus operator!
- n = tmp;
- }
- while (n != 0);
-
- *--it = static_cast<CharType>('-');
-}
-
-template <class OutputIterator, class Num> inline
-void formatPositiveInteger(Num n, OutputIterator& it)
-{
- assert(n >= 0);
- using CharType = typename std::iterator_traits<OutputIterator>::value_type;
- do
- {
- const Num tmp = n / 10;
- *--it = static_cast<CharType>('0' + (n - tmp * 10)); //8% faster than using modulus operator!
- n = tmp;
- }
- while (n != 0);
-}
-
-
-template <class S, class Num> inline
-S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>)
-{
- using CharType = typename GetCharType<S>::Type;
- CharType buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize?
- //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency
- //required chars (+ sign char): 1 + ceil(ln_10(256^sizeof(n) / 2 + 1)) -> divide by 2 for signed half-range; second +1 since one half starts with 1!
- // <= 1 + ceil(ln_10(256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.41)
-
- //caveat: consider INT_MIN: technically -INT_MIN == INT_MIN
- auto it = std::end(buffer);
- if (number < 0)
- formatNegativeInteger(number, it);
- else
- formatPositiveInteger(number, it);
- assert(it >= std::begin(buffer));
-
- return S(&*it, std::end(buffer) - it);
-}
-
-
-template <class S, class Num> inline
-S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>)
-{
- using CharType = typename GetCharType<S>::Type;
- CharType buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize?
- //required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41)
-
- auto it = std::end(buffer);
- formatPositiveInteger(number, it);
- assert(it >= std::begin(buffer));
-
- return S(&*it, std::end(buffer) - it);
-}
-
-//--------------------------------------------------------------------------------
-
-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
-{
- using CharType = typename GetCharType<S>::Type;
- Num number = 0;
- std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType>>(str)) >> number;
- return number;
-}
-
-
-template <class Num> inline Num stringToFloat(const char* str) { return std::strtod(str, nullptr); }
-template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, nullptr); }
-
-template <class Num, class S> inline
-Num stringTo(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>)
-{
- return stringToFloat<Num>(strBegin(str));
-}
-
-template <class Num, class S>
-Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
-{
- using CharType = typename GetCharType<S>::Type;
-
- const CharType* first = strBegin(str);
- const CharType* last = first + strLength(str);
-
- while (first != last && isWhiteSpace(*first)) //skip leading whitespace
- ++first;
-
- //handle minus sign
- hasMinusSign = false;
- if (first != last)
- {
- if (*first == static_cast<CharType>('-'))
- {
- hasMinusSign = true;
- ++first;
- }
- else if (*first == static_cast<CharType>('+'))
- ++first;
- }
-
- Num number = 0;
- for (const CharType* it = first; it != last; ++it)
- {
- const CharType c = *it;
- if (static_cast<CharType>('0') <= c && c <= static_cast<CharType>('9'))
- {
- number *= 10;
- number += c - static_cast<CharType>('0');
- }
- else //rest of string should contain whitespace only, it's NOT a bug if there is something else!
- break; //assert(std::all_of(iter, last, &isWhiteSpace<CharType>)); -> this is NO assert situation
- }
- return number;
-}
-
-
-template <class Num, class S> inline
-Num stringTo(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>)
-{
- bool hasMinusSign = false; //handle minus sign
- const Num number = extractInteger<Num>(str, hasMinusSign);
- return hasMinusSign ? -number : number;
-}
-
-
-template <class Num, class S> inline
-Num stringTo(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
-{
- bool hasMinusSign = false; //handle minus sign
- const Num number = extractInteger<Num>(str, hasMinusSign);
- if (hasMinusSign)
- {
- assert(false);
- return 0U;
- }
- return number;
-}
-}
-
-
-template <class S, class Num> inline
-S numberTo(const Num& number)
-{
- using TypeTag = Int2Type<
- IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT :
- IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT :
- IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT :
- impl::NUM_TYPE_OTHER>;
-
- return impl::numberTo<S>(number, TypeTag());
-}
-
-
-template <class Num, class S> inline
-Num stringTo(const S& str)
-{
- using TypeTag = Int2Type<
- IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT :
- IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT :
- IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT :
- impl::NUM_TYPE_OTHER>;
-
- return impl::stringTo<Num>(str, TypeTag());
-}
-
-
-inline //hexify beats "printNumber<std::string>("%02X", c)" by a nice factor of 3!
-std::pair<char, char> hexify(unsigned char c, bool upperCase)
-{
- auto hexifyDigit = [upperCase](int num) -> char //input [0, 15], output 0-9, A-F
- {
- assert(0 <= num&& num <= 15); //guaranteed by design below!
- if (num <= 9)
- return static_cast<char>('0' + num); //no signed/unsigned char problem here!
-
- if (upperCase)
- return static_cast<char>('A' + (num - 10));
- else
- return static_cast<char>('a' + (num - 10));
- };
- return std::make_pair(hexifyDigit(c / 16), hexifyDigit(c % 16));
-}
-
-
-inline //unhexify beats "::sscanf(&it[3], "%02X", &tmp)" by a factor of 3000 for ~250000 calls!!!
-char unhexify(char high, char low)
-{
- auto unhexifyDigit = [](char hex) -> int //input 0-9, a-f, A-F; output range: [0, 15]
- {
- if ('0' <= hex && hex <= '9') //no signed/unsigned char problem here!
- return hex - '0';
- else if ('A' <= hex && hex <= 'F')
- return (hex - 'A') + 10;
- else if ('a' <= hex && hex <= 'f')
- return (hex - 'a') + 10;
- assert(false);
- return 0;
- };
- return static_cast<unsigned char>(16 * unhexifyDigit(high) + unhexifyDigit(low)); //[!] convert to unsigned char first, then to char (which may be signed)
-}
-}
-
-#endif //STRING_TOOLS_H_213458973046
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef STRING_TOOLS_H_213458973046
+#define STRING_TOOLS_H_213458973046
+
+#include <cctype> //isspace
+#include <cwctype> //iswspace
+#include <cstdio> //sprintf
+#include <cwchar> //swprintf
+#include <algorithm>
+#include <cassert>
+#include <vector>
+#include <sstream>
+#include "stl_tools.h"
+#include "string_traits.h"
+
+
+//enhance arbitray string class with useful non-member functions:
+namespace zen
+{
+template <class Char> bool isWhiteSpace(Char ch);
+template <class Char> bool isDigit (Char ch); //not exactly the same as "std::isdigit" -> we consider '0'-'9' only!
+template <class Char> bool isHexDigit (Char ch);
+template <class Char> bool isAsciiAlpha(Char ch);
+
+//case-sensitive comparison (compile-time correctness: use different number of arguments as STL comparison predicates!)
+struct CmpBinary { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; };
+
+//basic case-insensitive comparison (considering A-Z only!)
+struct CmpAsciiNoCase { template <class Char> int operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const; };
+
+struct LessAsciiNoCase
+{
+ template <class S> //don't support heterogenous input! => use as container predicate only!
+ bool operator()(const S& lhs, const S& rhs) const { return CmpAsciiNoCase()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
+};
+
+//both S and T can be strings or char/wchar_t arrays or simple char/wchar_t
+template <class S, class T> bool contains(const S& str, const T& term);
+
+template <class S, class T> bool startsWith(const S& str, const T& prefix);
+template <class S, class T, class Function> bool startsWith(const S& str, const T& prefix, Function cmpStringFun);
+
+template <class S, class T> bool endsWith (const S& str, const T& postfix);
+template <class S, class T, class Function> bool endsWith (const S& str, const T& postfix, Function cmpStringFun);
+
+template <class S, class T> bool strEqual(const S& lhs, const T& rhs);
+template <class S, class T, class Function> bool strEqual(const S& lhs, const T& rhs, Function cmpStringFun);
+
+enum FailureReturnVal
+{
+ IF_MISSING_RETURN_ALL,
+ IF_MISSING_RETURN_NONE
+};
+
+template <class S, class T> S afterLast (const S& str, const T& term, FailureReturnVal rv);
+template <class S, class T> S beforeLast (const S& str, const T& term, FailureReturnVal rv);
+template <class S, class T> S afterFirst (const S& str, const T& term, FailureReturnVal rv);
+template <class S, class T> S beforeFirst(const S& str, const T& term, FailureReturnVal rv);
+
+enum class SplitType
+{
+ ALLOW_EMPTY,
+ SKIP_EMPTY
+};
+template <class S, class T> std::vector<S> split(const S& str, const T& delimiter, SplitType st);
+
+template <class S> S trimCpy(S str, bool fromLeft = true, bool fromRight = true);
+template <class S> void trim (S& str, bool fromLeft = true, bool fromRight = true);
+template <class S, class Function> void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar);
+
+template <class S, class T, class U> void replace ( S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true);
+template <class S, class T, class U> S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll = true);
+
+//high-performance conversion between numbers and strings
+template <class S, class Num> S numberTo(const Num& number);
+template <class Num, class S> Num stringTo(const S& str);
+
+std::pair<char, char> hexify (unsigned char c, bool upperCase = true);
+char unhexify(char high, char low);
+
+template <class S, class T, class Num> S printNumber(const T& format, const Num& number); //format a single number using std::snprintf()
+
+//string to string conversion: converts string-like type into char-compatible target string class
+template <class T, class S> T copyStringTo(S&& str);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//---------------------- implementation ----------------------
+template <> inline
+bool isWhiteSpace(char ch)
+{
+ assert(ch != 0); //std C++ does not consider 0 as white space
+ //caveat 1: std::isspace() takes an int, but expects an unsigned char
+ //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
+ return static_cast<unsigned char>(ch) < 128 &&
+ std::isspace(static_cast<unsigned char>(ch)) != 0;
+}
+
+template <> inline
+bool isWhiteSpace(wchar_t ch)
+{
+ assert(ch != 0); //std C++ does not consider 0 as white space
+ return std::iswspace(ch) != 0;
+}
+
+
+template <class Char> inline
+bool isDigit(Char ch) //similar to implmenetation of std::isdigit()!
+{
+ static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
+ return static_cast<Char>('0') <= ch && ch <= static_cast<Char>('9');
+}
+
+
+template <class Char> inline
+bool isHexDigit(Char c)
+{
+ static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
+ return (static_cast<Char>('0') <= c && c <= static_cast<Char>('9')) ||
+ (static_cast<Char>('A') <= c && c <= static_cast<Char>('F')) ||
+ (static_cast<Char>('a') <= c && c <= static_cast<Char>('f'));
+}
+
+
+template <class Char> inline
+bool isAsciiAlpha(Char c)
+{
+ static_assert(IsSameType<Char, char>::value || IsSameType<Char, wchar_t>::value, "");
+ return (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z')) ||
+ (static_cast<Char>('a') <= c && c <= static_cast<Char>('z'));
+}
+
+
+template <class S, class T, class Function> inline
+bool startsWith(const S& str, const T& prefix, Function cmpStringFun)
+{
+ const size_t pfLen = strLength(prefix);
+ if (strLength(str) < pfLen)
+ return false;
+
+ return cmpStringFun(strBegin(str), pfLen,
+ strBegin(prefix), pfLen) == 0;
+}
+
+
+template <class S, class T, class Function> inline
+bool endsWith(const S& str, const T& postfix, Function cmpStringFun)
+{
+ const size_t strLen = strLength(str);
+ const size_t pfLen = strLength(postfix);
+ if (strLen < pfLen)
+ return false;
+
+ return cmpStringFun(strBegin(str) + strLen - pfLen, pfLen,
+ strBegin(postfix), pfLen) == 0;
+}
+
+
+template <class S, class T, class Function> inline
+bool strEqual(const S& lhs, const T& rhs, Function cmpStringFun)
+{
+ return cmpStringFun(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) == 0;
+}
+
+
+template <class S, class T> inline bool startsWith(const S& str, const T& prefix ) { return startsWith(str, prefix, CmpBinary()); }
+template <class S, class T> inline bool endsWith (const S& str, const T& postfix) { return endsWith (str, postfix, CmpBinary()); }
+template <class S, class T> inline bool strEqual (const S& lhs, const T& rhs ) { return strEqual (lhs, rhs, CmpBinary()); }
+
+
+template <class S, class T> inline
+bool contains(const S& str, const T& term)
+{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ const size_t strLen = strLength(str);
+ const size_t termLen = strLength(term);
+ if (strLen < termLen)
+ return false;
+
+ const auto* const strFirst = strBegin(str);
+ const auto* const strLast = strFirst + strLen;
+ const auto* const termFirst = strBegin(term);
+
+ return std::search(strFirst, strLast,
+ termFirst, termFirst + termLen) != strLast;
+}
+
+
+template <class S, class T> inline
+S afterLast(const S& str, const T& term, FailureReturnVal rv)
+{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ const size_t termLen = strLength(term);
+ assert(termLen > 0);
+
+ const auto* const strFirst = strBegin(str);
+ const auto* const strLast = strFirst + strLength(str);
+ const auto* const termFirst = strBegin(term);
+
+ const auto* it = search_last(strFirst, strLast,
+ termFirst, termFirst + termLen);
+ if (it == strLast)
+ return rv == IF_MISSING_RETURN_ALL ? str : S();
+
+ it += termLen;
+ return S(it, strLast - it);
+}
+
+
+template <class S, class T> inline
+S beforeLast(const S& str, const T& term, FailureReturnVal rv)
+{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ const size_t termLen = strLength(term);
+ assert(termLen > 0);
+
+ const auto* const strFirst = strBegin(str);
+ const auto* const strLast = strFirst + strLength(str);
+ const auto* const termFirst = strBegin(term);
+
+ const auto* it = search_last(strFirst, strLast,
+ termFirst, termFirst + termLen);
+ if (it == strLast)
+ return rv == IF_MISSING_RETURN_ALL ? str : S();
+
+ return S(strFirst, it - strFirst);
+}
+
+
+template <class S, class T> inline
+S afterFirst(const S& str, const T& term, FailureReturnVal rv)
+{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ const size_t termLen = strLength(term);
+ assert(termLen > 0);
+
+ const auto* const strFirst = strBegin(str);
+ const auto* const strLast = strFirst + strLength(str);
+ const auto* const termFirst = strBegin(term);
+
+ const auto* it = std::search(strFirst, strLast,
+ termFirst, termFirst + termLen);
+ if (it == strLast)
+ return rv == IF_MISSING_RETURN_ALL ? str : S();
+
+ it += termLen;
+ return S(it, strLast - it);
+}
+
+
+template <class S, class T> inline
+S beforeFirst(const S& str, const T& term, FailureReturnVal rv)
+{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ const size_t termLen = strLength(term);
+ assert(termLen > 0);
+
+ const auto* const strFirst = strBegin(str);
+ const auto* const strLast = strFirst + strLength(str);
+ const auto* const termFirst = strBegin(term);
+
+ auto it = std::search(strFirst, strLast,
+ termFirst, termFirst + termLen);
+ if (it == strLast)
+ return rv == IF_MISSING_RETURN_ALL ? str : S();
+
+ return S(strFirst, it - strFirst);
+}
+
+
+template <class S, class T> inline
+std::vector<S> split(const S& str, const T& delimiter, SplitType st)
+{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ const size_t delimLen = strLength(delimiter);
+ assert(delimLen > 0);
+ if (delimLen == 0)
+ {
+ if (str.empty() && st == SplitType::SKIP_EMPTY)
+ return {};
+ return { str };
+ }
+
+ const auto* const delimFirst = strBegin(delimiter);
+ const auto* const delimLast = delimFirst + delimLen;
+
+ const auto* blockStart = strBegin(str);
+ const auto* const strLast = blockStart + strLength(str);
+
+ std::vector<S> output;
+ for (;;)
+ {
+ const auto* const blockEnd = std::search(blockStart, strLast,
+ delimFirst, delimLast);
+ if (blockStart != blockEnd || st == SplitType::ALLOW_EMPTY)
+ output.emplace_back(blockStart, blockEnd - blockStart);
+
+ if (blockEnd == strLast)
+ return output;
+ blockStart = blockEnd + delimLen;
+ }
+}
+
+
+namespace impl
+{
+ZEN_INIT_DETECT_MEMBER(append);
+
+//either call operator+=(S(str, len)) or append(str, len)
+template <class S, class InputIterator> inline
+typename EnableIf<HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str.append(first, last); }
+
+template <class S, class InputIterator> inline
+typename EnableIf<!HasMember_append<S>::value>::Type stringAppend(S& str, InputIterator first, InputIterator last) { str += S(first, last); }
+}
+
+
+template <class S, class T, class U> inline
+S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
+{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, "");
+ const size_t oldLen = strLength(oldTerm);
+ if (oldLen == 0)
+ return str;
+
+ const auto* const oldBegin = strBegin(oldTerm);
+ const auto* const oldEnd = oldBegin + oldLen;
+
+ const auto* const newBegin = strBegin(newTerm);
+ const auto* const newEnd = newBegin + strLength(newTerm);
+
+ S output;
+
+ for (auto it = str.begin();;)
+ {
+ const auto itFound = std::search(it, str.end(),
+ oldBegin, oldEnd);
+ if (itFound == str.end() && it == str.begin())
+ return str; //optimize "oldTerm not found": return ref-counted copy
+
+ impl::stringAppend(output, it, itFound);
+ if (itFound == str.end())
+ return output;
+
+ impl::stringAppend(output, newBegin, newEnd);
+ it = itFound + oldLen;
+
+ if (!replaceAll)
+ {
+ impl::stringAppend(output, it, str.end());
+ return output;
+ }
+ }
+}
+
+
+template <class S, class T, class U> inline
+void replace(S& str, const T& oldTerm, const U& newTerm, bool replaceAll)
+{
+ str = replaceCpy(str, oldTerm, newTerm, replaceAll);
+}
+
+
+template <class S, class Function> inline
+void trim(S& str, bool fromLeft, bool fromRight, Function trimThisChar)
+{
+ assert(fromLeft || fromRight);
+
+ const auto* const oldBegin = strBegin(str);
+ const auto* newBegin = oldBegin;
+ const auto* newEnd = oldBegin + strLength(str);
+
+ if (fromRight)
+ while (newBegin != newEnd && trimThisChar(newEnd[-1]))
+ --newEnd;
+
+ if (fromLeft)
+ while (newBegin != newEnd && trimThisChar(*newBegin))
+ ++newBegin;
+
+ if (newBegin != oldBegin)
+ str = S(newBegin, newEnd - newBegin); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only
+ else
+ str.resize(newEnd - newBegin);
+}
+
+
+template <class S> inline
+void trim(S& str, bool fromLeft, bool fromRight)
+{
+ using CharType = typename GetCharType<S>::Type;
+ trim(str, fromLeft, fromRight, [](CharType c) { return isWhiteSpace(c); });
+}
+
+
+template <class S> inline
+S trimCpy(S str, bool fromLeft, bool fromRight)
+{
+ //implementing trimCpy() in terms of trim(), instead of the other way round, avoids memory allocations when trimming from right!
+ trim(str, fromLeft, fromRight);
+ return std::move(str); //"str" is an l-value parameter => no copy elision!
+}
+
+
+namespace impl
+{
+template <class S, class T>
+struct CopyStringToString
+{
+ T copy(const S& src) const { return T(strBegin(src), strLength(src)); }
+};
+
+template <class T>
+struct CopyStringToString<T, T> //perf: we don't need a deep copy if string types match
+{
+ template <class S>
+ T copy(S&& str) const { return std::forward<S>(str); }
+};
+
+inline int strcmpWithNulls(const char* ptr1, const char* ptr2, size_t num) { return std::memcmp (ptr1, ptr2, num); }
+inline int strcmpWithNulls(const wchar_t* ptr1, const wchar_t* ptr2, size_t num) { return std::wmemcmp(ptr1, ptr2, num); }
+}
+
+template <class T, class S> inline
+T copyStringTo(S&& str) { return impl::CopyStringToString<std::decay_t<S>, T>().copy(std::forward<S>(str)); }
+
+
+template <class Char> inline
+int CmpBinary::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const
+{
+ //support embedded 0, unlike strncmp/wcsncmp!
+ const int rv = impl::strcmpWithNulls(lhs, rhs, std::min(lhsLen, rhsLen));
+ if (rv != 0)
+ return rv;
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
+
+
+template <class Char> inline
+int CmpAsciiNoCase::operator()(const Char* lhs, size_t lhsLen, const Char* rhs, size_t rhsLen) const
+{
+ auto asciiToLower = [](Char c) //ordering: lower-case chars have higher code points than uppper-case
+ {
+ if (static_cast<Char>('A') <= c && c <= static_cast<Char>('Z'))
+ return static_cast<Char>(c - static_cast<Char>('A') + static_cast<Char>('a'));
+ return c;
+ };
+
+ const auto* const lhsLast = lhs + std::min(lhsLen, rhsLen);
+
+ while (lhs != lhsLast)
+ {
+ const Char charL = asciiToLower(*lhs++);
+ const Char charR = asciiToLower(*rhs++);
+ if (charL != charR)
+ return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention!
+ //unsigned underflow is well-defined!
+ }
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
+
+
+namespace impl
+{
+template <class Num> inline
+int saferPrintf(char* buffer, size_t bufferSize, const char* format, const Num& number) //there is no such thing as a "safe" printf ;)
+{
+ return std::snprintf(buffer, bufferSize, format, number); //C99: returns number of chars written if successful, < 0 or >= bufferSize on failure
+}
+
+template <class Num> inline
+int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const Num& number)
+{
+ return std::swprintf(buffer, bufferSize, format, number); //C99: returns number of chars written if successful, < 0 on failure (including buffer too small)
+}
+}
+
+template <class S, class T, class Num> inline
+S printNumber(const T& format, const Num& number) //format a single number using ::sprintf
+{
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ using CharType = typename GetCharType<S>::Type;
+
+ const int BUFFER_SIZE = 128;
+ CharType buffer[BUFFER_SIZE]; //zero-initialize?
+ const int charsWritten = impl::saferPrintf(buffer, BUFFER_SIZE, strBegin(format), number);
+
+ return 0 < charsWritten && charsWritten < BUFFER_SIZE ? S(buffer, charsWritten) : S();
+}
+
+
+namespace impl
+{
+enum NumberType
+{
+ NUM_TYPE_SIGNED_INT,
+ NUM_TYPE_UNSIGNED_INT,
+ NUM_TYPE_FLOATING_POINT,
+ NUM_TYPE_OTHER,
+};
+
+
+template <class S, class Num> inline
+S numberTo(const Num& number, Int2Type<NUM_TYPE_OTHER>) //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
+{
+ using CharType = typename GetCharType<S>::Type;
+
+ std::basic_ostringstream<CharType> ss;
+ ss << number;
+ return copyStringTo<S>(ss.str());
+}
+
+
+template <class S, class Num> inline S floatToString(const Num& number, char ) { return printNumber<S>( "%g", static_cast<double>(number)); }
+template <class S, class Num> inline S floatToString(const Num& number, wchar_t) { return printNumber<S>(L"%g", static_cast<double>(number)); }
+
+template <class S, class Num> inline
+S numberTo(const Num& number, Int2Type<NUM_TYPE_FLOATING_POINT>)
+{
+ return floatToString<S>(number, typename GetCharType<S>::Type());
+}
+
+
+/*
+perf: integer to string: (executed 10 mio. times)
+ std::stringstream - 14796 ms
+ std::sprintf - 3086 ms
+ formatInteger - 778 ms
+*/
+
+template <class OutputIterator, class Num> inline
+void formatNegativeInteger(Num n, OutputIterator& it)
+{
+ assert(n < 0);
+ using CharType = typename std::iterator_traits<OutputIterator>::value_type;
+ do
+ {
+ const Num tmp = n / 10;
+ *--it = static_cast<CharType>('0' + (tmp * 10 - n)); //8% faster than using modulus operator!
+ n = tmp;
+ }
+ while (n != 0);
+
+ *--it = static_cast<CharType>('-');
+}
+
+template <class OutputIterator, class Num> inline
+void formatPositiveInteger(Num n, OutputIterator& it)
+{
+ assert(n >= 0);
+ using CharType = typename std::iterator_traits<OutputIterator>::value_type;
+ do
+ {
+ const Num tmp = n / 10;
+ *--it = static_cast<CharType>('0' + (n - tmp * 10)); //8% faster than using modulus operator!
+ n = tmp;
+ }
+ while (n != 0);
+}
+
+
+template <class S, class Num> inline
+S numberTo(const Num& number, Int2Type<NUM_TYPE_SIGNED_INT>)
+{
+ using CharType = typename GetCharType<S>::Type;
+ CharType buffer[2 + sizeof(Num) * 241 / 100]; //zero-initialize?
+ //it's generally faster to use a buffer than to rely on String::operator+=() (in)efficiency
+ //required chars (+ sign char): 1 + ceil(ln_10(256^sizeof(n) / 2 + 1)) -> divide by 2 for signed half-range; second +1 since one half starts with 1!
+ // <= 1 + ceil(ln_10(256^sizeof(n))) =~ 1 + ceil(sizeof(n) * 2.4082) <= 2 + floor(sizeof(n) * 2.41)
+
+ //caveat: consider INT_MIN: technically -INT_MIN == INT_MIN
+ auto it = std::end(buffer);
+ if (number < 0)
+ formatNegativeInteger(number, it);
+ else
+ formatPositiveInteger(number, it);
+ assert(it >= std::begin(buffer));
+
+ return S(&*it, std::end(buffer) - it);
+}
+
+
+template <class S, class Num> inline
+S numberTo(const Num& number, Int2Type<NUM_TYPE_UNSIGNED_INT>)
+{
+ using CharType = typename GetCharType<S>::Type;
+ CharType buffer[1 + sizeof(Num) * 241 / 100]; //zero-initialize?
+ //required chars: ceil(ln_10(256^sizeof(n))) =~ ceil(sizeof(n) * 2.4082) <= 1 + floor(sizeof(n) * 2.41)
+
+ auto it = std::end(buffer);
+ formatPositiveInteger(number, it);
+ assert(it >= std::begin(buffer));
+
+ return S(&*it, std::end(buffer) - it);
+}
+
+//--------------------------------------------------------------------------------
+
+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
+{
+ using CharType = typename GetCharType<S>::Type;
+ Num number = 0;
+ std::basic_istringstream<CharType>(copyStringTo<std::basic_string<CharType>>(str)) >> number;
+ return number;
+}
+
+
+template <class Num> inline Num stringToFloat(const char* str) { return std::strtod(str, nullptr); }
+template <class Num> inline Num stringToFloat(const wchar_t* str) { return std::wcstod(str, nullptr); }
+
+template <class Num, class S> inline
+Num stringTo(const S& str, Int2Type<NUM_TYPE_FLOATING_POINT>)
+{
+ return stringToFloat<Num>(strBegin(str));
+}
+
+template <class Num, class S>
+Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
+{
+ using CharType = typename GetCharType<S>::Type;
+
+ const CharType* first = strBegin(str);
+ const CharType* last = first + strLength(str);
+
+ while (first != last && isWhiteSpace(*first)) //skip leading whitespace
+ ++first;
+
+ //handle minus sign
+ hasMinusSign = false;
+ if (first != last)
+ {
+ if (*first == static_cast<CharType>('-'))
+ {
+ hasMinusSign = true;
+ ++first;
+ }
+ else if (*first == static_cast<CharType>('+'))
+ ++first;
+ }
+
+ Num number = 0;
+ for (const CharType* it = first; it != last; ++it)
+ {
+ const CharType c = *it;
+ if (static_cast<CharType>('0') <= c && c <= static_cast<CharType>('9'))
+ {
+ number *= 10;
+ number += c - static_cast<CharType>('0');
+ }
+ else //rest of string should contain whitespace only, it's NOT a bug if there is something else!
+ break; //assert(std::all_of(iter, last, &isWhiteSpace<CharType>)); -> this is NO assert situation
+ }
+ return number;
+}
+
+
+template <class Num, class S> inline
+Num stringTo(const S& str, Int2Type<NUM_TYPE_SIGNED_INT>)
+{
+ bool hasMinusSign = false; //handle minus sign
+ const Num number = extractInteger<Num>(str, hasMinusSign);
+ return hasMinusSign ? -number : number;
+}
+
+
+template <class Num, class S> inline
+Num stringTo(const S& str, Int2Type<NUM_TYPE_UNSIGNED_INT>) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
+{
+ bool hasMinusSign = false; //handle minus sign
+ const Num number = extractInteger<Num>(str, hasMinusSign);
+ if (hasMinusSign)
+ {
+ assert(false);
+ return 0U;
+ }
+ return number;
+}
+}
+
+
+template <class S, class Num> inline
+S numberTo(const Num& number)
+{
+ using TypeTag = Int2Type<
+ IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT :
+ IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT :
+ IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT :
+ impl::NUM_TYPE_OTHER>;
+
+ return impl::numberTo<S>(number, TypeTag());
+}
+
+
+template <class Num, class S> inline
+Num stringTo(const S& str)
+{
+ using TypeTag = Int2Type<
+ IsSignedInt <Num>::value ? impl::NUM_TYPE_SIGNED_INT :
+ IsUnsignedInt<Num>::value ? impl::NUM_TYPE_UNSIGNED_INT :
+ IsFloat <Num>::value ? impl::NUM_TYPE_FLOATING_POINT :
+ impl::NUM_TYPE_OTHER>;
+
+ return impl::stringTo<Num>(str, TypeTag());
+}
+
+
+inline //hexify beats "printNumber<std::string>("%02X", c)" by a nice factor of 3!
+std::pair<char, char> hexify(unsigned char c, bool upperCase)
+{
+ auto hexifyDigit = [upperCase](int num) -> char //input [0, 15], output 0-9, A-F
+ {
+ assert(0 <= num&& num <= 15); //guaranteed by design below!
+ if (num <= 9)
+ return static_cast<char>('0' + num); //no signed/unsigned char problem here!
+
+ if (upperCase)
+ return static_cast<char>('A' + (num - 10));
+ else
+ return static_cast<char>('a' + (num - 10));
+ };
+ return std::make_pair(hexifyDigit(c / 16), hexifyDigit(c % 16));
+}
+
+
+inline //unhexify beats "::sscanf(&it[3], "%02X", &tmp)" by a factor of 3000 for ~250000 calls!!!
+char unhexify(char high, char low)
+{
+ auto unhexifyDigit = [](char hex) -> int //input 0-9, a-f, A-F; output range: [0, 15]
+ {
+ if ('0' <= hex && hex <= '9') //no signed/unsigned char problem here!
+ return hex - '0';
+ else if ('A' <= hex && hex <= 'F')
+ return (hex - 'A') + 10;
+ else if ('a' <= hex && hex <= 'f')
+ return (hex - 'a') + 10;
+ assert(false);
+ return 0;
+ };
+ return static_cast<unsigned char>(16 * unhexifyDigit(high) + unhexifyDigit(low)); //[!] convert to unsigned char first, then to char (which may be signed)
+}
+}
+
+#endif //STRING_TOOLS_H_213458973046
diff --git a/zen/string_traits.h b/zen/string_traits.h
index 5ae5733c..f17e5e0d 100755
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -1,216 +1,216 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef STRING_TRAITS_H_813274321443234
-#define STRING_TRAITS_H_813274321443234
-
-#include <cstring> //strlen
-#include "type_tools.h"
-
-
-//uniform access to string-like types, both classes and character arrays
-namespace zen
-{
-/*
-IsStringLike<>::value:
- IsStringLike<const wchar_t*>::value; //equals "true"
- IsStringLike<const int*> ::value; //equals "false"
-
-GetCharType<>::Type:
- GetCharType<std::wstring>::Type //equals wchar_t
- GetCharType<wchar_t[5]> ::Type //equals wchar_t
-
-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
-*/
-
-//reference a sub-string for consumption by zen string_tools
-template <class Char>
-class StringRef
-{
-public:
- template <class Iterator>
- StringRef(Iterator first, Iterator last) : len_(last - first), str_(first != last ? &*first : nullptr) {}
- //StringRef(const Char* str, size_t len) : str_(str), len_(len) {} -> needless constraint! Char* not available for empty range!
-
- Char* data () const { return str_; } //1. no null-termination! 2. may be nullptr!
- size_t length() const { return len_; }
-
-private:
- const size_t len_;
- Char* str_;
-};
-
-
-
-
-
-
-
-
-
-
-
-
-//---------------------- implementation ----------------------
-namespace implementation
-{
-template<class S, class Char> //test if result of S::c_str() can convert to const Char*
-class HasConversion
-{
- using Yes = char[1];
- using No = char[2];
-
- static Yes& hasConversion(const Char*);
- static No& hasConversion(...);
-
-public:
- enum { value = sizeof(hasConversion(std::declval<S>().c_str())) == sizeof(Yes) };
-};
-
-
-template <class S, bool isStringClass> struct GetCharTypeImpl : ResultType<NullType> {};
-
-template <class S>
-struct GetCharTypeImpl<S, true> :
- ResultType<
- typename SelectIf<HasConversion<S, wchar_t>::value, wchar_t,
- typename SelectIf<HasConversion<S, char >::value, char, NullType>::Type
- >::Type>
-{
- //using Type = typename S::value_type;
- /*DON'T use S::value_type:
- 1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*"
- 2. wxString, wxWidgets v2.9, has some questionable string design: wxString::c_str() returns a proxy (wxCStrData) which
- is implicitly convertible to *both* "const char*" and "const wchar_t*" while wxString::value_type is a wrapper around an unsigned int
- */
-};
-
-template <> struct GetCharTypeImpl<char, false> : ResultType<char > {};
-template <> struct GetCharTypeImpl<wchar_t, false> : ResultType<wchar_t> {};
-
-template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {};
-template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> : ResultType<wchar_t> {};
-template <> struct GetCharTypeImpl<StringRef<const char >, false> : ResultType<char > {};
-template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> : ResultType<wchar_t> {};
-
-
-ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
-ZEN_INIT_DETECT_MEMBER(c_str); //we don't know the exact declaration of the member attribute and it may be in a base class!
-ZEN_INIT_DETECT_MEMBER(length); //
-
-template <class S>
-class StringTraits
-{
- using NonRefType = typename RemoveRef <S >::Type;
- using NonConstType = typename RemoveConst <NonRefType >::Type;
- using NonArrayType = typename RemoveArray <NonConstType>::Type;
- using NonPtrType = typename RemovePointer<NonArrayType>::Type;
- using UndecoratedType = typename RemoveConst <NonPtrType >::Type ; //handle "const char* const"
-
-public:
- enum
- {
- isStringClass = HasMemberType_value_type<NonConstType>::value &&
- HasMember_c_str <NonConstType>::value &&
- HasMember_length <NonConstType>::value
- };
-
- using CharType = typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type;
-
- enum
- {
- isStringLike = IsSameType<CharType, char>::value ||
- IsSameType<CharType, wchar_t>::value
- };
-};
-}
-
-template <class T>
-struct IsStringLike : StaticBool<implementation::StringTraits<T>::isStringLike> {};
-
-template <class T>
-struct GetCharType : ResultType<typename implementation::StringTraits<T>::CharType> {};
-
-
-namespace implementation
-{
-//strlen/wcslen are vectorized since VS14 CTP3
-inline size_t cStringLength(const char* str) { return std::strlen(str); }
-inline size_t cStringLength(const wchar_t* str) { return std::wcslen(str); }
-
-//no significant perf difference for "comparison" test case between cStringLength/wcslen:
-#if 0
-template <class C> inline
-size_t cStringLength(const C* str)
-{
- static_assert(IsSameType<C, char>::value || IsSameType<C, wchar_t>::value, "");
- size_t len = 0;
- while (*str++ != 0)
- ++len;
- return len;
-}
-#endif
-
-template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline
-const typename GetCharType<S>::Type* strBegin(const S& str) //SFINAE: T must be a "string"
-{
- return str.c_str();
-}
-
-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(); }
-inline const char* strBegin(const StringRef<const char >& ref) { return ref.data(); }
-inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); }
-
-
-template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline
-size_t strLength(const S& str) //SFINAE: T must be a "string"
-{
- return str.length();
-}
-
-inline size_t strLength(const char* str) { return cStringLength(str); }
-inline size_t strLength(const wchar_t* str) { return 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(); }
-inline size_t strLength(const StringRef<const char >& ref) { return ref.length(); }
-inline size_t strLength(const StringRef<const wchar_t>& ref) { return ref.length(); }
-}
-
-
-template <class S> inline
-auto strBegin(S&& str) -> const typename GetCharType<S>::Type*
-{
- static_assert(IsStringLike<S>::value, "");
- return implementation::strBegin(std::forward<S>(str));
-}
-
-
-template <class S> inline
-size_t strLength(S&& str)
-{
- static_assert(IsStringLike<S>::value, "");
- return implementation::strLength(std::forward<S>(str));
-}
-}
-
-#endif //STRING_TRAITS_H_813274321443234
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef STRING_TRAITS_H_813274321443234
+#define STRING_TRAITS_H_813274321443234
+
+#include <cstring> //strlen
+#include "type_tools.h"
+
+
+//uniform access to string-like types, both classes and character arrays
+namespace zen
+{
+/*
+IsStringLike<>::value:
+ IsStringLike<const wchar_t*>::value; //equals "true"
+ IsStringLike<const int*> ::value; //equals "false"
+
+GetCharType<>::Type:
+ GetCharType<std::wstring>::Type //equals wchar_t
+ GetCharType<wchar_t[5]> ::Type //equals wchar_t
+
+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
+*/
+
+//reference a sub-string for consumption by zen string_tools
+template <class Char>
+class StringRef
+{
+public:
+ template <class Iterator>
+ StringRef(Iterator first, Iterator last) : len_(last - first), str_(first != last ? &*first : nullptr) {}
+ //StringRef(const Char* str, size_t len) : str_(str), len_(len) {} -> needless constraint! Char* not available for empty range!
+
+ Char* data () const { return str_; } //1. no null-termination! 2. may be nullptr!
+ size_t length() const { return len_; }
+
+private:
+ const size_t len_;
+ Char* str_;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+//---------------------- implementation ----------------------
+namespace implementation
+{
+template<class S, class Char> //test if result of S::c_str() can convert to const Char*
+class HasConversion
+{
+ using Yes = char[1];
+ using No = char[2];
+
+ static Yes& hasConversion(const Char*);
+ static No& hasConversion(...);
+
+public:
+ enum { value = sizeof(hasConversion(std::declval<S>().c_str())) == sizeof(Yes) };
+};
+
+
+template <class S, bool isStringClass> struct GetCharTypeImpl : ResultType<NullType> {};
+
+template <class S>
+struct GetCharTypeImpl<S, true> :
+ ResultType<
+ typename SelectIf<HasConversion<S, wchar_t>::value, wchar_t,
+ typename SelectIf<HasConversion<S, char >::value, char, NullType>::Type
+ >::Type>
+{
+ //using Type = typename S::value_type;
+ /*DON'T use S::value_type:
+ 1. support Glib::ustring: value_type is "unsigned int" but c_str() returns "const char*"
+ 2. wxString, wxWidgets v2.9, has some questionable string design: wxString::c_str() returns a proxy (wxCStrData) which
+ is implicitly convertible to *both* "const char*" and "const wchar_t*" while wxString::value_type is a wrapper around an unsigned int
+ */
+};
+
+template <> struct GetCharTypeImpl<char, false> : ResultType<char > {};
+template <> struct GetCharTypeImpl<wchar_t, false> : ResultType<wchar_t> {};
+
+template <> struct GetCharTypeImpl<StringRef<char >, false> : ResultType<char > {};
+template <> struct GetCharTypeImpl<StringRef<wchar_t >, false> : ResultType<wchar_t> {};
+template <> struct GetCharTypeImpl<StringRef<const char >, false> : ResultType<char > {};
+template <> struct GetCharTypeImpl<StringRef<const wchar_t>, false> : ResultType<wchar_t> {};
+
+
+ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
+ZEN_INIT_DETECT_MEMBER(c_str); //we don't know the exact declaration of the member attribute and it may be in a base class!
+ZEN_INIT_DETECT_MEMBER(length); //
+
+template <class S>
+class StringTraits
+{
+ using NonRefType = typename RemoveRef <S >::Type;
+ using NonConstType = typename RemoveConst <NonRefType >::Type;
+ using NonArrayType = typename RemoveArray <NonConstType>::Type;
+ using NonPtrType = typename RemovePointer<NonArrayType>::Type;
+ using UndecoratedType = typename RemoveConst <NonPtrType >::Type ; //handle "const char* const"
+
+public:
+ enum
+ {
+ isStringClass = HasMemberType_value_type<NonConstType>::value &&
+ HasMember_c_str <NonConstType>::value &&
+ HasMember_length <NonConstType>::value
+ };
+
+ using CharType = typename GetCharTypeImpl<UndecoratedType, isStringClass>::Type;
+
+ enum
+ {
+ isStringLike = IsSameType<CharType, char>::value ||
+ IsSameType<CharType, wchar_t>::value
+ };
+};
+}
+
+template <class T>
+struct IsStringLike : StaticBool<implementation::StringTraits<T>::isStringLike> {};
+
+template <class T>
+struct GetCharType : ResultType<typename implementation::StringTraits<T>::CharType> {};
+
+
+namespace implementation
+{
+//strlen/wcslen are vectorized since VS14 CTP3
+inline size_t cStringLength(const char* str) { return std::strlen(str); }
+inline size_t cStringLength(const wchar_t* str) { return std::wcslen(str); }
+
+//no significant perf difference for "comparison" test case between cStringLength/wcslen:
+#if 0
+template <class C> inline
+size_t cStringLength(const C* str)
+{
+ static_assert(IsSameType<C, char>::value || IsSameType<C, wchar_t>::value, "");
+ size_t len = 0;
+ while (*str++ != 0)
+ ++len;
+ return len;
+}
+#endif
+
+template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline
+const typename GetCharType<S>::Type* strBegin(const S& str) //SFINAE: T must be a "string"
+{
+ return str.c_str();
+}
+
+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(); }
+inline const char* strBegin(const StringRef<const char >& ref) { return ref.data(); }
+inline const wchar_t* strBegin(const StringRef<const wchar_t>& ref) { return ref.data(); }
+
+
+template <class S, typename = typename EnableIf<implementation::StringTraits<S>::isStringClass>::Type> inline
+size_t strLength(const S& str) //SFINAE: T must be a "string"
+{
+ return str.length();
+}
+
+inline size_t strLength(const char* str) { return cStringLength(str); }
+inline size_t strLength(const wchar_t* str) { return 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(); }
+inline size_t strLength(const StringRef<const char >& ref) { return ref.length(); }
+inline size_t strLength(const StringRef<const wchar_t>& ref) { return ref.length(); }
+}
+
+
+template <class S> inline
+auto strBegin(S&& str) -> const typename GetCharType<S>::Type*
+{
+ static_assert(IsStringLike<S>::value, "");
+ return implementation::strBegin(std::forward<S>(str));
+}
+
+
+template <class S> inline
+size_t strLength(S&& str)
+{
+ static_assert(IsStringLike<S>::value, "");
+ return implementation::strLength(std::forward<S>(str));
+}
+}
+
+#endif //STRING_TRAITS_H_813274321443234
diff --git a/zen/symlink_target.h b/zen/symlink_target.h
index 4620e7cc..84b8452a 100755
--- a/zen/symlink_target.h
+++ b/zen/symlink_target.h
@@ -1,75 +1,75 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef SYMLINK_TARGET_H_801783470198357483
-#define SYMLINK_TARGET_H_801783470198357483
-
-#include "scope_guard.h"
-#include "file_error.h"
-
- #include <unistd.h>
- #include <stdlib.h> //realpath
-
-
-namespace zen
-{
-
-Zstring getResolvedSymlinkPath(const Zstring& linkPath); //throw FileError; Win: requires Vista or later!
-Zstring getSymlinkTargetRaw (const Zstring& linkPath); //throw FileError
-}
-
-
-
-
-
-
-
-
-
-//################################ implementation ################################
-
-namespace
-{
-//retrieve raw target data of symlink or junction
-Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileError
-{
- using namespace zen;
- const size_t BUFFER_SIZE = 10000;
- std::vector<char> buffer(BUFFER_SIZE);
-
- const ssize_t bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE);
- if (bytesWritten < 0)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink");
- if (bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation, not an error for readlink!
- throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink: buffer truncated.");
-
- return Zstring(&buffer[0], bytesWritten); //readlink does not append 0-termination!
-}
-
-
-Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError
-{
- using namespace zen;
- char* targetPath = ::realpath(linkPath.c_str(), nullptr);
- if (!targetPath)
- THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"realpath");
- ZEN_ON_SCOPE_EXIT(::free(targetPath));
- return targetPath;
-}
-}
-
-
-namespace zen
-{
-inline
-Zstring getSymlinkTargetRaw(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); }
-
-inline
-Zstring getResolvedSymlinkPath(const Zstring& linkPath) { return getResolvedSymlinkPath_impl(linkPath); }
-
-}
-
-#endif //SYMLINK_TARGET_H_801783470198357483
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef SYMLINK_TARGET_H_801783470198357483
+#define SYMLINK_TARGET_H_801783470198357483
+
+#include "scope_guard.h"
+#include "file_error.h"
+
+ #include <unistd.h>
+ #include <stdlib.h> //realpath
+
+
+namespace zen
+{
+
+Zstring getResolvedSymlinkPath(const Zstring& linkPath); //throw FileError; Win: requires Vista or later!
+Zstring getSymlinkTargetRaw (const Zstring& linkPath); //throw FileError
+}
+
+
+
+
+
+
+
+
+
+//################################ implementation ################################
+
+namespace
+{
+//retrieve raw target data of symlink or junction
+Zstring getSymlinkRawTargetString_impl(const Zstring& linkPath) //throw FileError
+{
+ using namespace zen;
+ const size_t BUFFER_SIZE = 10000;
+ std::vector<char> buffer(BUFFER_SIZE);
+
+ const ssize_t bytesWritten = ::readlink(linkPath.c_str(), &buffer[0], BUFFER_SIZE);
+ if (bytesWritten < 0)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink");
+ if (bytesWritten >= static_cast<ssize_t>(BUFFER_SIZE)) //detect truncation, not an error for readlink!
+ throw FileError(replaceCpy(_("Cannot resolve symbolic link %x."), L"%x", fmtPath(linkPath)), L"readlink: buffer truncated.");
+
+ return Zstring(&buffer[0], bytesWritten); //readlink does not append 0-termination!
+}
+
+
+Zstring getResolvedSymlinkPath_impl(const Zstring& linkPath) //throw FileError
+{
+ using namespace zen;
+ char* targetPath = ::realpath(linkPath.c_str(), nullptr);
+ if (!targetPath)
+ THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(linkPath)), L"realpath");
+ ZEN_ON_SCOPE_EXIT(::free(targetPath));
+ return targetPath;
+}
+}
+
+
+namespace zen
+{
+inline
+Zstring getSymlinkTargetRaw(const Zstring& linkPath) { return getSymlinkRawTargetString_impl(linkPath); }
+
+inline
+Zstring getResolvedSymlinkPath(const Zstring& linkPath) { return getResolvedSymlinkPath_impl(linkPath); }
+
+}
+
+#endif //SYMLINK_TARGET_H_801783470198357483
diff --git a/zen/sys_error.h b/zen/sys_error.h
index f7c128ef..026a3be5 100755
--- a/zen/sys_error.h
+++ b/zen/sys_error.h
@@ -1,105 +1,105 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef SYS_ERROR_H_3284791347018951324534
-#define SYS_ERROR_H_3284791347018951324534
-
-#include <string>
-#include "utf.h"
-#include "i18n.h"
-#include "scope_guard.h"
-
- #include <cstring>
- #include <cerrno>
-
-
-namespace zen
-{
-//evaluate GetLastError()/errno and assemble specific error message
- using ErrorCode = int;
-
-ErrorCode getLastError();
-
-std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec);
-std::wstring formatSystemError(const std::wstring& functionName, const std::wstring& errorCode, const std::wstring& errorMsg);
-
-//A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"!
-class SysError
-{
-public:
- explicit SysError(const std::wstring& msg) : msg_(msg) {}
- const std::wstring& toString() const { return msg_; }
-
-private:
- std::wstring msg_;
-};
-
-#define DEFINE_NEW_SYS_ERROR(X) struct X : public SysError { X(const std::wstring& msg) : SysError(msg) {} };
-
-
-
-#define THROW_LAST_SYS_ERROR(functionName) \
- do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false)
-
-
-
-
-
-
-//######################## implementation ########################
-inline
-ErrorCode getLastError()
-{
- return errno; //don't use "::", errno is a macro!
-}
-
-
-std::wstring formatSystemErrorRaw(long long) = delete; //intentional overload ambiguity to catch usage errors
-
-inline
-std::wstring formatSystemErrorRaw(ErrorCode ec) //return empty string on error
-{
- const ErrorCode currentError = getLastError(); //not necessarily == lastError
-
- std::wstring errorMsg;
- ZEN_ON_SCOPE_EXIT(errno = currentError);
-
- errorMsg = utfTo<std::wstring>(::strerror(ec));
- trim(errorMsg); //Windows messages seem to end with a blank...
-
- return errorMsg;
-}
-
-
-std::wstring formatSystemError(const std::wstring& functionName, long long lastError) = delete; //intentional overload ambiguity to catch usage errors with HRESULT!
-
-inline
-std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec)
-{
- return formatSystemError(functionName, replaceCpy(_("Error Code %x"), L"%x", numberTo<std::wstring>(ec)), formatSystemErrorRaw(ec));
-}
-
-
-inline
-std::wstring formatSystemError(const std::wstring& functionName, const std::wstring& errorCode, const std::wstring& errorMsg)
-{
- std::wstring output = errorCode + L":";
-
- const std::wstring errorMsgFmt = trimCpy(errorMsg);
- if (!errorMsgFmt.empty())
- {
- output += L" ";
- output += errorMsgFmt;
- }
-
- output += L" [" + functionName + L"]";
-
- return output;
-}
-
-}
-
-#endif //SYS_ERROR_H_3284791347018951324534
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef SYS_ERROR_H_3284791347018951324534
+#define SYS_ERROR_H_3284791347018951324534
+
+#include <string>
+#include "utf.h"
+#include "i18n.h"
+#include "scope_guard.h"
+
+ #include <cstring>
+ #include <cerrno>
+
+
+namespace zen
+{
+//evaluate GetLastError()/errno and assemble specific error message
+ using ErrorCode = int;
+
+ErrorCode getLastError();
+
+std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec);
+std::wstring formatSystemError(const std::wstring& functionName, const std::wstring& errorCode, const std::wstring& errorMsg);
+
+//A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"!
+class SysError
+{
+public:
+ explicit SysError(const std::wstring& msg) : msg_(msg) {}
+ const std::wstring& toString() const { return msg_; }
+
+private:
+ std::wstring msg_;
+};
+
+#define DEFINE_NEW_SYS_ERROR(X) struct X : public SysError { X(const std::wstring& msg) : SysError(msg) {} };
+
+
+
+#define THROW_LAST_SYS_ERROR(functionName) \
+ do { const ErrorCode ecInternal = getLastError(); throw SysError(formatSystemError(functionName, ecInternal)); } while (false)
+
+
+
+
+
+
+//######################## implementation ########################
+inline
+ErrorCode getLastError()
+{
+ return errno; //don't use "::", errno is a macro!
+}
+
+
+std::wstring formatSystemErrorRaw(long long) = delete; //intentional overload ambiguity to catch usage errors
+
+inline
+std::wstring formatSystemErrorRaw(ErrorCode ec) //return empty string on error
+{
+ const ErrorCode currentError = getLastError(); //not necessarily == lastError
+
+ std::wstring errorMsg;
+ ZEN_ON_SCOPE_EXIT(errno = currentError);
+
+ errorMsg = utfTo<std::wstring>(::strerror(ec));
+ trim(errorMsg); //Windows messages seem to end with a blank...
+
+ return errorMsg;
+}
+
+
+std::wstring formatSystemError(const std::wstring& functionName, long long lastError) = delete; //intentional overload ambiguity to catch usage errors with HRESULT!
+
+inline
+std::wstring formatSystemError(const std::wstring& functionName, ErrorCode ec)
+{
+ return formatSystemError(functionName, replaceCpy(_("Error Code %x"), L"%x", numberTo<std::wstring>(ec)), formatSystemErrorRaw(ec));
+}
+
+
+inline
+std::wstring formatSystemError(const std::wstring& functionName, const std::wstring& errorCode, const std::wstring& errorMsg)
+{
+ std::wstring output = errorCode + L":";
+
+ const std::wstring errorMsgFmt = trimCpy(errorMsg);
+ if (!errorMsgFmt.empty())
+ {
+ output += L" ";
+ output += errorMsgFmt;
+ }
+
+ output += L" [" + functionName + L"]";
+
+ return output;
+}
+
+}
+
+#endif //SYS_ERROR_H_3284791347018951324534
diff --git a/zen/thread.h b/zen/thread.h
index ae4c347e..76306a51 100755
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -1,415 +1,415 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef THREAD_H_7896323423432235246427
-#define THREAD_H_7896323423432235246427
-
-#include <thread>
-#include <future>
-#include "scope_guard.h"
-#include "type_traits.h"
-#include "optional.h"
-
-
-namespace zen
-{
-class InterruptionStatus;
-
-class InterruptibleThread
-{
-public:
- InterruptibleThread() {}
- InterruptibleThread (InterruptibleThread&& tmp) = default;
- InterruptibleThread& operator=(InterruptibleThread&& tmp) = default;
-
- template <class Function>
- InterruptibleThread(Function&& f);
-
- bool joinable () const { return stdThread_.joinable(); }
- void interrupt();
- void join () { stdThread_.join(); }
- void detach () { stdThread_.detach(); }
-
- template <class Rep, class Period>
- bool tryJoinFor(const std::chrono::duration<Rep, Period>& relTime)
- {
- if (threadCompleted_.wait_for(relTime) == std::future_status::ready)
- {
- stdThread_.join(); //runs thread-local destructors => this better be fast!!!
- return true;
- }
- return false;
- }
-
-private:
- std::thread stdThread_;
- std::shared_ptr<InterruptionStatus> intStatus_;
- std::future<void> threadCompleted_;
-};
-
-//context of worker thread:
-void interruptionPoint(); //throw ThreadInterruption
-
-template<class Predicate>
-void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred); //throw ThreadInterruption
-
-template <class Rep, class Period>
-void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //throw ThreadInterruption
-
-
-uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
-
-//------------------------------------------------------------------------------------------
-
-/*
-std::async replacement without crappy semantics:
- 1. guaranteed to run asynchronously
- 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor
-
-Example:
- Zstring dirpath = ...
- auto ft = zen::runAsync([=](){ return zen::dirExists(dirpath); });
- if (ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get())
- //dir exising
-*/
-template <class Function>
-auto runAsync(Function&& fun);
-
-//wait for all with a time limit: return true if *all* results are available!
-template<class InputIterator, class Duration>
-bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration);
-
-template<typename T> inline
-bool isReady(const std::future<T>& f) { return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; }
-//------------------------------------------------------------------------------------------
-
-//wait until first job is successful or all failed: substitute until std::when_any is available
-template <class T>
-class GetFirstResult
-{
-public:
- GetFirstResult();
-
- template <class Fun>
- void addJob(Fun&& f); //f must return a zen::Opt<T> containing a value if successful
-
- template <class Duration>
- bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed
-
- //return first value or none if all jobs failed; blocks until result is ready!
- Opt<T> get() const; //may be called only once!
-
-private:
- class AsyncResult;
- std::shared_ptr<AsyncResult> asyncResult_;
- size_t jobsTotal_ = 0;
-};
-
-//------------------------------------------------------------------------------------------
-
-//value associated with mutex and guaranteed protected access:
-template <class T>
-class Protected
-{
-public:
- Protected() {}
- Protected(const T& value) : value_(value) {}
-
- template <class Function>
- void access(Function fun)
- {
- std::lock_guard<std::mutex> dummy(lockValue);
- fun(value_);
- }
-
-private:
- Protected (const Protected&) = delete;
- Protected& operator=(const Protected&) = delete;
-
- std::mutex lockValue;
- T value_{};
-};
-
-
-
-
-
-
-
-
-
-//###################### implementation ######################
-
-namespace impl
-{
-template <class Function> inline
-auto runAsync(Function&& fun, TrueType /*copy-constructible*/)
-{
- using ResultType = decltype(fun());
-
- //note: std::packaged_task does NOT support move-only function objects!
- std::packaged_task<ResultType()> pt(std::forward<Function>(fun));
- auto fut = pt.get_future();
- std::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
- return fut;
-}
-
-
-template <class Function> inline
-auto runAsync(Function&& fun, FalseType /*copy-constructible*/)
-{
- //support move-only function objects!
- auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun));
- return runAsync([sharedFun] { return (*sharedFun)(); }, TrueType());
-}
-}
-
-
-template <class Function> inline
-auto runAsync(Function&& fun)
-{
- return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>());
-}
-
-
-template<class InputIterator, class Duration> inline
-bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& duration)
-{
- const std::chrono::steady_clock::time_point stopTime = std::chrono::steady_clock::now() + duration;
- for (; first != last; ++first)
- if (first->wait_until(stopTime) != std::future_status::ready)
- return false; //time elapsed
- return true;
-}
-
-
-template <class T>
-class GetFirstResult<T>::AsyncResult
-{
-public:
- //context: worker threads
- void reportFinished(Opt<T>&& result)
- {
- {
- std::lock_guard<std::mutex> dummy(lockResult);
- ++jobsFinished;
- if (!result_)
- result_ = std::move(result);
- }
- conditionJobDone.notify_all(); //better notify all, considering bugs like: https://svn.boost.org/trac/boost/ticket/7796
- }
-
- //context: main thread
- template <class Duration>
- bool waitForResult(size_t jobsTotal, const Duration& duration)
- {
- std::unique_lock<std::mutex> dummy(lockResult);
- return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); });
- }
-
- Opt<T> getResult(size_t jobsTotal)
- {
- std::unique_lock<std::mutex> dummy(lockResult);
- conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); });
-
- return std::move(result_);
- }
-
-private:
- bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished >= jobsTotal); } //call while locked!
-
-
- std::mutex lockResult;
- size_t jobsFinished = 0; //
- Opt<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal"
- std::condition_variable conditionJobDone;
-};
-
-
-
-template <class T> inline
-GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {}
-
-
-template <class T>
-template <class Fun> inline
-void GetFirstResult<T>::addJob(Fun&& f) //f must return a zen::Opt<T> containing a value on success
-{
- std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); });
- ++jobsTotal_;
- t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
-}
-
-
-template <class T>
-template <class Duration> inline
-bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
-
-
-template <class T> inline
-Opt<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); }
-
-//------------------------------------------------------------------------------------------
-
-//thread_local with non-POD seems to cause memory leaks on VS 14 => pointer only is fine:
- #define ZEN_THREAD_LOCAL_SPECIFIER __thread
-
-
-class ThreadInterruption {};
-
-
-class InterruptionStatus
-{
-public:
- //context of InterruptibleThread instance:
- void interrupt()
- {
- interrupted = true;
-
- {
- std::lock_guard<std::mutex> dummy(lockSleep); //needed! makes sure the following signal is not lost!
- //usually we'd make "interrupted" non-atomic, but this is already given due to interruptibleWait() handling
- }
- conditionSleepInterruption.notify_all();
-
- std::lock_guard<std::mutex> dummy(lockConditionPtr);
- if (activeCondition)
- activeCondition->notify_all(); //signal may get lost!
- //alternative design locking the cv's mutex here could be dangerous: potential for dead lock!
- }
-
- //context of worker thread:
- void checkInterruption() //throw ThreadInterruption
- {
- if (interrupted)
- throw ThreadInterruption();
- }
-
- //context of worker thread:
- template<class Predicate>
- void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred) //throw ThreadInterruption
- {
- setConditionVar(&cv);
- ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr));
-
- //"interrupted" is not protected by cv's mutex => signal may get lost!!! => add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out!
- while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted || pred(); }))
- ;
-
- checkInterruption(); //throw ThreadInterruption
- }
-
- //context of worker thread:
- template <class Rep, class Period>
- void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption
- {
- std::unique_lock<std::mutex> lock(lockSleep);
- if (conditionSleepInterruption.wait_for(lock, relTime, [this] { return static_cast<bool>(this->interrupted); }))
- throw ThreadInterruption();
- }
-
-private:
- void setConditionVar(std::condition_variable* cv)
- {
- std::lock_guard<std::mutex> dummy(lockConditionPtr);
- activeCondition = cv;
- }
-
- std::atomic<bool> interrupted{ false }; //std:atomic is uninitialized by default!!!
- //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects."
-
- std::condition_variable* activeCondition = nullptr;
- std::mutex lockConditionPtr; //serialize pointer access (only!)
-
- std::condition_variable conditionSleepInterruption;
- std::mutex lockSleep;
-};
-
-
-namespace impl
-{
-inline
-InterruptionStatus*& refThreadLocalInterruptionStatus()
-{
- static ZEN_THREAD_LOCAL_SPECIFIER InterruptionStatus* threadLocalInterruptionStatus = nullptr;
- return threadLocalInterruptionStatus;
-}
-}
-
-//context of worker thread:
-inline
-void interruptionPoint() //throw ThreadInterruption
-{
- assert(impl::refThreadLocalInterruptionStatus());
- if (impl::refThreadLocalInterruptionStatus())
- impl::refThreadLocalInterruptionStatus()->checkInterruption(); //throw ThreadInterruption
-}
-
-
-//context of worker thread:
-template<class Predicate> inline
-void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred) //throw ThreadInterruption
-{
- assert(impl::refThreadLocalInterruptionStatus());
- if (impl::refThreadLocalInterruptionStatus())
- impl::refThreadLocalInterruptionStatus()->interruptibleWait(cv, lock, pred);
- else
- cv.wait(lock, pred);
-}
-
-//context of worker thread:
-template <class Rep, class Period> inline
-void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption
-{
- assert(impl::refThreadLocalInterruptionStatus());
- if (impl::refThreadLocalInterruptionStatus())
- impl::refThreadLocalInterruptionStatus()->interruptibleSleep(relTime);
- else
- std::this_thread::sleep_for(relTime);
-}
-
-
-template <class Function> inline
-InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_shared<InterruptionStatus>())
-{
- std::promise<void> pFinished;
- threadCompleted_ = pFinished.get_future();
-
- stdThread_ = std::thread([f = std::forward<Function>(f),
- intStatus = this->intStatus_,
- pFinished = std::move(pFinished)]() mutable
- {
- assert(!impl::refThreadLocalInterruptionStatus());
- impl::refThreadLocalInterruptionStatus() = intStatus.get();
- ZEN_ON_SCOPE_EXIT(impl::refThreadLocalInterruptionStatus() = nullptr);
- ZEN_ON_SCOPE_EXIT(pFinished.set_value());
-
- try
- {
- f(); //throw ThreadInterruption
- }
- catch (ThreadInterruption&) {}
- });
-}
-
-
-inline
-void InterruptibleThread::interrupt() { intStatus_->interrupt(); }
-
-
-
-
-inline
-uint64_t getThreadId()
-{
- //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach:
- static_assert(sizeof(uint64_t) >= sizeof(void*), "");
- return reinterpret_cast<uint64_t>(static_cast<void*>(&errno));
-
-}
-}
-
-#endif //THREAD_H_7896323423432235246427
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef THREAD_H_7896323423432235246427
+#define THREAD_H_7896323423432235246427
+
+#include <thread>
+#include <future>
+#include "scope_guard.h"
+#include "type_traits.h"
+#include "optional.h"
+
+
+namespace zen
+{
+class InterruptionStatus;
+
+class InterruptibleThread
+{
+public:
+ InterruptibleThread() {}
+ InterruptibleThread (InterruptibleThread&& tmp) = default;
+ InterruptibleThread& operator=(InterruptibleThread&& tmp) = default;
+
+ template <class Function>
+ InterruptibleThread(Function&& f);
+
+ bool joinable () const { return stdThread_.joinable(); }
+ void interrupt();
+ void join () { stdThread_.join(); }
+ void detach () { stdThread_.detach(); }
+
+ template <class Rep, class Period>
+ bool tryJoinFor(const std::chrono::duration<Rep, Period>& relTime)
+ {
+ if (threadCompleted_.wait_for(relTime) == std::future_status::ready)
+ {
+ stdThread_.join(); //runs thread-local destructors => this better be fast!!!
+ return true;
+ }
+ return false;
+ }
+
+private:
+ std::thread stdThread_;
+ std::shared_ptr<InterruptionStatus> intStatus_;
+ std::future<void> threadCompleted_;
+};
+
+//context of worker thread:
+void interruptionPoint(); //throw ThreadInterruption
+
+template<class Predicate>
+void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred); //throw ThreadInterruption
+
+template <class Rep, class Period>
+void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime); //throw ThreadInterruption
+
+
+uint64_t getThreadId(); //simple integer thread id, unlike boost::thread::id: https://svn.boost.org/trac/boost/ticket/5754
+
+//------------------------------------------------------------------------------------------
+
+/*
+std::async replacement without crappy semantics:
+ 1. guaranteed to run asynchronously
+ 2. does not follow C++11 [futures.async], Paragraph 5, where std::future waits for thread in destructor
+
+Example:
+ Zstring dirpath = ...
+ auto ft = zen::runAsync([=](){ return zen::dirExists(dirpath); });
+ if (ft.wait_for(std::chrono::milliseconds(200)) == std::future_status::ready && ft.get())
+ //dir exising
+*/
+template <class Function>
+auto runAsync(Function&& fun);
+
+//wait for all with a time limit: return true if *all* results are available!
+template<class InputIterator, class Duration>
+bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration);
+
+template<typename T> inline
+bool isReady(const std::future<T>& f) { return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready; }
+//------------------------------------------------------------------------------------------
+
+//wait until first job is successful or all failed: substitute until std::when_any is available
+template <class T>
+class GetFirstResult
+{
+public:
+ GetFirstResult();
+
+ template <class Fun>
+ void addJob(Fun&& f); //f must return a zen::Opt<T> containing a value if successful
+
+ template <class Duration>
+ bool timedWait(const Duration& duration) const; //true: "get()" is ready, false: time elapsed
+
+ //return first value or none if all jobs failed; blocks until result is ready!
+ Opt<T> get() const; //may be called only once!
+
+private:
+ class AsyncResult;
+ std::shared_ptr<AsyncResult> asyncResult_;
+ size_t jobsTotal_ = 0;
+};
+
+//------------------------------------------------------------------------------------------
+
+//value associated with mutex and guaranteed protected access:
+template <class T>
+class Protected
+{
+public:
+ Protected() {}
+ Protected(const T& value) : value_(value) {}
+
+ template <class Function>
+ void access(Function fun)
+ {
+ std::lock_guard<std::mutex> dummy(lockValue);
+ fun(value_);
+ }
+
+private:
+ Protected (const Protected&) = delete;
+ Protected& operator=(const Protected&) = delete;
+
+ std::mutex lockValue;
+ T value_{};
+};
+
+
+
+
+
+
+
+
+
+//###################### implementation ######################
+
+namespace impl
+{
+template <class Function> inline
+auto runAsync(Function&& fun, TrueType /*copy-constructible*/)
+{
+ using ResultType = decltype(fun());
+
+ //note: std::packaged_task does NOT support move-only function objects!
+ std::packaged_task<ResultType()> pt(std::forward<Function>(fun));
+ auto fut = pt.get_future();
+ std::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
+ return fut;
+}
+
+
+template <class Function> inline
+auto runAsync(Function&& fun, FalseType /*copy-constructible*/)
+{
+ //support move-only function objects!
+ auto sharedFun = std::make_shared<Function>(std::forward<Function>(fun));
+ return runAsync([sharedFun] { return (*sharedFun)(); }, TrueType());
+}
+}
+
+
+template <class Function> inline
+auto runAsync(Function&& fun)
+{
+ return impl::runAsync(std::forward<Function>(fun), StaticBool<std::is_copy_constructible<Function>::value>());
+}
+
+
+template<class InputIterator, class Duration> inline
+bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& duration)
+{
+ const std::chrono::steady_clock::time_point stopTime = std::chrono::steady_clock::now() + duration;
+ for (; first != last; ++first)
+ if (first->wait_until(stopTime) != std::future_status::ready)
+ return false; //time elapsed
+ return true;
+}
+
+
+template <class T>
+class GetFirstResult<T>::AsyncResult
+{
+public:
+ //context: worker threads
+ void reportFinished(Opt<T>&& result)
+ {
+ {
+ std::lock_guard<std::mutex> dummy(lockResult);
+ ++jobsFinished;
+ if (!result_)
+ result_ = std::move(result);
+ }
+ conditionJobDone.notify_all(); //better notify all, considering bugs like: https://svn.boost.org/trac/boost/ticket/7796
+ }
+
+ //context: main thread
+ template <class Duration>
+ bool waitForResult(size_t jobsTotal, const Duration& duration)
+ {
+ std::unique_lock<std::mutex> dummy(lockResult);
+ return conditionJobDone.wait_for(dummy, duration, [&] { return this->jobDone(jobsTotal); });
+ }
+
+ Opt<T> getResult(size_t jobsTotal)
+ {
+ std::unique_lock<std::mutex> dummy(lockResult);
+ conditionJobDone.wait(dummy, [&] { return this->jobDone(jobsTotal); });
+
+ return std::move(result_);
+ }
+
+private:
+ bool jobDone(size_t jobsTotal) const { return result_ || (jobsFinished >= jobsTotal); } //call while locked!
+
+
+ std::mutex lockResult;
+ size_t jobsFinished = 0; //
+ Opt<T> result_; //our condition is: "have result" or "jobsFinished == jobsTotal"
+ std::condition_variable conditionJobDone;
+};
+
+
+
+template <class T> inline
+GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {}
+
+
+template <class T>
+template <class Fun> inline
+void GetFirstResult<T>::addJob(Fun&& f) //f must return a zen::Opt<T> containing a value on success
+{
+ std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); });
+ ++jobsTotal_;
+ t.detach(); //we have to be explicit since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!!
+}
+
+
+template <class T>
+template <class Duration> inline
+bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
+
+
+template <class T> inline
+Opt<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); }
+
+//------------------------------------------------------------------------------------------
+
+//thread_local with non-POD seems to cause memory leaks on VS 14 => pointer only is fine:
+ #define ZEN_THREAD_LOCAL_SPECIFIER __thread
+
+
+class ThreadInterruption {};
+
+
+class InterruptionStatus
+{
+public:
+ //context of InterruptibleThread instance:
+ void interrupt()
+ {
+ interrupted = true;
+
+ {
+ std::lock_guard<std::mutex> dummy(lockSleep); //needed! makes sure the following signal is not lost!
+ //usually we'd make "interrupted" non-atomic, but this is already given due to interruptibleWait() handling
+ }
+ conditionSleepInterruption.notify_all();
+
+ std::lock_guard<std::mutex> dummy(lockConditionPtr);
+ if (activeCondition)
+ activeCondition->notify_all(); //signal may get lost!
+ //alternative design locking the cv's mutex here could be dangerous: potential for dead lock!
+ }
+
+ //context of worker thread:
+ void checkInterruption() //throw ThreadInterruption
+ {
+ if (interrupted)
+ throw ThreadInterruption();
+ }
+
+ //context of worker thread:
+ template<class Predicate>
+ void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred) //throw ThreadInterruption
+ {
+ setConditionVar(&cv);
+ ZEN_ON_SCOPE_EXIT(setConditionVar(nullptr));
+
+ //"interrupted" is not protected by cv's mutex => signal may get lost!!! => add artifical time out to mitigate! CPU: 0.25% vs 0% for longer time out!
+ while (!cv.wait_for(lock, std::chrono::milliseconds(1), [&] { return this->interrupted || pred(); }))
+ ;
+
+ checkInterruption(); //throw ThreadInterruption
+ }
+
+ //context of worker thread:
+ template <class Rep, class Period>
+ void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption
+ {
+ std::unique_lock<std::mutex> lock(lockSleep);
+ if (conditionSleepInterruption.wait_for(lock, relTime, [this] { return static_cast<bool>(this->interrupted); }))
+ throw ThreadInterruption();
+ }
+
+private:
+ void setConditionVar(std::condition_variable* cv)
+ {
+ std::lock_guard<std::mutex> dummy(lockConditionPtr);
+ activeCondition = cv;
+ }
+
+ std::atomic<bool> interrupted{ false }; //std:atomic is uninitialized by default!!!
+ //"The default constructor is trivial: no initialization takes place other than zero initialization of static and thread-local objects."
+
+ std::condition_variable* activeCondition = nullptr;
+ std::mutex lockConditionPtr; //serialize pointer access (only!)
+
+ std::condition_variable conditionSleepInterruption;
+ std::mutex lockSleep;
+};
+
+
+namespace impl
+{
+inline
+InterruptionStatus*& refThreadLocalInterruptionStatus()
+{
+ static ZEN_THREAD_LOCAL_SPECIFIER InterruptionStatus* threadLocalInterruptionStatus = nullptr;
+ return threadLocalInterruptionStatus;
+}
+}
+
+//context of worker thread:
+inline
+void interruptionPoint() //throw ThreadInterruption
+{
+ assert(impl::refThreadLocalInterruptionStatus());
+ if (impl::refThreadLocalInterruptionStatus())
+ impl::refThreadLocalInterruptionStatus()->checkInterruption(); //throw ThreadInterruption
+}
+
+
+//context of worker thread:
+template<class Predicate> inline
+void interruptibleWait(std::condition_variable& cv, std::unique_lock<std::mutex>& lock, Predicate pred) //throw ThreadInterruption
+{
+ assert(impl::refThreadLocalInterruptionStatus());
+ if (impl::refThreadLocalInterruptionStatus())
+ impl::refThreadLocalInterruptionStatus()->interruptibleWait(cv, lock, pred);
+ else
+ cv.wait(lock, pred);
+}
+
+//context of worker thread:
+template <class Rep, class Period> inline
+void interruptibleSleep(const std::chrono::duration<Rep, Period>& relTime) //throw ThreadInterruption
+{
+ assert(impl::refThreadLocalInterruptionStatus());
+ if (impl::refThreadLocalInterruptionStatus())
+ impl::refThreadLocalInterruptionStatus()->interruptibleSleep(relTime);
+ else
+ std::this_thread::sleep_for(relTime);
+}
+
+
+template <class Function> inline
+InterruptibleThread::InterruptibleThread(Function&& f) : intStatus_(std::make_shared<InterruptionStatus>())
+{
+ std::promise<void> pFinished;
+ threadCompleted_ = pFinished.get_future();
+
+ stdThread_ = std::thread([f = std::forward<Function>(f),
+ intStatus = this->intStatus_,
+ pFinished = std::move(pFinished)]() mutable
+ {
+ assert(!impl::refThreadLocalInterruptionStatus());
+ impl::refThreadLocalInterruptionStatus() = intStatus.get();
+ ZEN_ON_SCOPE_EXIT(impl::refThreadLocalInterruptionStatus() = nullptr);
+ ZEN_ON_SCOPE_EXIT(pFinished.set_value());
+
+ try
+ {
+ f(); //throw ThreadInterruption
+ }
+ catch (ThreadInterruption&) {}
+ });
+}
+
+
+inline
+void InterruptibleThread::interrupt() { intStatus_->interrupt(); }
+
+
+
+
+inline
+uint64_t getThreadId()
+{
+ //obviously "gettid()" is not available on Ubuntu/Debian/Suse => use the OpenSSL approach:
+ static_assert(sizeof(uint64_t) >= sizeof(void*), "");
+ return reinterpret_cast<uint64_t>(static_cast<void*>(&errno));
+
+}
+}
+
+#endif //THREAD_H_7896323423432235246427
diff --git a/zen/time.h b/zen/time.h
index 1b6a3c92..ace87024 100755
--- a/zen/time.h
+++ b/zen/time.h
@@ -1,368 +1,368 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef TIME_H_8457092814324342453627
-#define TIME_H_8457092814324342453627
-
-#include <ctime>
-#include "string_tools.h"
-
-
-namespace zen
-{
-struct TimeComp //replaces std::tm and SYSTEMTIME
-{
- int year = 0; // -
- int month = 0; //1-12
- int day = 0; //1-31
- int hour = 0; //0-23
- int minute = 0; //0-59
- int second = 0; //0-60 (including leap second)
-};
-
-TimeComp getLocalTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components
-time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error
-
-TimeComp getUtcTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to UTC time components
-time_t utcToTimeT(const TimeComp& comp); //convert UTC time components to time_t (UTC), returns -1 on error
-
-//----------------------------------------------------------------------------------------------------------------------------------
-
-/*
-format (current) date and time; example:
- formatTime<std::wstring>(L"%Y|%m|%d"); -> "2011|10|29"
- formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29"
- formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34"
-*/
-template <class String, class String2>
-String formatTime(const String2& format, const TimeComp& comp = getLocalTime()); //format as specified by "std::strftime", returns empty string on failure
-
-//the "format" parameter of formatTime() is partially specialized with the following type tags:
-const struct FormatDateTag {} FORMAT_DATE = {}; //%x - locale dependent date representation: e.g. 08/23/01
-const struct FormatTimeTag {} FORMAT_TIME = {}; //%X - locale dependent time representation: e.g. 14:55:02
-const struct FormatDateTimeTag {} FORMAT_DATE_TIME = {}; //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001
-
-const struct FormatIsoDateTag {} FORMAT_ISO_DATE = {}; //%Y-%m-%d - e.g. 2001-08-23
-const struct FormatIsoTimeTag {} FORMAT_ISO_TIME = {}; //%H:%M:%S - e.g. 14:55:02
-const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02
-
-//----------------------------------------------------------------------------------------------------------------------------------
-
-template <class String, class String2>
-bool parseTime(const String& format, const String2& str, TimeComp& comp); //similar to ::strptime(), return true on success
-
-//----------------------------------------------------------------------------------------------------------------------------------
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//############################ implementation ##############################
-namespace implementation
-{
-inline
-std::tm toClibTimeComponents(const TimeComp& comp)
-{
- assert(1 <= comp.month && comp.month <= 12 &&
- 1 <= comp.day && comp.day <= 31 &&
- 0 <= comp.hour && comp.hour <= 23 &&
- 0 <= comp.minute && comp.minute <= 59 &&
- 0 <= comp.second && comp.second <= 61);
-
- std::tm ctc = {};
- ctc.tm_year = comp.year - 1900; //years since 1900
- ctc.tm_mon = comp.month - 1; //0-11
- ctc.tm_mday = comp.day; //1-31
- ctc.tm_hour = comp.hour; //0-23
- ctc.tm_min = comp.minute; //0-59
- ctc.tm_sec = comp.second; //0-60 (including leap second)
- ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available
- //ctc.tm_wday
- //ctc.tm_yday
- return ctc;
-}
-
-inline
-TimeComp toZenTimeComponents(const std::tm& ctc)
-{
- TimeComp comp;
- comp.year = ctc.tm_year + 1900;
- comp.month = ctc.tm_mon + 1;
- comp.day = ctc.tm_mday;
- comp.hour = ctc.tm_hour;
- comp.minute = ctc.tm_min;
- comp.second = ctc.tm_sec;
- return comp;
-}
-
-
-template <class T>
-struct GetFormat; //get default time formats as char* or wchar_t*
-
-template <>
-struct GetFormat<FormatDateTag> //%x - locale dependent date representation: e.g. 08/23/01
-{
- const char* format(char) const { return "%x"; }
- const wchar_t* format(wchar_t) const { return L"%x"; }
-};
-
-template <>
-struct GetFormat<FormatTimeTag> //%X - locale dependent time representation: e.g. 14:55:02
-{
- const char* format(char) const { return "%X"; }
- const wchar_t* format(wchar_t) const { return L"%X"; }
-};
-
-template <>
-struct GetFormat<FormatDateTimeTag> //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001
-{
- const char* format(char) const { return "%c"; }
- const wchar_t* format(wchar_t) const { return L"%c"; }
-};
-
-template <>
-struct GetFormat<FormatIsoDateTag> //%Y-%m-%d - e.g. 2001-08-23
-{
- const char* format(char) const { return "%Y-%m-%d"; }
- const wchar_t* format(wchar_t) const { return L"%Y-%m-%d"; }
-};
-
-template <>
-struct GetFormat<FormatIsoTimeTag> //%H:%M:%S - e.g. 14:55:02
-{
- const char* format(char) const { return "%H:%M:%S"; }
- const wchar_t* format(wchar_t) const { return L"%H:%M:%S"; }
-};
-
-template <>
-struct GetFormat<FormatIsoDateTimeTag> //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02
-{
- const char* format(char) const { return "%Y-%m-%d %H:%M:%S"; }
- const wchar_t* format(wchar_t) const { return L"%Y-%m-%d %H:%M:%S"; }
-};
-
-
-//strftime() craziness on invalid input:
-// VS 2010: CRASH unless "_invalid_parameter_handler" is set: https://msdn.microsoft.com/en-us/library/ksazx244.aspx
-// GCC: returns 0, apparently no crash. Still, considering some clib maintainer's comments, we should expect the worst!
-inline
-size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const std::tm* timeptr)
-{
- return std::strftime(buffer, bufferSize, format, timeptr);
-}
-
-
-inline
-size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const std::tm* timeptr)
-{
- return std::wcsftime(buffer, bufferSize, format, timeptr);
-}
-
-/*
-inline
-bool isValid(const std::tm& t)
-{
- -> not enough! MSCRT has different limits than the C standard which even seem to change with different versions:
- _VALIDATE_RETURN((( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE)
- _VALIDATE_RETURN(( timeptr->tm_year >= -1900 ) && ( timeptr->tm_year <= 8099 ), EINVAL, FALSE)
- -> also std::mktime does *not* help here at all!
-
- auto inRange = [](int value, int minVal, int maxVal) { return minVal <= value && value <= maxVal; };
-
- //http://www.cplusplus.com/reference/clibrary/ctime/tm/
- return inRange(t.tm_sec , 0, 61) &&
- inRange(t.tm_min , 0, 59) &&
- inRange(t.tm_hour, 0, 23) &&
- inRange(t.tm_mday, 1, 31) &&
- inRange(t.tm_mon , 0, 11) &&
- //tm_year
- inRange(t.tm_wday, 0, 6) &&
- inRange(t.tm_yday, 0, 365);
- //tm_isdst
-};
-*/
-
-template <class CharType> inline
-size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const std::tm* timeptr)
-{
-
- return strftimeWrap_impl(buffer, bufferSize, format, timeptr);
-}
-
-
-struct UserDefinedFormatTag {};
-struct PredefinedFormatTag {};
-
-template <class String, class String2> inline
-String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure
-{
- using CharType = typename GetCharType<String>::Type;
- std::tm ctc = toClibTimeComponents(comp);
- std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday
- //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent
-
- CharType buffer[256] = {};
- const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc);
- return String(buffer, charsWritten);
-}
-
-template <class String, class FormatType> inline
-String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag)
-{
- using CharType = typename GetCharType<String>::Type;
- return formatTime<String>(GetFormat<FormatType>().format(CharType()), comp, UserDefinedFormatTag());
-}
-}
-
-
-inline
-TimeComp getLocalTime(time_t utc)
-{
- std::tm comp = {};
- if (::localtime_r(&utc, &comp) == nullptr)
- return TimeComp();
-
- return implementation::toZenTimeComponents(comp);
-}
-
-
-inline
-TimeComp getUtcTime(time_t utc)
-{
- std::tm comp = {};
- if (::gmtime_r(&utc, &comp) == nullptr)
- return TimeComp();
-
- return implementation::toZenTimeComponents(comp);
-}
-
-
-inline
-time_t localToTimeT(const TimeComp& comp) //returns -1 on error
-{
- std::tm ctc = implementation::toClibTimeComponents(comp);
- return std::mktime(&ctc);
-}
-
-
-inline
-time_t utcToTimeT(const TimeComp& comp) //returns -1 on error
-{
- std::tm ctc = implementation::toClibTimeComponents(comp);
- ctc.tm_isdst = 0; //"Zero (0) to indicate that standard time is in effect" => unused by _mkgmtime, but take no chances
- return ::timegm(&ctc);
-}
-
-
-template <class String, class String2> inline
-String formatTime(const String2& format, const TimeComp& comp)
-{
- using FormatTag = typename SelectIf<
- IsSameType<String2, FormatDateTag >::value ||
- IsSameType<String2, FormatTimeTag >::value ||
- IsSameType<String2, FormatDateTimeTag >::value ||
- IsSameType<String2, FormatIsoDateTag >::value ||
- IsSameType<String2, FormatIsoTimeTag >::value ||
- IsSameType<String2, FormatIsoDateTimeTag>::value, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Type;
-
- return implementation::formatTime<String>(format, comp, FormatTag());
-}
-
-
-template <class String, class String2>
-bool parseTime(const String& format, const String2& str, TimeComp& comp) //return true on success
-{
- using CharType = typename GetCharType<String>::Type;
- static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, "");
-
- const CharType* itFmt = strBegin(format);
- const CharType* const fmtLast = itFmt + strLength(format);
-
- const CharType* itStr = strBegin(str);
- const CharType* const strLast = itStr + strLength(str);
-
- auto extractNumber = [&](int& result, size_t digitCount) -> bool
- {
- if (strLast - itStr < makeSigned(digitCount))
- return false;
-
- if (std::any_of(itStr, itStr + digitCount, [](CharType c) { return !isDigit(c); }))
- return false;
-
- result = zen::stringTo<int>(StringRef<const CharType>(itStr, itStr + digitCount));
- itStr += digitCount;
- return true;
- };
-
- for (; itFmt != fmtLast; ++itFmt)
- {
- const CharType fmt = *itFmt;
-
- if (fmt == '%')
- {
- ++itFmt;
- if (itFmt == fmtLast)
- return false;
-
- switch (*itFmt)
- {
- case 'Y':
- if (!extractNumber(comp.year, 4))
- return false;
- break;
- case 'm':
- if (!extractNumber(comp.month, 2))
- return false;
- break;
- case 'd':
- if (!extractNumber(comp.day, 2))
- return false;
- break;
- case 'H':
- if (!extractNumber(comp.hour, 2))
- return false;
- break;
- case 'M':
- if (!extractNumber(comp.minute, 2))
- return false;
- break;
- case 'S':
- if (!extractNumber(comp.second, 2))
- return false;
- break;
- default:
- return false;
- }
- }
- else if (isWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars
- {
- while (itStr != strLast && isWhiteSpace(*itStr))
- ++itStr;
- }
- else
- {
- if (itStr == strLast || *itStr != fmt)
- return false;
- ++itStr;
- }
- }
-
- return itStr == strLast;
-}
-}
-
-#endif //TIME_H_8457092814324342453627
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef TIME_H_8457092814324342453627
+#define TIME_H_8457092814324342453627
+
+#include <ctime>
+#include "string_tools.h"
+
+
+namespace zen
+{
+struct TimeComp //replaces std::tm and SYSTEMTIME
+{
+ int year = 0; // -
+ int month = 0; //1-12
+ int day = 0; //1-31
+ int hour = 0; //0-23
+ int minute = 0; //0-59
+ int second = 0; //0-60 (including leap second)
+};
+
+TimeComp getLocalTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to local time components
+time_t localToTimeT(const TimeComp& comp); //convert local time components to time_t (UTC), returns -1 on error
+
+TimeComp getUtcTime(time_t utc = std::time(nullptr)); //convert time_t (UTC) to UTC time components
+time_t utcToTimeT(const TimeComp& comp); //convert UTC time components to time_t (UTC), returns -1 on error
+
+//----------------------------------------------------------------------------------------------------------------------------------
+
+/*
+format (current) date and time; example:
+ formatTime<std::wstring>(L"%Y|%m|%d"); -> "2011|10|29"
+ formatTime<std::wstring>(FORMAT_DATE); -> "2011-10-29"
+ formatTime<std::wstring>(FORMAT_TIME); -> "17:55:34"
+*/
+template <class String, class String2>
+String formatTime(const String2& format, const TimeComp& comp = getLocalTime()); //format as specified by "std::strftime", returns empty string on failure
+
+//the "format" parameter of formatTime() is partially specialized with the following type tags:
+const struct FormatDateTag {} FORMAT_DATE = {}; //%x - locale dependent date representation: e.g. 08/23/01
+const struct FormatTimeTag {} FORMAT_TIME = {}; //%X - locale dependent time representation: e.g. 14:55:02
+const struct FormatDateTimeTag {} FORMAT_DATE_TIME = {}; //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001
+
+const struct FormatIsoDateTag {} FORMAT_ISO_DATE = {}; //%Y-%m-%d - e.g. 2001-08-23
+const struct FormatIsoTimeTag {} FORMAT_ISO_TIME = {}; //%H:%M:%S - e.g. 14:55:02
+const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02
+
+//----------------------------------------------------------------------------------------------------------------------------------
+
+template <class String, class String2>
+bool parseTime(const String& format, const String2& str, TimeComp& comp); //similar to ::strptime(), return true on success
+
+//----------------------------------------------------------------------------------------------------------------------------------
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//############################ implementation ##############################
+namespace implementation
+{
+inline
+std::tm toClibTimeComponents(const TimeComp& comp)
+{
+ assert(1 <= comp.month && comp.month <= 12 &&
+ 1 <= comp.day && comp.day <= 31 &&
+ 0 <= comp.hour && comp.hour <= 23 &&
+ 0 <= comp.minute && comp.minute <= 59 &&
+ 0 <= comp.second && comp.second <= 61);
+
+ std::tm ctc = {};
+ ctc.tm_year = comp.year - 1900; //years since 1900
+ ctc.tm_mon = comp.month - 1; //0-11
+ ctc.tm_mday = comp.day; //1-31
+ ctc.tm_hour = comp.hour; //0-23
+ ctc.tm_min = comp.minute; //0-59
+ ctc.tm_sec = comp.second; //0-60 (including leap second)
+ ctc.tm_isdst = -1; //> 0 if DST is active, == 0 if DST is not active, < 0 if the information is not available
+ //ctc.tm_wday
+ //ctc.tm_yday
+ return ctc;
+}
+
+inline
+TimeComp toZenTimeComponents(const std::tm& ctc)
+{
+ TimeComp comp;
+ comp.year = ctc.tm_year + 1900;
+ comp.month = ctc.tm_mon + 1;
+ comp.day = ctc.tm_mday;
+ comp.hour = ctc.tm_hour;
+ comp.minute = ctc.tm_min;
+ comp.second = ctc.tm_sec;
+ return comp;
+}
+
+
+template <class T>
+struct GetFormat; //get default time formats as char* or wchar_t*
+
+template <>
+struct GetFormat<FormatDateTag> //%x - locale dependent date representation: e.g. 08/23/01
+{
+ const char* format(char) const { return "%x"; }
+ const wchar_t* format(wchar_t) const { return L"%x"; }
+};
+
+template <>
+struct GetFormat<FormatTimeTag> //%X - locale dependent time representation: e.g. 14:55:02
+{
+ const char* format(char) const { return "%X"; }
+ const wchar_t* format(wchar_t) const { return L"%X"; }
+};
+
+template <>
+struct GetFormat<FormatDateTimeTag> //%c - locale dependent date and time: e.g. Thu Aug 23 14:55:02 2001
+{
+ const char* format(char) const { return "%c"; }
+ const wchar_t* format(wchar_t) const { return L"%c"; }
+};
+
+template <>
+struct GetFormat<FormatIsoDateTag> //%Y-%m-%d - e.g. 2001-08-23
+{
+ const char* format(char) const { return "%Y-%m-%d"; }
+ const wchar_t* format(wchar_t) const { return L"%Y-%m-%d"; }
+};
+
+template <>
+struct GetFormat<FormatIsoTimeTag> //%H:%M:%S - e.g. 14:55:02
+{
+ const char* format(char) const { return "%H:%M:%S"; }
+ const wchar_t* format(wchar_t) const { return L"%H:%M:%S"; }
+};
+
+template <>
+struct GetFormat<FormatIsoDateTimeTag> //%Y-%m-%d %H:%M:%S - e.g. 2001-08-23 14:55:02
+{
+ const char* format(char) const { return "%Y-%m-%d %H:%M:%S"; }
+ const wchar_t* format(wchar_t) const { return L"%Y-%m-%d %H:%M:%S"; }
+};
+
+
+//strftime() craziness on invalid input:
+// VS 2010: CRASH unless "_invalid_parameter_handler" is set: https://msdn.microsoft.com/en-us/library/ksazx244.aspx
+// GCC: returns 0, apparently no crash. Still, considering some clib maintainer's comments, we should expect the worst!
+inline
+size_t strftimeWrap_impl(char* buffer, size_t bufferSize, const char* format, const std::tm* timeptr)
+{
+ return std::strftime(buffer, bufferSize, format, timeptr);
+}
+
+
+inline
+size_t strftimeWrap_impl(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const std::tm* timeptr)
+{
+ return std::wcsftime(buffer, bufferSize, format, timeptr);
+}
+
+/*
+inline
+bool isValid(const std::tm& t)
+{
+ -> not enough! MSCRT has different limits than the C standard which even seem to change with different versions:
+ _VALIDATE_RETURN((( timeptr->tm_sec >=0 ) && ( timeptr->tm_sec <= 59 ) ), EINVAL, FALSE)
+ _VALIDATE_RETURN(( timeptr->tm_year >= -1900 ) && ( timeptr->tm_year <= 8099 ), EINVAL, FALSE)
+ -> also std::mktime does *not* help here at all!
+
+ auto inRange = [](int value, int minVal, int maxVal) { return minVal <= value && value <= maxVal; };
+
+ //http://www.cplusplus.com/reference/clibrary/ctime/tm/
+ return inRange(t.tm_sec , 0, 61) &&
+ inRange(t.tm_min , 0, 59) &&
+ inRange(t.tm_hour, 0, 23) &&
+ inRange(t.tm_mday, 1, 31) &&
+ inRange(t.tm_mon , 0, 11) &&
+ //tm_year
+ inRange(t.tm_wday, 0, 6) &&
+ inRange(t.tm_yday, 0, 365);
+ //tm_isdst
+};
+*/
+
+template <class CharType> inline
+size_t strftimeWrap(CharType* buffer, size_t bufferSize, const CharType* format, const std::tm* timeptr)
+{
+
+ return strftimeWrap_impl(buffer, bufferSize, format, timeptr);
+}
+
+
+struct UserDefinedFormatTag {};
+struct PredefinedFormatTag {};
+
+template <class String, class String2> inline
+String formatTime(const String2& format, const TimeComp& comp, UserDefinedFormatTag) //format as specified by "std::strftime", returns empty string on failure
+{
+ using CharType = typename GetCharType<String>::Type;
+ std::tm ctc = toClibTimeComponents(comp);
+ std::mktime(&ctc); // unfortunately std::strftime() needs all elements of "struct tm" filled, e.g. tm_wday, tm_yday
+ //note: although std::mktime() explicitly expects "local time", calculating weekday and day of year *should* be time-zone and DST independent
+
+ CharType buffer[256] = {};
+ const size_t charsWritten = strftimeWrap(buffer, 256, strBegin(format), &ctc);
+ return String(buffer, charsWritten);
+}
+
+template <class String, class FormatType> inline
+String formatTime(FormatType, const TimeComp& comp, PredefinedFormatTag)
+{
+ using CharType = typename GetCharType<String>::Type;
+ return formatTime<String>(GetFormat<FormatType>().format(CharType()), comp, UserDefinedFormatTag());
+}
+}
+
+
+inline
+TimeComp getLocalTime(time_t utc)
+{
+ std::tm comp = {};
+ if (::localtime_r(&utc, &comp) == nullptr)
+ return TimeComp();
+
+ return implementation::toZenTimeComponents(comp);
+}
+
+
+inline
+TimeComp getUtcTime(time_t utc)
+{
+ std::tm comp = {};
+ if (::gmtime_r(&utc, &comp) == nullptr)
+ return TimeComp();
+
+ return implementation::toZenTimeComponents(comp);
+}
+
+
+inline
+time_t localToTimeT(const TimeComp& comp) //returns -1 on error
+{
+ std::tm ctc = implementation::toClibTimeComponents(comp);
+ return std::mktime(&ctc);
+}
+
+
+inline
+time_t utcToTimeT(const TimeComp& comp) //returns -1 on error
+{
+ std::tm ctc = implementation::toClibTimeComponents(comp);
+ ctc.tm_isdst = 0; //"Zero (0) to indicate that standard time is in effect" => unused by _mkgmtime, but take no chances
+ return ::timegm(&ctc);
+}
+
+
+template <class String, class String2> inline
+String formatTime(const String2& format, const TimeComp& comp)
+{
+ using FormatTag = typename SelectIf<
+ IsSameType<String2, FormatDateTag >::value ||
+ IsSameType<String2, FormatTimeTag >::value ||
+ IsSameType<String2, FormatDateTimeTag >::value ||
+ IsSameType<String2, FormatIsoDateTag >::value ||
+ IsSameType<String2, FormatIsoTimeTag >::value ||
+ IsSameType<String2, FormatIsoDateTimeTag>::value, implementation::PredefinedFormatTag, implementation::UserDefinedFormatTag>::Type;
+
+ return implementation::formatTime<String>(format, comp, FormatTag());
+}
+
+
+template <class String, class String2>
+bool parseTime(const String& format, const String2& str, TimeComp& comp) //return true on success
+{
+ using CharType = typename GetCharType<String>::Type;
+ static_assert(IsSameType<CharType, typename GetCharType<String2>::Type>::value, "");
+
+ const CharType* itFmt = strBegin(format);
+ const CharType* const fmtLast = itFmt + strLength(format);
+
+ const CharType* itStr = strBegin(str);
+ const CharType* const strLast = itStr + strLength(str);
+
+ auto extractNumber = [&](int& result, size_t digitCount) -> bool
+ {
+ if (strLast - itStr < makeSigned(digitCount))
+ return false;
+
+ if (std::any_of(itStr, itStr + digitCount, [](CharType c) { return !isDigit(c); }))
+ return false;
+
+ result = zen::stringTo<int>(StringRef<const CharType>(itStr, itStr + digitCount));
+ itStr += digitCount;
+ return true;
+ };
+
+ for (; itFmt != fmtLast; ++itFmt)
+ {
+ const CharType fmt = *itFmt;
+
+ if (fmt == '%')
+ {
+ ++itFmt;
+ if (itFmt == fmtLast)
+ return false;
+
+ switch (*itFmt)
+ {
+ case 'Y':
+ if (!extractNumber(comp.year, 4))
+ return false;
+ break;
+ case 'm':
+ if (!extractNumber(comp.month, 2))
+ return false;
+ break;
+ case 'd':
+ if (!extractNumber(comp.day, 2))
+ return false;
+ break;
+ case 'H':
+ if (!extractNumber(comp.hour, 2))
+ return false;
+ break;
+ case 'M':
+ if (!extractNumber(comp.minute, 2))
+ return false;
+ break;
+ case 'S':
+ if (!extractNumber(comp.second, 2))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ }
+ else if (isWhiteSpace(fmt)) //single whitespace in format => skip 0..n whitespace chars
+ {
+ while (itStr != strLast && isWhiteSpace(*itStr))
+ ++itStr;
+ }
+ else
+ {
+ if (itStr == strLast || *itStr != fmt)
+ return false;
+ ++itStr;
+ }
+ }
+
+ return itStr == strLast;
+}
+}
+
+#endif //TIME_H_8457092814324342453627
diff --git a/zen/type_tools.h b/zen/type_tools.h
index 082530ec..d0a62ea2 100755
--- a/zen/type_tools.h
+++ b/zen/type_tools.h
@@ -1,103 +1,103 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef TYPE_TOOLS_H_45237590734254545
-#define TYPE_TOOLS_H_45237590734254545
-
-#include "type_traits.h"
-
-
-namespace zen
-{
-//########## Strawman Classes ##########################
-struct NullType {}; //:= no type here
-
-//########## Type Mapping ##############################
-template <int n>
-struct Int2Type {};
-//------------------------------------------------------
-template <class T>
-struct Type2Type {};
-
-//########## Control Structures ########################
-template <bool flag, class T, class U>
-struct SelectIf : ResultType<T> {};
-
-template <class T, class U>
-struct SelectIf<false, T, U> : ResultType<U> {};
-//------------------------------------------------------
-template <class T, class U>
-struct IsSameType : FalseType {};
-
-template <class T>
-struct IsSameType<T, T> : TrueType {};
-
-//------------------------------------------------------
-template <bool, class T = void>
-struct EnableIf {};
-
-template <class T>
-struct EnableIf<true, T> : ResultType<T> {};
-//########## Type Cleanup ##############################
-template <class T>
-struct RemoveRef : ResultType<T> {};
-
-template <class T>
-struct RemoveRef<T&> : ResultType<T> {};
-
-template <class T>
-struct RemoveRef<T&&> : ResultType<T> {};
-//------------------------------------------------------
-template <class T>
-struct RemoveConst : ResultType<T> {};
-
-template <class T>
-struct RemoveConst<const T> : ResultType<T> {};
-//------------------------------------------------------
-template <class T>
-struct RemovePointer : ResultType<T> {};
-
-template <class T>
-struct RemovePointer<T*> : ResultType<T> {};
-//------------------------------------------------------
-template <class T>
-struct RemoveArray : ResultType<T> {};
-
-template <class T, int N>
-struct RemoveArray<T[N]> : ResultType<T> {};
-
-//########## Sorting ##############################
-/*
-Generate a descending binary predicate at compile time!
-
-Usage:
- static const bool ascending = ...
- makeSortDirection(old binary predicate, Int2Type<ascending>()) -> new binary predicate
-
-or directly;
- makeDescending(old binary predicate) -> new binary predicate
-*/
-
-template <class Predicate>
-struct LessDescending
-{
- LessDescending(Predicate lessThan) : lessThan_(lessThan) {}
- template <class T> bool operator()(const T& lhs, const T& rhs) const { return lessThan_(rhs, lhs); }
-private:
- Predicate lessThan_;
-};
-
-template <class Predicate> inline
-/**/ Predicate makeSortDirection(Predicate pred, Int2Type<true>) { return pred; }
-
-template <class Predicate> inline
-LessDescending<Predicate> makeSortDirection(Predicate pred, Int2Type<false>) { return pred; }
-
-template <class Predicate> inline
-LessDescending<Predicate> makeDescending(Predicate pred) { return pred; }
-}
-
-#endif //TYPE_TOOLS_H_45237590734254545
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef TYPE_TOOLS_H_45237590734254545
+#define TYPE_TOOLS_H_45237590734254545
+
+#include "type_traits.h"
+
+
+namespace zen
+{
+//########## Strawman Classes ##########################
+struct NullType {}; //:= no type here
+
+//########## Type Mapping ##############################
+template <int n>
+struct Int2Type {};
+//------------------------------------------------------
+template <class T>
+struct Type2Type {};
+
+//########## Control Structures ########################
+template <bool flag, class T, class U>
+struct SelectIf : ResultType<T> {};
+
+template <class T, class U>
+struct SelectIf<false, T, U> : ResultType<U> {};
+//------------------------------------------------------
+template <class T, class U>
+struct IsSameType : FalseType {};
+
+template <class T>
+struct IsSameType<T, T> : TrueType {};
+
+//------------------------------------------------------
+template <bool, class T = void>
+struct EnableIf {};
+
+template <class T>
+struct EnableIf<true, T> : ResultType<T> {};
+//########## Type Cleanup ##############################
+template <class T>
+struct RemoveRef : ResultType<T> {};
+
+template <class T>
+struct RemoveRef<T&> : ResultType<T> {};
+
+template <class T>
+struct RemoveRef<T&&> : ResultType<T> {};
+//------------------------------------------------------
+template <class T>
+struct RemoveConst : ResultType<T> {};
+
+template <class T>
+struct RemoveConst<const T> : ResultType<T> {};
+//------------------------------------------------------
+template <class T>
+struct RemovePointer : ResultType<T> {};
+
+template <class T>
+struct RemovePointer<T*> : ResultType<T> {};
+//------------------------------------------------------
+template <class T>
+struct RemoveArray : ResultType<T> {};
+
+template <class T, int N>
+struct RemoveArray<T[N]> : ResultType<T> {};
+
+//########## Sorting ##############################
+/*
+Generate a descending binary predicate at compile time!
+
+Usage:
+ static const bool ascending = ...
+ makeSortDirection(old binary predicate, Int2Type<ascending>()) -> new binary predicate
+
+or directly;
+ makeDescending(old binary predicate) -> new binary predicate
+*/
+
+template <class Predicate>
+struct LessDescending
+{
+ LessDescending(Predicate lessThan) : lessThan_(lessThan) {}
+ template <class T> bool operator()(const T& lhs, const T& rhs) const { return lessThan_(rhs, lhs); }
+private:
+ Predicate lessThan_;
+};
+
+template <class Predicate> inline
+/**/ Predicate makeSortDirection(Predicate pred, Int2Type<true>) { return pred; }
+
+template <class Predicate> inline
+LessDescending<Predicate> makeSortDirection(Predicate pred, Int2Type<false>) { return pred; }
+
+template <class Predicate> inline
+LessDescending<Predicate> makeDescending(Predicate pred) { return pred; }
+}
+
+#endif //TYPE_TOOLS_H_45237590734254545
diff --git a/zen/type_traits.h b/zen/type_traits.h
index f4e5cebd..917b3258 100755
--- a/zen/type_traits.h
+++ b/zen/type_traits.h
@@ -1,195 +1,195 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef TYPE_TRAITS_H_3425628658765467
-#define TYPE_TRAITS_H_3425628658765467
-
-#include <type_traits> //all we need is std::is_class!!
-
-
-namespace zen
-{
-//################# TMP compile time return values: "inherit to return compile-time result" ##############
-template <int i>
-struct StaticInt
-{
- enum { value = i };
-};
-
-template <bool b>
-struct StaticBool : StaticInt<b> {};
-
-using TrueType = StaticBool<true>;
-using FalseType = StaticBool<false>;
-
-template <class EnumType, EnumType val>
-struct StaticEnum
-{
- static const EnumType value = val;
-};
-//---------------------------------------------------------
-template <class T>
-struct ResultType
-{
- using Type = T;
-};
-
-//Herb Sutter's signedness conversion helpers: http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/
-template<class T> inline auto makeSigned (T t) { return static_cast<std::make_signed_t <T>>(t); }
-template<class T> inline auto makeUnsigned(T t) { return static_cast<std::make_unsigned_t<T>>(t); }
-
-//################# Built-in Types ########################
-//Example: "IsSignedInt<int>::value" evaluates to "true"
-
-template <class T> struct IsUnsignedInt;
-template <class T> struct IsSignedInt;
-template <class T> struct IsFloat;
-
-template <class T> struct IsInteger; //IsSignedInt or IsUnsignedInt
-template <class T> struct IsArithmetic; //IsInteger or IsFloat
-//remaining non-arithmetic types: bool, char, wchar_t
-
-//optional: specialize new types like:
-//template <> struct IsUnsignedInt<UInt64> : TrueType {};
-
-//################# Class Members ########################
-
-/* Detect data or function members of a class by name: ZEN_INIT_DETECT_MEMBER + HasMember_
- Example: 1. ZEN_INIT_DETECT_MEMBER(c_str);
- 2. HasMember_c_str<T>::value -> use boolean
-*/
-
-/* Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_
-
- Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const);
- 2. HasMember_size<T>::value -> use as boolean
-*/
-
-/* Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_
-
- Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
- 2. HasMemberType_value_type<T>::value -> use as boolean
-*/
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-//################ implementation ######################
-#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : TrueType {};
-
-template <class T>
-struct IsUnsignedInt : FalseType {};
-
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned char);
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned short int);
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned int);
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long int);
-ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long long int); //new with C++11 - same type as unsigned __int64 in VS2010
-//------------------------------------------------------
-
-template <class T>
-struct IsSignedInt : FalseType {};
-
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, signed char);
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, short int);
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, int);
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, long int);
-ZEN_SPECIALIZE_TRAIT(IsSignedInt, long long int); //new with C++11 - same type as __int64 in VS2010
-//------------------------------------------------------
-
-template <class T>
-struct IsFloat : FalseType {};
-
-ZEN_SPECIALIZE_TRAIT(IsFloat, float);
-ZEN_SPECIALIZE_TRAIT(IsFloat, double);
-ZEN_SPECIALIZE_TRAIT(IsFloat, long double);
-//------------------------------------------------------
-
-#undef ZEN_SPECIALIZE_TRAIT
-
-template <class T>
-struct IsInteger : StaticBool<IsUnsignedInt<T>::value || IsSignedInt<T>::value> {};
-
-template <class T>
-struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
-//####################################################################
-
-#define ZEN_INIT_DETECT_MEMBER(NAME) \
- \
- template<bool isClass, class T> \
- struct HasMemberImpl_##NAME \
- { \
- private: \
- using Yes = char[1]; \
- using No = char[2]; \
- \
- template <typename U, U t> \
- class Helper {}; \
- struct Fallback { int NAME; }; \
- \
- template <class U> \
- struct Helper2 : public U, public Fallback {}; /*this works only for class types!!!*/ \
- \
- template <class U> static No& hasMember(Helper<int Fallback::*, &Helper2<U>::NAME>*); \
- template <class U> static Yes& hasMember(...); \
- public: \
- enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \
- }; \
- \
- template<class T> \
- struct HasMemberImpl_##NAME<false, T> : FalseType {}; \
- \
- template<typename T> \
- struct HasMember_##NAME : StaticBool<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {};
-
-//####################################################################
-
-#define ZEN_INIT_DETECT_MEMBER2(NAME, TYPE) \
- \
- template<typename U> \
- class HasMember_##NAME \
- { \
- using Yes = char[1]; \
- using No = char[2]; \
- \
- template <typename T, T t> class Helper {}; \
- \
- template <class T> static Yes& hasMember(Helper<TYPE, &T::NAME>*); \
- template <class T> static No& hasMember(...); \
- public: \
- enum { value = sizeof(hasMember<U>(nullptr)) == sizeof(Yes) }; \
- };
-//####################################################################
-
-#define ZEN_INIT_DETECT_MEMBER_TYPE(TYPENAME) \
- \
- template<typename T> \
- class HasMemberType_##TYPENAME \
- { \
- using Yes = char[1]; \
- using No = char[2]; \
- \
- template <typename U> class Helper {}; \
- \
- template <class U> static Yes& hasMemberType(Helper<typename U::TYPENAME>*); \
- template <class U> static No& hasMemberType(...); \
- public: \
- enum { value = sizeof(hasMemberType<T>(nullptr)) == sizeof(Yes) }; \
- };
-}
-
-#endif //TYPE_TRAITS_H_3425628658765467
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef TYPE_TRAITS_H_3425628658765467
+#define TYPE_TRAITS_H_3425628658765467
+
+#include <type_traits> //all we need is std::is_class!!
+
+
+namespace zen
+{
+//################# TMP compile time return values: "inherit to return compile-time result" ##############
+template <int i>
+struct StaticInt
+{
+ enum { value = i };
+};
+
+template <bool b>
+struct StaticBool : StaticInt<b> {};
+
+using TrueType = StaticBool<true>;
+using FalseType = StaticBool<false>;
+
+template <class EnumType, EnumType val>
+struct StaticEnum
+{
+ static const EnumType value = val;
+};
+//---------------------------------------------------------
+template <class T>
+struct ResultType
+{
+ using Type = T;
+};
+
+//Herb Sutter's signedness conversion helpers: http://herbsutter.com/2013/06/13/gotw-93-solution-auto-variables-part-2/
+template<class T> inline auto makeSigned (T t) { return static_cast<std::make_signed_t <T>>(t); }
+template<class T> inline auto makeUnsigned(T t) { return static_cast<std::make_unsigned_t<T>>(t); }
+
+//################# Built-in Types ########################
+//Example: "IsSignedInt<int>::value" evaluates to "true"
+
+template <class T> struct IsUnsignedInt;
+template <class T> struct IsSignedInt;
+template <class T> struct IsFloat;
+
+template <class T> struct IsInteger; //IsSignedInt or IsUnsignedInt
+template <class T> struct IsArithmetic; //IsInteger or IsFloat
+//remaining non-arithmetic types: bool, char, wchar_t
+
+//optional: specialize new types like:
+//template <> struct IsUnsignedInt<UInt64> : TrueType {};
+
+//################# Class Members ########################
+
+/* Detect data or function members of a class by name: ZEN_INIT_DETECT_MEMBER + HasMember_
+ Example: 1. ZEN_INIT_DETECT_MEMBER(c_str);
+ 2. HasMember_c_str<T>::value -> use boolean
+*/
+
+/* Detect data or function members of a class by name *and* type: ZEN_INIT_DETECT_MEMBER2 + HasMember_
+
+ Example: 1. ZEN_INIT_DETECT_MEMBER2(size, size_t (T::*)() const);
+ 2. HasMember_size<T>::value -> use as boolean
+*/
+
+/* Detect member type of a class: ZEN_INIT_DETECT_MEMBER_TYPE + HasMemberType_
+
+ Example: 1. ZEN_INIT_DETECT_MEMBER_TYPE(value_type);
+ 2. HasMemberType_value_type<T>::value -> use as boolean
+*/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+//################ implementation ######################
+#define ZEN_SPECIALIZE_TRAIT(X, Y) template <> struct X<Y> : TrueType {};
+
+template <class T>
+struct IsUnsignedInt : FalseType {};
+
+ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned char);
+ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned short int);
+ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned int);
+ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long int);
+ZEN_SPECIALIZE_TRAIT(IsUnsignedInt, unsigned long long int); //new with C++11 - same type as unsigned __int64 in VS2010
+//------------------------------------------------------
+
+template <class T>
+struct IsSignedInt : FalseType {};
+
+ZEN_SPECIALIZE_TRAIT(IsSignedInt, signed char);
+ZEN_SPECIALIZE_TRAIT(IsSignedInt, short int);
+ZEN_SPECIALIZE_TRAIT(IsSignedInt, int);
+ZEN_SPECIALIZE_TRAIT(IsSignedInt, long int);
+ZEN_SPECIALIZE_TRAIT(IsSignedInt, long long int); //new with C++11 - same type as __int64 in VS2010
+//------------------------------------------------------
+
+template <class T>
+struct IsFloat : FalseType {};
+
+ZEN_SPECIALIZE_TRAIT(IsFloat, float);
+ZEN_SPECIALIZE_TRAIT(IsFloat, double);
+ZEN_SPECIALIZE_TRAIT(IsFloat, long double);
+//------------------------------------------------------
+
+#undef ZEN_SPECIALIZE_TRAIT
+
+template <class T>
+struct IsInteger : StaticBool<IsUnsignedInt<T>::value || IsSignedInt<T>::value> {};
+
+template <class T>
+struct IsArithmetic : StaticBool<IsInteger<T>::value || IsFloat<T>::value> {};
+//####################################################################
+
+#define ZEN_INIT_DETECT_MEMBER(NAME) \
+ \
+ template<bool isClass, class T> \
+ struct HasMemberImpl_##NAME \
+ { \
+ private: \
+ using Yes = char[1]; \
+ using No = char[2]; \
+ \
+ template <typename U, U t> \
+ class Helper {}; \
+ struct Fallback { int NAME; }; \
+ \
+ template <class U> \
+ struct Helper2 : public U, public Fallback {}; /*this works only for class types!!!*/ \
+ \
+ template <class U> static No& hasMember(Helper<int Fallback::*, &Helper2<U>::NAME>*); \
+ template <class U> static Yes& hasMember(...); \
+ public: \
+ enum { value = sizeof(hasMember<T>(nullptr)) == sizeof(Yes) }; \
+ }; \
+ \
+ template<class T> \
+ struct HasMemberImpl_##NAME<false, T> : FalseType {}; \
+ \
+ template<typename T> \
+ struct HasMember_##NAME : StaticBool<HasMemberImpl_##NAME<std::is_class<T>::value, T>::value> {};
+
+//####################################################################
+
+#define ZEN_INIT_DETECT_MEMBER2(NAME, TYPE) \
+ \
+ template<typename U> \
+ class HasMember_##NAME \
+ { \
+ using Yes = char[1]; \
+ using No = char[2]; \
+ \
+ template <typename T, T t> class Helper {}; \
+ \
+ template <class T> static Yes& hasMember(Helper<TYPE, &T::NAME>*); \
+ template <class T> static No& hasMember(...); \
+ public: \
+ enum { value = sizeof(hasMember<U>(nullptr)) == sizeof(Yes) }; \
+ };
+//####################################################################
+
+#define ZEN_INIT_DETECT_MEMBER_TYPE(TYPENAME) \
+ \
+ template<typename T> \
+ class HasMemberType_##TYPENAME \
+ { \
+ using Yes = char[1]; \
+ using No = char[2]; \
+ \
+ template <typename U> class Helper {}; \
+ \
+ template <class U> static Yes& hasMemberType(Helper<typename U::TYPENAME>*); \
+ template <class U> static No& hasMemberType(...); \
+ public: \
+ enum { value = sizeof(hasMemberType<T>(nullptr)) == sizeof(Yes) }; \
+ };
+}
+
+#endif //TYPE_TRAITS_H_3425628658765467
diff --git a/zen/utf.h b/zen/utf.h
index ab8fda50..bc1e3270 100755
--- a/zen/utf.h
+++ b/zen/utf.h
@@ -1,387 +1,387 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef UTF_H_01832479146991573473545
-#define UTF_H_01832479146991573473545
-
-#include <cstdint>
-#include <iterator>
-#include "string_tools.h" //copyStringTo
-#include "optional.h"
-
-namespace zen
-{
-//convert all(!) char- and wchar_t-based "string-like" objects applying a UTF8 conversions (but only if necessary!)
-template <class TargetString, class SourceString>
-TargetString utfTo(const SourceString& str);
-
-const char BYTE_ORDER_MARK_UTF8[] = "\xEF\xBB\xBF";
-
-template <class UtfString>
-bool isValidUtf(const UtfString& str); //check for UTF-8 encoding errors
-
-//access unicode characters in UTF-encoded string (char- or wchar_t-based)
-template <class UtfString>
-size_t unicodeLength(const UtfString& str); //return number of code points for UTF-encoded string
-
-template <class UtfString>
-UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t uniPosLast);
-
-
-
-
-
-
-
-
-
-//----------------------- implementation ----------------------------------
-namespace implementation
-{
-using CodePoint = uint32_t;
-using Char16 = uint16_t;
-using Char8 = uint8_t;
-
-const CodePoint LEAD_SURROGATE = 0xd800;
-const CodePoint TRAIL_SURROGATE = 0xdc00; //== LEAD_SURROGATE_MAX + 1
-const CodePoint TRAIL_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
- if (cp < LEAD_SURROGATE)
- writeOutput(static_cast<Char16>(cp));
- 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>( LEAD_SURROGATE + (cp >> 10)));
- writeOutput(static_cast<Char16>(TRAIL_SURROGATE + (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! returns 0 on error!
-{
- 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
- return 1;
-}
-
-
-class Utf16Decoder
-{
-public:
- Utf16Decoder(const Char16* str, size_t len) : it_(str), last_(str + len) {}
-
- Opt<CodePoint> getNext()
- {
- if (it_ == last_)
- return NoValue();
-
- const Char16 ch = *it_++;
- CodePoint cp = ch;
- switch (getUtf16Len(ch))
- {
- case 0: //invalid utf16 character
- cp = REPLACEMENT_CHAR;
- break;
- case 1:
- break;
- case 2:
- decodeTrail(cp);
- break;
- }
- return cp;
- }
-
-private:
- void decodeTrail(CodePoint& cp)
- {
- if (it_ != last_) //trail surrogate expected!
- {
- const Char16 ch = *it_;
- if (TRAIL_SURROGATE <= ch && ch <= TRAIL_SURROGATE_MAX) //trail surrogate expected!
- {
- cp = ((cp - LEAD_SURROGATE) << 10) + (ch - TRAIL_SURROGATE) + 0x10000;
- ++it_;
- return;
- }
- }
- cp = REPLACEMENT_CHAR;
- }
-
- const Char16* it_;
- const Char16* const last_;
-};
-
-//----------------------------------------------------------------------------------------------------------------
-
-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));
- else if (cp < 0x800)
- {
- writeOutput(static_cast<Char8>((cp >> 6 ) | 0xc0));
- writeOutput(static_cast<Char8>((cp & 0x3f) | 0x80));
- }
- else if (cp < 0x10000)
- {
- writeOutput(static_cast<Char8>( (cp >> 12 ) | 0xe0));
- writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80));
- writeOutput(static_cast<Char8>( (cp & 0x3f) | 0x80));
- }
- else if (cp <= CODE_POINT_MAX)
- {
- 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));
- }
- else //invalid code point
- codePointToUtf8(REPLACEMENT_CHAR, writeOutput); //resolves to 3-byte utf8
-}
-
-
-inline
-size_t getUtf8Len(Char8 ch) //ch must be first code unit! returns 0 on error!
-{
- if (ch < 0x80)
- return 1;
- if (ch >> 5 == 0x6)
- return 2;
- if (ch >> 4 == 0xe)
- return 3;
- if (ch >> 3 == 0x1e)
- return 4;
- return 0; //innvalid begin of UTF8 encoding
-}
-
-
-class Utf8Decoder
-{
-public:
- Utf8Decoder(const Char8* str, size_t len) : it_(str), last_(str + len) {}
-
- Opt<CodePoint> getNext()
- {
- if (it_ == last_)
- return NoValue();
-
- const Char8 ch = *it_++;
- CodePoint cp = ch;
- switch (getUtf8Len(ch))
- {
- case 0: //invalid utf8 character
- cp = REPLACEMENT_CHAR;
- break;
- case 1:
- break;
- case 2:
- cp &= 0x1f;
- decodeTrail(cp);
- break;
- case 3:
- cp &= 0xf;
- if (decodeTrail(cp))
- decodeTrail(cp);
- break;
- case 4:
- cp &= 0x7;
- if (decodeTrail(cp))
- if (decodeTrail(cp))
- decodeTrail(cp);
- if (cp > CODE_POINT_MAX) cp = REPLACEMENT_CHAR;
- break;
- }
- return cp;
- }
-
-private:
- bool decodeTrail(CodePoint& cp)
- {
- if (it_ != last_) //trail surrogate expected!
- {
- const Char8 ch = *it_;
- if (ch >> 6 == 0x2) //trail surrogate expected!
- {
- cp = (cp << 6) + (ch & 0x3f);
- ++it_;
- return true;
- }
- }
- cp = REPLACEMENT_CHAR;
- return false;
- }
-
- const Char8* it_;
- const Char8* const last_;
-};
-
-//----------------------------------------------------------------------------------------------------------------
-
-template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<1>) { codePointToUtf8 (cp, writeOutput); } //UTF8-char
-template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<2>) { codePointToUtf16(cp, writeOutput); } //Windows: UTF16-wchar_t
-template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<4>) { writeOutput(cp); } //other OS: UTF32-wchar_t
-
-template <class CharType, class Function> inline
-void codePointToUtf(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a CharType
-{
- return codePointToUtf(cp, writeOutput, Int2Type<sizeof(CharType)>());
-}
-
-//----------------------------------------------------------------------------------------------------------------
-
-template <class CharType, int charSize>
-class UtfDecoderImpl;
-
-
-template <class CharType>
-class UtfDecoderImpl<CharType, 1> //UTF8-char
-{
-public:
- UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast<const Char8*>(str), len) {}
- Opt<CodePoint> getNext() { return decoder_.getNext(); }
-private:
- Utf8Decoder decoder_;
-};
-
-
-template <class CharType>
-class UtfDecoderImpl<CharType, 2> //Windows: UTF16-wchar_t
-{
-public:
- UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast<const Char16*>(str), len) {}
- Opt<CodePoint> getNext() { return decoder_.getNext(); }
-private:
- Utf16Decoder decoder_;
-};
-
-
-template <class CharType>
-class UtfDecoderImpl<CharType, 4> //other OS: UTF32-wchar_t
-{
-public:
- UtfDecoderImpl(const CharType* str, size_t len) : it_(reinterpret_cast<const CodePoint*>(str)), last_(it_ + len) {}
- Opt<CodePoint> getNext()
- {
- if (it_ == last_)
- return NoValue();
- return *it_++;
- }
-private:
- const CodePoint* it_;
- const CodePoint* last_;
-};
-
-
-template <class CharType>
-using UtfDecoder = UtfDecoderImpl<CharType, sizeof(CharType)>;
-}
-
-//-------------------------------------------------------------------------------------------
-
-template <class UtfString> inline
-bool isValidUtf(const UtfString& str)
-{
- using namespace implementation;
-
- UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str));
- while (Opt<CodePoint> cp = decoder.getNext())
- if (*cp == REPLACEMENT_CHAR)
- return false;
-
- return true;
-}
-
-
-template <class UtfString> inline
-size_t unicodeLength(const UtfString& str) //return number of code points (+ correctly handle broken UTF encoding)
-{
- size_t uniLen = 0;
- implementation::UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str));
- while (decoder.getNext())
- ++uniLen;
- return uniLen;
-}
-
-
-template <class UtfString> inline
-UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t uniPosLast) //return position of unicode char in UTF-encoded string
-{
- assert(uniPosFirst <= uniPosLast && uniPosLast <= unicodeLength(str));
- using namespace implementation;
- using CharType = typename GetCharType<UtfString>::Type;
- UtfString output;
- if (uniPosFirst >= uniPosLast) //optimize for empty range
- return output;
-
- UtfDecoder<CharType> decoder(strBegin(str), strLength(str));
- for (size_t uniPos = 0; Opt<CodePoint> cp = decoder.getNext(); ++uniPos) //[!] declaration in condition part of the for-loop
- if (uniPosFirst <= uniPos)
- {
- if (uniPos >= uniPosLast)
- break;
- codePointToUtf<CharType>(*cp, [&](CharType c) { output += c; });
- }
- return output;
-}
-
-//-------------------------------------------------------------------------------------------
-
-namespace implementation
-{
-template <class TargetString, class SourceString> inline
-TargetString utfTo(const SourceString& str, FalseType)
-{
- using CharSrc = typename GetCharType<SourceString>::Type;
- using CharTrg = typename GetCharType<TargetString>::Type;
- static_assert(sizeof(CharSrc) != sizeof(CharTrg), "no UTF-conversion needed");
-
- TargetString output;
-
- UtfDecoder<CharSrc> decoder(strBegin(str), strLength(str));
- while (Opt<CodePoint> cp = decoder.getNext())
- codePointToUtf<CharTrg>(*cp, [&](CharTrg c) { output += c; });
-
- return output;
-}
-
-
-template <class TargetString, class SourceString> inline
-TargetString utfTo(const SourceString& str, TrueType) { return copyStringTo<TargetString>(str); }
-}
-
-
-template <class TargetString, class SourceString> inline
-TargetString utfTo(const SourceString& str)
-{
- return implementation::utfTo<TargetString>(str, StaticBool<sizeof(typename GetCharType<SourceString>::Type) == sizeof(typename GetCharType<TargetString>::Type)>());
-}
-}
-
-#endif //UTF_H_01832479146991573473545
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef UTF_H_01832479146991573473545
+#define UTF_H_01832479146991573473545
+
+#include <cstdint>
+#include <iterator>
+#include "string_tools.h" //copyStringTo
+#include "optional.h"
+
+namespace zen
+{
+//convert all(!) char- and wchar_t-based "string-like" objects applying a UTF8 conversions (but only if necessary!)
+template <class TargetString, class SourceString>
+TargetString utfTo(const SourceString& str);
+
+const char BYTE_ORDER_MARK_UTF8[] = "\xEF\xBB\xBF";
+
+template <class UtfString>
+bool isValidUtf(const UtfString& str); //check for UTF-8 encoding errors
+
+//access unicode characters in UTF-encoded string (char- or wchar_t-based)
+template <class UtfString>
+size_t unicodeLength(const UtfString& str); //return number of code points for UTF-encoded string
+
+template <class UtfString>
+UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t uniPosLast);
+
+
+
+
+
+
+
+
+
+//----------------------- implementation ----------------------------------
+namespace implementation
+{
+using CodePoint = uint32_t;
+using Char16 = uint16_t;
+using Char8 = uint8_t;
+
+const CodePoint LEAD_SURROGATE = 0xd800;
+const CodePoint TRAIL_SURROGATE = 0xdc00; //== LEAD_SURROGATE_MAX + 1
+const CodePoint TRAIL_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
+ if (cp < LEAD_SURROGATE)
+ writeOutput(static_cast<Char16>(cp));
+ 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>( LEAD_SURROGATE + (cp >> 10)));
+ writeOutput(static_cast<Char16>(TRAIL_SURROGATE + (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! returns 0 on error!
+{
+ 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
+ return 1;
+}
+
+
+class Utf16Decoder
+{
+public:
+ Utf16Decoder(const Char16* str, size_t len) : it_(str), last_(str + len) {}
+
+ Opt<CodePoint> getNext()
+ {
+ if (it_ == last_)
+ return NoValue();
+
+ const Char16 ch = *it_++;
+ CodePoint cp = ch;
+ switch (getUtf16Len(ch))
+ {
+ case 0: //invalid utf16 character
+ cp = REPLACEMENT_CHAR;
+ break;
+ case 1:
+ break;
+ case 2:
+ decodeTrail(cp);
+ break;
+ }
+ return cp;
+ }
+
+private:
+ void decodeTrail(CodePoint& cp)
+ {
+ if (it_ != last_) //trail surrogate expected!
+ {
+ const Char16 ch = *it_;
+ if (TRAIL_SURROGATE <= ch && ch <= TRAIL_SURROGATE_MAX) //trail surrogate expected!
+ {
+ cp = ((cp - LEAD_SURROGATE) << 10) + (ch - TRAIL_SURROGATE) + 0x10000;
+ ++it_;
+ return;
+ }
+ }
+ cp = REPLACEMENT_CHAR;
+ }
+
+ const Char16* it_;
+ const Char16* const last_;
+};
+
+//----------------------------------------------------------------------------------------------------------------
+
+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));
+ else if (cp < 0x800)
+ {
+ writeOutput(static_cast<Char8>((cp >> 6 ) | 0xc0));
+ writeOutput(static_cast<Char8>((cp & 0x3f) | 0x80));
+ }
+ else if (cp < 0x10000)
+ {
+ writeOutput(static_cast<Char8>( (cp >> 12 ) | 0xe0));
+ writeOutput(static_cast<Char8>(((cp >> 6) & 0x3f) | 0x80));
+ writeOutput(static_cast<Char8>( (cp & 0x3f) | 0x80));
+ }
+ else if (cp <= CODE_POINT_MAX)
+ {
+ 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));
+ }
+ else //invalid code point
+ codePointToUtf8(REPLACEMENT_CHAR, writeOutput); //resolves to 3-byte utf8
+}
+
+
+inline
+size_t getUtf8Len(Char8 ch) //ch must be first code unit! returns 0 on error!
+{
+ if (ch < 0x80)
+ return 1;
+ if (ch >> 5 == 0x6)
+ return 2;
+ if (ch >> 4 == 0xe)
+ return 3;
+ if (ch >> 3 == 0x1e)
+ return 4;
+ return 0; //innvalid begin of UTF8 encoding
+}
+
+
+class Utf8Decoder
+{
+public:
+ Utf8Decoder(const Char8* str, size_t len) : it_(str), last_(str + len) {}
+
+ Opt<CodePoint> getNext()
+ {
+ if (it_ == last_)
+ return NoValue();
+
+ const Char8 ch = *it_++;
+ CodePoint cp = ch;
+ switch (getUtf8Len(ch))
+ {
+ case 0: //invalid utf8 character
+ cp = REPLACEMENT_CHAR;
+ break;
+ case 1:
+ break;
+ case 2:
+ cp &= 0x1f;
+ decodeTrail(cp);
+ break;
+ case 3:
+ cp &= 0xf;
+ if (decodeTrail(cp))
+ decodeTrail(cp);
+ break;
+ case 4:
+ cp &= 0x7;
+ if (decodeTrail(cp))
+ if (decodeTrail(cp))
+ decodeTrail(cp);
+ if (cp > CODE_POINT_MAX) cp = REPLACEMENT_CHAR;
+ break;
+ }
+ return cp;
+ }
+
+private:
+ bool decodeTrail(CodePoint& cp)
+ {
+ if (it_ != last_) //trail surrogate expected!
+ {
+ const Char8 ch = *it_;
+ if (ch >> 6 == 0x2) //trail surrogate expected!
+ {
+ cp = (cp << 6) + (ch & 0x3f);
+ ++it_;
+ return true;
+ }
+ }
+ cp = REPLACEMENT_CHAR;
+ return false;
+ }
+
+ const Char8* it_;
+ const Char8* const last_;
+};
+
+//----------------------------------------------------------------------------------------------------------------
+
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<1>) { codePointToUtf8 (cp, writeOutput); } //UTF8-char
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<2>) { codePointToUtf16(cp, writeOutput); } //Windows: UTF16-wchar_t
+template <class Function> inline void codePointToUtf(CodePoint cp, Function writeOutput, Int2Type<4>) { writeOutput(cp); } //other OS: UTF32-wchar_t
+
+template <class CharType, class Function> inline
+void codePointToUtf(CodePoint cp, Function writeOutput) //"writeOutput" is a unary function taking a CharType
+{
+ return codePointToUtf(cp, writeOutput, Int2Type<sizeof(CharType)>());
+}
+
+//----------------------------------------------------------------------------------------------------------------
+
+template <class CharType, int charSize>
+class UtfDecoderImpl;
+
+
+template <class CharType>
+class UtfDecoderImpl<CharType, 1> //UTF8-char
+{
+public:
+ UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast<const Char8*>(str), len) {}
+ Opt<CodePoint> getNext() { return decoder_.getNext(); }
+private:
+ Utf8Decoder decoder_;
+};
+
+
+template <class CharType>
+class UtfDecoderImpl<CharType, 2> //Windows: UTF16-wchar_t
+{
+public:
+ UtfDecoderImpl(const CharType* str, size_t len) : decoder_(reinterpret_cast<const Char16*>(str), len) {}
+ Opt<CodePoint> getNext() { return decoder_.getNext(); }
+private:
+ Utf16Decoder decoder_;
+};
+
+
+template <class CharType>
+class UtfDecoderImpl<CharType, 4> //other OS: UTF32-wchar_t
+{
+public:
+ UtfDecoderImpl(const CharType* str, size_t len) : it_(reinterpret_cast<const CodePoint*>(str)), last_(it_ + len) {}
+ Opt<CodePoint> getNext()
+ {
+ if (it_ == last_)
+ return NoValue();
+ return *it_++;
+ }
+private:
+ const CodePoint* it_;
+ const CodePoint* last_;
+};
+
+
+template <class CharType>
+using UtfDecoder = UtfDecoderImpl<CharType, sizeof(CharType)>;
+}
+
+//-------------------------------------------------------------------------------------------
+
+template <class UtfString> inline
+bool isValidUtf(const UtfString& str)
+{
+ using namespace implementation;
+
+ UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str));
+ while (Opt<CodePoint> cp = decoder.getNext())
+ if (*cp == REPLACEMENT_CHAR)
+ return false;
+
+ return true;
+}
+
+
+template <class UtfString> inline
+size_t unicodeLength(const UtfString& str) //return number of code points (+ correctly handle broken UTF encoding)
+{
+ size_t uniLen = 0;
+ implementation::UtfDecoder<typename GetCharType<UtfString>::Type> decoder(strBegin(str), strLength(str));
+ while (decoder.getNext())
+ ++uniLen;
+ return uniLen;
+}
+
+
+template <class UtfString> inline
+UtfString getUnicodeSubstring(const UtfString& str, size_t uniPosFirst, size_t uniPosLast) //return position of unicode char in UTF-encoded string
+{
+ assert(uniPosFirst <= uniPosLast && uniPosLast <= unicodeLength(str));
+ using namespace implementation;
+ using CharType = typename GetCharType<UtfString>::Type;
+ UtfString output;
+ if (uniPosFirst >= uniPosLast) //optimize for empty range
+ return output;
+
+ UtfDecoder<CharType> decoder(strBegin(str), strLength(str));
+ for (size_t uniPos = 0; Opt<CodePoint> cp = decoder.getNext(); ++uniPos) //[!] declaration in condition part of the for-loop
+ if (uniPosFirst <= uniPos)
+ {
+ if (uniPos >= uniPosLast)
+ break;
+ codePointToUtf<CharType>(*cp, [&](CharType c) { output += c; });
+ }
+ return output;
+}
+
+//-------------------------------------------------------------------------------------------
+
+namespace implementation
+{
+template <class TargetString, class SourceString> inline
+TargetString utfTo(const SourceString& str, FalseType)
+{
+ using CharSrc = typename GetCharType<SourceString>::Type;
+ using CharTrg = typename GetCharType<TargetString>::Type;
+ static_assert(sizeof(CharSrc) != sizeof(CharTrg), "no UTF-conversion needed");
+
+ TargetString output;
+
+ UtfDecoder<CharSrc> decoder(strBegin(str), strLength(str));
+ while (Opt<CodePoint> cp = decoder.getNext())
+ codePointToUtf<CharTrg>(*cp, [&](CharTrg c) { output += c; });
+
+ return output;
+}
+
+
+template <class TargetString, class SourceString> inline
+TargetString utfTo(const SourceString& str, TrueType) { return copyStringTo<TargetString>(str); }
+}
+
+
+template <class TargetString, class SourceString> inline
+TargetString utfTo(const SourceString& str)
+{
+ return implementation::utfTo<TargetString>(str, StaticBool<sizeof(typename GetCharType<SourceString>::Type) == sizeof(typename GetCharType<TargetString>::Type)>());
+}
+}
+
+#endif //UTF_H_01832479146991573473545
diff --git a/zen/warn_static.h b/zen/warn_static.h
index 74dbd627..c2232f74 100755
--- a/zen/warn_static.h
+++ b/zen/warn_static.h
@@ -1,24 +1,24 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef WARN_STATIC_H_08724567834560832745
-#define WARN_STATIC_H_08724567834560832745
-
-/*
-Portable Compile-Time Warning
------------------------------
-Usage:
- warn_static("my message")
-*/
-
-#define STATIC_WARNING_CONCAT_SUB(X, Y) X ## Y
-#define STATIC_WARNING_CONCAT(X, Y) STATIC_WARNING_CONCAT_SUB(X, Y)
-
-#define warn_static(TXT) \
- typedef int STATIC_WARNING_87903124 __attribute__ ((deprecated)); \
- enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) };
-
-#endif //WARN_STATIC_H_08724567834560832745
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef WARN_STATIC_H_08724567834560832745
+#define WARN_STATIC_H_08724567834560832745
+
+/*
+Portable Compile-Time Warning
+-----------------------------
+Usage:
+ warn_static("my message")
+*/
+
+#define STATIC_WARNING_CONCAT_SUB(X, Y) X ## Y
+#define STATIC_WARNING_CONCAT(X, Y) STATIC_WARNING_CONCAT_SUB(X, Y)
+
+#define warn_static(TXT) \
+ typedef int STATIC_WARNING_87903124 __attribute__ ((deprecated)); \
+ enum { STATIC_WARNING_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING_87903124) };
+
+#endif //WARN_STATIC_H_08724567834560832745
diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp
index d3d59200..0a1c8505 100755
--- a/zen/xml_io.cpp
+++ b/zen/xml_io.cpp
@@ -1,83 +1,83 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "xml_io.h"
-#include "file_access.h"
-#include "file_io.h"
-
-using namespace zen;
-
-
-XmlDoc zen::loadXmlDocument(const Zstring& filePath) //throw FileError
-{
- FileInput fileIn(filePath, nullptr /*notifyUnbufferedIO*/); //throw FileError, ErrorFileLocked
- const size_t blockSize = fileIn.getBlockSize();
- const std::string xmlPrefix = "<?xml version=";
- bool xmlPrefixChecked = false;
-
- std::string buffer;
- for (;;)
- {
- buffer.resize(buffer.size() + blockSize);
- const size_t bytesRead = fileIn.read(&*(buffer.end() - blockSize), blockSize); //throw FileError, (X); return "bytesToRead" bytes unless end of stream!
- buffer.resize(buffer.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
-
- //quick test whether input is an XML: avoid loading large binary files up front!
- if (!xmlPrefixChecked && buffer.size() >= xmlPrefix.size() + strLength(BYTE_ORDER_MARK_UTF8))
- {
- xmlPrefixChecked = true;
- if (!startsWith(buffer, xmlPrefix) &&
- !startsWith(buffer, BYTE_ORDER_MARK_UTF8 + xmlPrefix)) //allow BOM!
- throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath)));
- }
-
- if (bytesRead < blockSize) //end of file
- break;
- }
-
- try
- {
- return parse(buffer); //throw XmlParsingError
- }
- catch (const XmlParsingError& e)
- {
- throw FileError(
- replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."),
- L"%x", fmtPath(filePath)),
- L"%y", numberTo<std::wstring>(e.row + 1)),
- L"%z", numberTo<std::wstring>(e.col + 1)));
- }
-}
-
-
-void zen::saveXmlDocument(const XmlDoc& doc, const Zstring& filePath) //throw FileError
-{
- const std::string stream = serialize(doc); //noexcept
-
- //only update xml file if there are real changes
- try
- {
- if (getFileSize(filePath) == stream.size()) //throw FileError
- if (loadBinContainer<std::string>(filePath, nullptr /*notifyUnbufferedIO*/) == stream) //throw FileError
- return;
- }
- catch (FileError&) {}
-
- saveBinContainer(filePath, stream, nullptr /*notifyUnbufferedIO*/); //throw FileError
-}
-
-
-void zen::checkForMappingErrors(const XmlIn& xmlInput, const Zstring& filePath) //throw FileError
-{
- if (xmlInput.errorsOccured())
- {
- std::wstring msg = _("The following XML elements could not be read:") + L"\n";
- for (const std::wstring& elem : xmlInput.getErrorsAs<std::wstring>())
- msg += L"\n" + elem;
-
- throw FileError(replaceCpy(_("Configuration file %x is incomplete. The missing elements will be set to their default values."), L"%x", fmtPath(filePath)) + L"\n\n" + msg);
- }
-}
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "xml_io.h"
+#include "file_access.h"
+#include "file_io.h"
+
+using namespace zen;
+
+
+XmlDoc zen::loadXmlDocument(const Zstring& filePath) //throw FileError
+{
+ FileInput fileIn(filePath, nullptr /*notifyUnbufferedIO*/); //throw FileError, ErrorFileLocked
+ const size_t blockSize = fileIn.getBlockSize();
+ const std::string xmlPrefix = "<?xml version=";
+ bool xmlPrefixChecked = false;
+
+ std::string buffer;
+ for (;;)
+ {
+ buffer.resize(buffer.size() + blockSize);
+ const size_t bytesRead = fileIn.read(&*(buffer.end() - blockSize), blockSize); //throw FileError, (X); return "bytesToRead" bytes unless end of stream!
+ buffer.resize(buffer.size() - blockSize + bytesRead); //caveat: unsigned arithmetics
+
+ //quick test whether input is an XML: avoid loading large binary files up front!
+ if (!xmlPrefixChecked && buffer.size() >= xmlPrefix.size() + strLength(BYTE_ORDER_MARK_UTF8))
+ {
+ xmlPrefixChecked = true;
+ if (!startsWith(buffer, xmlPrefix) &&
+ !startsWith(buffer, BYTE_ORDER_MARK_UTF8 + xmlPrefix)) //allow BOM!
+ throw FileError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath)));
+ }
+
+ if (bytesRead < blockSize) //end of file
+ break;
+ }
+
+ try
+ {
+ return parse(buffer); //throw XmlParsingError
+ }
+ catch (const XmlParsingError& e)
+ {
+ throw FileError(
+ replaceCpy(replaceCpy(replaceCpy(_("Error parsing file %x, row %y, column %z."),
+ L"%x", fmtPath(filePath)),
+ L"%y", numberTo<std::wstring>(e.row + 1)),
+ L"%z", numberTo<std::wstring>(e.col + 1)));
+ }
+}
+
+
+void zen::saveXmlDocument(const XmlDoc& doc, const Zstring& filePath) //throw FileError
+{
+ const std::string stream = serialize(doc); //noexcept
+
+ //only update xml file if there are real changes
+ try
+ {
+ if (getFileSize(filePath) == stream.size()) //throw FileError
+ if (loadBinContainer<std::string>(filePath, nullptr /*notifyUnbufferedIO*/) == stream) //throw FileError
+ return;
+ }
+ catch (FileError&) {}
+
+ saveBinContainer(filePath, stream, nullptr /*notifyUnbufferedIO*/); //throw FileError
+}
+
+
+void zen::checkForMappingErrors(const XmlIn& xmlInput, const Zstring& filePath) //throw FileError
+{
+ if (xmlInput.errorsOccured())
+ {
+ std::wstring msg = _("The following XML elements could not be read:") + L"\n";
+ for (const std::wstring& elem : xmlInput.getErrorsAs<std::wstring>())
+ msg += L"\n" + elem;
+
+ throw FileError(replaceCpy(_("Configuration file %x is incomplete. The missing elements will be set to their default values."), L"%x", fmtPath(filePath)) + L"\n\n" + msg);
+ }
+}
diff --git a/zen/xml_io.h b/zen/xml_io.h
index ae735ae6..6c2fc720 100755
--- a/zen/xml_io.h
+++ b/zen/xml_io.h
@@ -1,26 +1,26 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef XML_IO_H_8914759321263879
-#define XML_IO_H_8914759321263879
-
-#include <zenxml/xml.h>
-#include "file_error.h"
-
-//combine zen::Xml and zen file i/o
-//-> loadXmlDocument vs loadStream:
-//1. better error reporting
-//2. quick exit if (potentially large) input file is not an XML
-
-namespace zen
-{
-XmlDoc loadXmlDocument(const Zstring& filePath); //throw FileError
-void checkForMappingErrors(const XmlIn& xmlInput, const Zstring& filePath); //throw FileError
-
-void saveXmlDocument(const XmlDoc& doc, const Zstring& filePath); //throw FileError
-}
-
-#endif //XML_IO_H_8914759321263879
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef XML_IO_H_8914759321263879
+#define XML_IO_H_8914759321263879
+
+#include <zenxml/xml.h>
+#include "file_error.h"
+
+//combine zen::Xml and zen file i/o
+//-> loadXmlDocument vs loadStream:
+//1. better error reporting
+//2. quick exit if (potentially large) input file is not an XML
+
+namespace zen
+{
+XmlDoc loadXmlDocument(const Zstring& filePath); //throw FileError
+void checkForMappingErrors(const XmlIn& xmlInput, const Zstring& filePath); //throw FileError
+
+void saveXmlDocument(const XmlDoc& doc, const Zstring& filePath); //throw FileError
+}
+
+#endif //XML_IO_H_8914759321263879
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index a936efb5..6b41af13 100755
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -1,151 +1,151 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#include "zstring.h"
-#include <stdexcept>
-#include "utf.h"
-
-
-using namespace zen;
-
-/*
-MSDN "Handling Sorting in Your Applications": https://msdn.microsoft.com/en-us/library/windows/desktop/dd318144
-
-Perf test: compare strings 10 mio times; 64 bit build
------------------------------------------------------
- string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-"
- string b = "fjK84$%kgfj$%T\\\\gfFg\\gsdgf\\fgSy----------dfdf"
-
-Windows (UTF16 wchar_t)
- 4 ns | wcscmp
- 67 ns | CompareStringOrdinalFunc+ + bIgnoreCase
-314 ns | LCMapString + wmemcmp
-
-OS X (UTF8 char)
- 6 ns | strcmp
- 98 ns | strcasecmp
- 120 ns | strncasecmp + std::min(sizeLhs, sizeRhs);
- 856 ns | CFStringCreateWithCString + CFStringCompare(kCFCompareCaseInsensitive)
-1110 ns | CFStringCreateWithCStringNoCopy + CFStringCompare(kCFCompareCaseInsensitive)
-________________________
-time per call | function
-*/
-
-
-
-
-namespace
-{
-int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
-{
- //- strncasecmp implements ASCII CI-comparsion only! => signature is broken for UTF8-input; toupper() similarly doesn't support Unicode
- //- wcsncasecmp: https://opensource.apple.com/source/Libc/Libc-763.12/string/wcsncasecmp-fbsd.c
- // => re-implement comparison based on towlower() to avoid memory allocations
- using namespace zen::implementation;
-
- UtfDecoder<char> decL(lhs, lhsLen);
- UtfDecoder<char> decR(rhs, rhsLen);
- for (;;)
- {
- const Opt<CodePoint> cpL = decL.getNext();
- const Opt<CodePoint> cpR = decR.getNext();
- if (!cpL || !cpR)
- return static_cast<int>(!cpR) - static_cast<int>(!cpL);
-
- static_assert(sizeof(wchar_t) == sizeof(CodePoint), "");
- const wchar_t charL = ::towlower(static_cast<wchar_t>(*cpL)); //ordering: towlower() converts to higher code points than towupper()
- const wchar_t charR = ::towlower(static_cast<wchar_t>(*cpR)); //uses LC_CTYPE category of current locale
- if (charL != charR)
- return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention!
- //unsigned underflow is well-defined!
- }
-}
-}
-
-
-int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
-{
- const char* const lhsEnd = lhs + lhsLen;
- const char* const rhsEnd = rhs + rhsLen;
- /*
- - compare strings after conceptually creating blocks of whitespace/numbers/text
- - implement strict weak ordering!
- - don't follow broken "strnatcasecmp": https://github.com/php/php-src/blob/master/ext/standard/strnatcmp.c
- 1. incorrect non-ASCII CI-comparison 2. incorrect bounds checks
- 3. incorrect trimming of *all* whitespace 4. arbitrary handling of leading 0 only at string begin
- 5. incorrect handling of whitespace following a number 6. code is a mess
- */
- for (;;)
- {
- if (lhs == lhsEnd || rhs == rhsEnd)
- return static_cast<int>(lhs != lhsEnd) - static_cast<int>(rhs != rhsEnd); //"nothing" before "something"
- //note: "something" never would have been condensed to "nothing" further below => can finish evaluation here
-
- const bool wsL = isWhiteSpace(*lhs);
- const bool wsR = isWhiteSpace(*rhs);
- if (wsL != wsR)
- return static_cast<int>(!wsL) - static_cast<int>(!wsR); //whitespace before non-ws!
- if (wsL)
- {
- ++lhs, ++rhs;
- while (lhs != lhsEnd && isWhiteSpace(*lhs)) ++lhs;
- while (rhs != rhsEnd && isWhiteSpace(*rhs)) ++rhs;
- continue;
- }
-
- const bool digitL = isDigit(*lhs);
- const bool digitR = isDigit(*rhs);
- if (digitL != digitR)
- return static_cast<int>(!digitL) - static_cast<int>(!digitR); //number before chars!
- if (digitL)
- {
- while (lhs != lhsEnd && *lhs == '0') ++lhs;
- while (rhs != rhsEnd && *rhs == '0') ++rhs;
-
- int rv = 0;
- for (;; ++lhs, ++rhs)
- {
- const bool endL = lhs == lhsEnd || !isDigit(*lhs);
- const bool endR = rhs == rhsEnd || !isDigit(*rhs);
- if (endL != endR)
- return static_cast<int>(!endL) - static_cast<int>(!endR); //more digits means bigger number
- if (endL)
- break; //same number of digits
-
- if (rv == 0 && *lhs != *rhs)
- rv = *lhs - *rhs; //found first digit difference comparing from left
- }
- if (rv != 0)
- return rv;
- continue;
- }
-
- //compare full junks of text: consider unicode encoding!
- const char* textBeginL = lhs++;
- const char* textBeginR = rhs++; //current char is neither white space nor digit at this point!
- while (lhs != lhsEnd && !isWhiteSpace(*lhs) && !isDigit(*lhs)) ++lhs;
- while (rhs != rhsEnd && !isWhiteSpace(*rhs) && !isDigit(*rhs)) ++rhs;
-
- const int rv = compareNoCaseUtf8(textBeginL, lhs - textBeginL, textBeginR, rhs - textBeginR);
- if (rv != 0)
- return rv;
- }
-}
-
-
-namespace
-{
-}
-
-
-int CmpNaturalSort::operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const
-{
- //auto strL = utfTo<std::string>(Zstring(lhs, lhsLen));
- //auto strR = utfTo<std::string>(Zstring(rhs, rhsLen));
- //return cmpStringNaturalLinux(strL.c_str(), strL.size(), strR.c_str(), strR.size());
- return cmpStringNaturalLinux(lhs, lhsLen, rhs, rhsLen);
-
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "zstring.h"
+#include <stdexcept>
+#include "utf.h"
+
+
+using namespace zen;
+
+/*
+MSDN "Handling Sorting in Your Applications": https://msdn.microsoft.com/en-us/library/windows/desktop/dd318144
+
+Perf test: compare strings 10 mio times; 64 bit build
+-----------------------------------------------------
+ string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-"
+ string b = "fjK84$%kgfj$%T\\\\gfFg\\gsdgf\\fgSy----------dfdf"
+
+Windows (UTF16 wchar_t)
+ 4 ns | wcscmp
+ 67 ns | CompareStringOrdinalFunc+ + bIgnoreCase
+314 ns | LCMapString + wmemcmp
+
+OS X (UTF8 char)
+ 6 ns | strcmp
+ 98 ns | strcasecmp
+ 120 ns | strncasecmp + std::min(sizeLhs, sizeRhs);
+ 856 ns | CFStringCreateWithCString + CFStringCompare(kCFCompareCaseInsensitive)
+1110 ns | CFStringCreateWithCStringNoCopy + CFStringCompare(kCFCompareCaseInsensitive)
+________________________
+time per call | function
+*/
+
+
+
+
+namespace
+{
+int compareNoCaseUtf8(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
+{
+ //- strncasecmp implements ASCII CI-comparsion only! => signature is broken for UTF8-input; toupper() similarly doesn't support Unicode
+ //- wcsncasecmp: https://opensource.apple.com/source/Libc/Libc-763.12/string/wcsncasecmp-fbsd.c
+ // => re-implement comparison based on towlower() to avoid memory allocations
+ using namespace zen::implementation;
+
+ UtfDecoder<char> decL(lhs, lhsLen);
+ UtfDecoder<char> decR(rhs, rhsLen);
+ for (;;)
+ {
+ const Opt<CodePoint> cpL = decL.getNext();
+ const Opt<CodePoint> cpR = decR.getNext();
+ if (!cpL || !cpR)
+ return static_cast<int>(!cpR) - static_cast<int>(!cpL);
+
+ static_assert(sizeof(wchar_t) == sizeof(CodePoint), "");
+ const wchar_t charL = ::towlower(static_cast<wchar_t>(*cpL)); //ordering: towlower() converts to higher code points than towupper()
+ const wchar_t charR = ::towlower(static_cast<wchar_t>(*cpR)); //uses LC_CTYPE category of current locale
+ if (charL != charR)
+ return static_cast<unsigned int>(charL) - static_cast<unsigned int>(charR); //unsigned char-comparison is the convention!
+ //unsigned underflow is well-defined!
+ }
+}
+}
+
+
+int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen)
+{
+ const char* const lhsEnd = lhs + lhsLen;
+ const char* const rhsEnd = rhs + rhsLen;
+ /*
+ - compare strings after conceptually creating blocks of whitespace/numbers/text
+ - implement strict weak ordering!
+ - don't follow broken "strnatcasecmp": https://github.com/php/php-src/blob/master/ext/standard/strnatcmp.c
+ 1. incorrect non-ASCII CI-comparison 2. incorrect bounds checks
+ 3. incorrect trimming of *all* whitespace 4. arbitrary handling of leading 0 only at string begin
+ 5. incorrect handling of whitespace following a number 6. code is a mess
+ */
+ for (;;)
+ {
+ if (lhs == lhsEnd || rhs == rhsEnd)
+ return static_cast<int>(lhs != lhsEnd) - static_cast<int>(rhs != rhsEnd); //"nothing" before "something"
+ //note: "something" never would have been condensed to "nothing" further below => can finish evaluation here
+
+ const bool wsL = isWhiteSpace(*lhs);
+ const bool wsR = isWhiteSpace(*rhs);
+ if (wsL != wsR)
+ return static_cast<int>(!wsL) - static_cast<int>(!wsR); //whitespace before non-ws!
+ if (wsL)
+ {
+ ++lhs, ++rhs;
+ while (lhs != lhsEnd && isWhiteSpace(*lhs)) ++lhs;
+ while (rhs != rhsEnd && isWhiteSpace(*rhs)) ++rhs;
+ continue;
+ }
+
+ const bool digitL = isDigit(*lhs);
+ const bool digitR = isDigit(*rhs);
+ if (digitL != digitR)
+ return static_cast<int>(!digitL) - static_cast<int>(!digitR); //number before chars!
+ if (digitL)
+ {
+ while (lhs != lhsEnd && *lhs == '0') ++lhs;
+ while (rhs != rhsEnd && *rhs == '0') ++rhs;
+
+ int rv = 0;
+ for (;; ++lhs, ++rhs)
+ {
+ const bool endL = lhs == lhsEnd || !isDigit(*lhs);
+ const bool endR = rhs == rhsEnd || !isDigit(*rhs);
+ if (endL != endR)
+ return static_cast<int>(!endL) - static_cast<int>(!endR); //more digits means bigger number
+ if (endL)
+ break; //same number of digits
+
+ if (rv == 0 && *lhs != *rhs)
+ rv = *lhs - *rhs; //found first digit difference comparing from left
+ }
+ if (rv != 0)
+ return rv;
+ continue;
+ }
+
+ //compare full junks of text: consider unicode encoding!
+ const char* textBeginL = lhs++;
+ const char* textBeginR = rhs++; //current char is neither white space nor digit at this point!
+ while (lhs != lhsEnd && !isWhiteSpace(*lhs) && !isDigit(*lhs)) ++lhs;
+ while (rhs != rhsEnd && !isWhiteSpace(*rhs) && !isDigit(*rhs)) ++rhs;
+
+ const int rv = compareNoCaseUtf8(textBeginL, lhs - textBeginL, textBeginR, rhs - textBeginR);
+ if (rv != 0)
+ return rv;
+ }
+}
+
+
+namespace
+{
+}
+
+
+int CmpNaturalSort::operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const
+{
+ //auto strL = utfTo<std::string>(Zstring(lhs, lhsLen));
+ //auto strR = utfTo<std::string>(Zstring(rhs, rhsLen));
+ //return cmpStringNaturalLinux(strL.c_str(), strL.size(), strR.c_str(), strR.size());
+ return cmpStringNaturalLinux(lhs, lhsLen, rhs, rhsLen);
+
} \ No newline at end of file
diff --git a/zen/zstring.h b/zen/zstring.h
index fdb71da0..273efd2f 100755
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -1,159 +1,159 @@
-// *****************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
-// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
-// *****************************************************************************
-
-#ifndef ZSTRING_H_73425873425789
-#define ZSTRING_H_73425873425789
-
-#include "string_base.h"
-
-
- using Zchar = char;
- #define Zstr(x) x
- const Zchar FILE_NAME_SEPARATOR = '/';
-
-//"The reason for all the fuss above" - Loki/SmartPtr
-//a high-performance string for interfacing with native OS APIs in multithreaded contexts
-using Zstring = zen::Zbase<Zchar>;
-
-
-//Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES
-struct CmpFilePath
-{
- int operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const;
-};
-
-struct CmpNaturalSort
-{
- int operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const;
-};
-
-
-struct LessFilePath
-{
- template <class S> //don't support heterogenous input! => use as container predicate only!
- bool operator()(const S& lhs, const S& rhs) const { using namespace zen; return CmpFilePath()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
-};
-
-struct LessNaturalSort
-{
- template <class S> //don't support heterogenous input! => use as container predicate only!
- bool operator()(const S& lhs, const S& rhs) const { using namespace zen; return CmpNaturalSort()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
-};
-
-
-template <class S>
-S makeUpperCopy(S str);
-
-
-template <class S, class T> inline
-bool equalFilePath(const S& lhs, const T& rhs) { using namespace zen; return strEqual(lhs, rhs, CmpFilePath()); }
-
-
-inline
-Zstring appendSeparator(Zstring path) //support rvalue references!
-{
- return zen::endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR); //returning a by-value parameter implicitly converts to r-value!
-}
-
-
-inline
-Zstring getFileExtension(const Zstring& filePath)
-{
- const Zstring shortName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL);
- return afterLast(shortName, Zchar('.'), zen::IF_MISSING_RETURN_NONE);
-}
-
-
-template <class S, class T, class U>
-S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm);
-
-
-
-
-//################################# inline implementation ########################################
-inline
-void makeUpperInPlace(wchar_t* str, size_t strLen)
-{
- std::for_each(str, str + strLen, [](wchar_t& c) { c = std::towupper(c); }); //locale-dependent!
-}
-
-
-inline
-void makeUpperInPlace(char* str, size_t strLen)
-{
- std::for_each(str, str + strLen, [](char& c) { c = std::toupper(static_cast<unsigned char>(c)); }); //locale-dependent!
- //result of toupper() is an unsigned char mapped to int range: the char representation is in the last 8 bits and we need not care about signedness!
- //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves!
-}
-
-
-template <class S> inline
-S makeUpperCopy(S str)
-{
- const size_t len = str.length(); //we assert S is a string type!
- if (len > 0)
- makeUpperInPlace(&*str.begin(), len);
-
- return std::move(str); //"str" is an l-value parameter => no copy elision!
-}
-
-
-inline
-int CmpFilePath::operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const
-{
- assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
- assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
-
- const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen));
- if (rv != 0)
- return rv;
- return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
-}
-
-
-template <class S, class T, class U> inline
-S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm)
-{
- using namespace zen;
- static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
- static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, "");
- const size_t oldLen = strLength(oldTerm);
- if (oldLen == 0)
- return str;
-
- const S strU = makeUpperCopy(str); //S required to be a string class
- const S oldU = makeUpperCopy<S>(oldTerm); //[!] T not required to be a string class
- assert(strLength(strU) == strLength(str ));
- assert(strLength(oldU) == strLength(oldTerm));
-
- const auto* const newBegin = strBegin(newTerm);
- const auto* const newEnd = newBegin + strLength(newTerm);
-
- S output;
-
- for (size_t pos = 0;;)
- {
- const auto itFound = std::search(strU.begin() + pos, strU.end(),
- oldU.begin(), oldU.end());
- if (itFound == strU.end() && pos == 0)
- return str; //optimize "oldTerm not found": return ref-counted copy
-
- impl::stringAppend(output, str.begin() + pos, str.begin() + (itFound - strU.begin()));
- if (itFound == strU.end())
- return output;
-
- impl::stringAppend(output, newBegin, newEnd);
- pos = (itFound - strU.begin()) + oldLen;
- }
-}
-
- int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
-
-//---------------------------------------------------------------------------
-//ZEN macro consistency checks:
-
-
-#endif //ZSTRING_H_73425873425789
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef ZSTRING_H_73425873425789
+#define ZSTRING_H_73425873425789
+
+#include "string_base.h"
+
+
+ using Zchar = char;
+ #define Zstr(x) x
+ const Zchar FILE_NAME_SEPARATOR = '/';
+
+//"The reason for all the fuss above" - Loki/SmartPtr
+//a high-performance string for interfacing with native OS APIs in multithreaded contexts
+using Zstring = zen::Zbase<Zchar>;
+
+
+//Compare filepaths: Windows/OS X does NOT distinguish between upper/lower-case, while Linux DOES
+struct CmpFilePath
+{
+ int operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const;
+};
+
+struct CmpNaturalSort
+{
+ int operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const;
+};
+
+
+struct LessFilePath
+{
+ template <class S> //don't support heterogenous input! => use as container predicate only!
+ bool operator()(const S& lhs, const S& rhs) const { using namespace zen; return CmpFilePath()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
+};
+
+struct LessNaturalSort
+{
+ template <class S> //don't support heterogenous input! => use as container predicate only!
+ bool operator()(const S& lhs, const S& rhs) const { using namespace zen; return CmpNaturalSort()(strBegin(lhs), strLength(lhs), strBegin(rhs), strLength(rhs)) < 0; }
+};
+
+
+template <class S>
+S makeUpperCopy(S str);
+
+
+template <class S, class T> inline
+bool equalFilePath(const S& lhs, const T& rhs) { using namespace zen; return strEqual(lhs, rhs, CmpFilePath()); }
+
+
+inline
+Zstring appendSeparator(Zstring path) //support rvalue references!
+{
+ return zen::endsWith(path, FILE_NAME_SEPARATOR) ? path : (path += FILE_NAME_SEPARATOR); //returning a by-value parameter implicitly converts to r-value!
+}
+
+
+inline
+Zstring getFileExtension(const Zstring& filePath)
+{
+ const Zstring shortName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL);
+ return afterLast(shortName, Zchar('.'), zen::IF_MISSING_RETURN_NONE);
+}
+
+
+template <class S, class T, class U>
+S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm);
+
+
+
+
+//################################# inline implementation ########################################
+inline
+void makeUpperInPlace(wchar_t* str, size_t strLen)
+{
+ std::for_each(str, str + strLen, [](wchar_t& c) { c = std::towupper(c); }); //locale-dependent!
+}
+
+
+inline
+void makeUpperInPlace(char* str, size_t strLen)
+{
+ std::for_each(str, str + strLen, [](char& c) { c = std::toupper(static_cast<unsigned char>(c)); }); //locale-dependent!
+ //result of toupper() is an unsigned char mapped to int range: the char representation is in the last 8 bits and we need not care about signedness!
+ //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves!
+}
+
+
+template <class S> inline
+S makeUpperCopy(S str)
+{
+ const size_t len = str.length(); //we assert S is a string type!
+ if (len > 0)
+ makeUpperInPlace(&*str.begin(), len);
+
+ return std::move(str); //"str" is an l-value parameter => no copy elision!
+}
+
+
+inline
+int CmpFilePath::operator()(const Zchar* lhs, size_t lhsLen, const Zchar* rhs, size_t rhsLen) const
+{
+ assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
+ assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
+
+ const int rv = std::strncmp(lhs, rhs, std::min(lhsLen, rhsLen));
+ if (rv != 0)
+ return rv;
+ return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
+}
+
+
+template <class S, class T, class U> inline
+S ciReplaceCpy(const S& str, const T& oldTerm, const U& newTerm)
+{
+ using namespace zen;
+ static_assert(IsSameType<typename GetCharType<S>::Type, typename GetCharType<T>::Type>::value, "");
+ static_assert(IsSameType<typename GetCharType<T>::Type, typename GetCharType<U>::Type>::value, "");
+ const size_t oldLen = strLength(oldTerm);
+ if (oldLen == 0)
+ return str;
+
+ const S strU = makeUpperCopy(str); //S required to be a string class
+ const S oldU = makeUpperCopy<S>(oldTerm); //[!] T not required to be a string class
+ assert(strLength(strU) == strLength(str ));
+ assert(strLength(oldU) == strLength(oldTerm));
+
+ const auto* const newBegin = strBegin(newTerm);
+ const auto* const newEnd = newBegin + strLength(newTerm);
+
+ S output;
+
+ for (size_t pos = 0;;)
+ {
+ const auto itFound = std::search(strU.begin() + pos, strU.end(),
+ oldU.begin(), oldU.end());
+ if (itFound == strU.end() && pos == 0)
+ return str; //optimize "oldTerm not found": return ref-counted copy
+
+ impl::stringAppend(output, str.begin() + pos, str.begin() + (itFound - strU.begin()));
+ if (itFound == strU.end())
+ return output;
+
+ impl::stringAppend(output, newBegin, newEnd);
+ pos = (itFound - strU.begin()) + oldLen;
+ }
+}
+
+ int cmpStringNaturalLinux(const char* lhs, size_t lhsLen, const char* rhs, size_t rhsLen);
+
+//---------------------------------------------------------------------------
+//ZEN macro consistency checks:
+
+
+#endif //ZSTRING_H_73425873425789
bgstack15