summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2018-11-13 06:58:56 -0500
committerB Stack <bgstack15@gmail.com>2018-11-13 06:58:56 -0500
commit076498028ff511afd88d93e7b0bf1d1a81093b3d (patch)
tree30bf08d782d58174a0ca212b2e4b172fabd9c42c
parentMerge branch '10.5' into 'master' (diff)
downloadFreeFileSync-076498028ff511afd88d93e7b0bf1d1a81093b3d.tar.gz
FreeFileSync-076498028ff511afd88d93e7b0bf1d1a81093b3d.tar.bz2
FreeFileSync-076498028ff511afd88d93e7b0bf1d1a81093b3d.zip
10.6
-rwxr-xr-xChangelog.txt24
-rwxr-xr-xFreeFileSync/Build/Languages/arabic.lng184
-rwxr-xr-xFreeFileSync/Build/Languages/bulgarian.lng192
-rwxr-xr-xFreeFileSync/Build/Languages/chinese_simple.lng174
-rwxr-xr-xFreeFileSync/Build/Languages/chinese_traditional.lng196
-rwxr-xr-xFreeFileSync/Build/Languages/croatian.lng178
-rwxr-xr-xFreeFileSync/Build/Languages/czech.lng178
-rwxr-xr-xFreeFileSync/Build/Languages/danish.lng176
-rwxr-xr-xFreeFileSync/Build/Languages/dutch.lng184
-rwxr-xr-xFreeFileSync/Build/Languages/english_uk.lng186
-rwxr-xr-xFreeFileSync/Build/Languages/french.lng176
-rwxr-xr-xFreeFileSync/Build/Languages/german.lng102
-rwxr-xr-xFreeFileSync/Build/Languages/greek.lng180
-rwxr-xr-xFreeFileSync/Build/Languages/hebrew.lng186
-rwxr-xr-xFreeFileSync/Build/Languages/hindi.lng178
-rwxr-xr-xFreeFileSync/Build/Languages/hungarian.lng194
-rwxr-xr-xFreeFileSync/Build/Languages/italian.lng176
-rwxr-xr-xFreeFileSync/Build/Languages/japanese.lng178
-rwxr-xr-xFreeFileSync/Build/Languages/korean.lng176
-rwxr-xr-xFreeFileSync/Build/Languages/lithuanian.lng178
-rwxr-xr-xFreeFileSync/Build/Languages/norwegian.lng186
-rwxr-xr-xFreeFileSync/Build/Languages/polish.lng180
-rwxr-xr-xFreeFileSync/Build/Languages/portuguese.lng176
-rwxr-xr-xFreeFileSync/Build/Languages/portuguese_br.lng182
-rwxr-xr-xFreeFileSync/Build/Languages/romanian.lng178
-rwxr-xr-xFreeFileSync/Build/Languages/russian.lng182
-rwxr-xr-xFreeFileSync/Build/Languages/slovak.lng208
-rwxr-xr-xFreeFileSync/Build/Languages/slovenian.lng180
-rwxr-xr-xFreeFileSync/Build/Languages/spanish.lng176
-rwxr-xr-xFreeFileSync/Build/Languages/swedish.lng176
-rwxr-xr-xFreeFileSync/Build/Languages/turkish.lng170
-rwxr-xr-xFreeFileSync/Build/Languages/ukrainian.lng178
-rwxr-xr-xFreeFileSync/Build/styles.gtk_rc4
-rwxr-xr-xFreeFileSync/Source/Makefile16
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/Makefile12
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/application.cpp5
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/folder_selector2.cpp3
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/main_dlg.cpp18
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/monitor.cpp16
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/tray_menu.cpp2
-rwxr-xr-xFreeFileSync/Source/RealTimeSync/xml_proc.cpp10
-rwxr-xr-xFreeFileSync/Source/base/algorithm.cpp325
-rwxr-xr-xFreeFileSync/Source/base/algorithm.h12
-rwxr-xr-xFreeFileSync/Source/base/application.cpp83
-rwxr-xr-xFreeFileSync/Source/base/comparison.cpp193
-rwxr-xr-xFreeFileSync/Source/base/comparison.h2
-rwxr-xr-xFreeFileSync/Source/base/db_file.cpp136
-rwxr-xr-xFreeFileSync/Source/base/db_file.h8
-rwxr-xr-xFreeFileSync/Source/base/dir_exist_async.h74
-rwxr-xr-xFreeFileSync/Source/base/dir_lock.cpp27
-rwxr-xr-xFreeFileSync/Source/base/dir_lock.h2
-rwxr-xr-xFreeFileSync/Source/base/ffs_paths.cpp73
-rwxr-xr-xFreeFileSync/Source/base/ffs_paths.h5
-rwxr-xr-xFreeFileSync/Source/base/file_hierarchy.cpp8
-rwxr-xr-xFreeFileSync/Source/base/file_hierarchy.h34
-rwxr-xr-xFreeFileSync/Source/base/generate_logfile.cpp40
-rwxr-xr-xFreeFileSync/Source/base/generate_logfile.h2
-rwxr-xr-xFreeFileSync/Source/base/localization.cpp93
-rwxr-xr-xFreeFileSync/Source/base/lock_holder.h16
-rwxr-xr-xFreeFileSync/Source/base/norm_filter.h6
-rwxr-xr-xFreeFileSync/Source/base/parallel_scan.cpp31
-rwxr-xr-xFreeFileSync/Source/base/parallel_scan.h14
-rwxr-xr-xFreeFileSync/Source/base/parse_lng.h2
-rwxr-xr-xFreeFileSync/Source/base/parse_plural.h4
-rwxr-xr-xFreeFileSync/Source/base/path_filter.cpp366
-rwxr-xr-xFreeFileSync/Source/base/path_filter.h239
-rwxr-xr-xFreeFileSync/Source/base/perf_check.cpp4
-rwxr-xr-xFreeFileSync/Source/base/process_xml.cpp8
-rwxr-xr-xFreeFileSync/Source/base/resolve_path.cpp18
-rwxr-xr-xFreeFileSync/Source/base/resolve_path.h2
-rwxr-xr-xFreeFileSync/Source/base/status_handler_impl.h28
-rwxr-xr-xFreeFileSync/Source/base/structures.cpp34
-rwxr-xr-xFreeFileSync/Source/base/structures.h13
-rwxr-xr-xFreeFileSync/Source/base/synchronization.cpp53
-rwxr-xr-xFreeFileSync/Source/base/synchronization.h2
-rwxr-xr-xFreeFileSync/Source/base/versioning.cpp101
-rwxr-xr-xFreeFileSync/Source/base/versioning.h4
-rwxr-xr-xFreeFileSync/Source/fs/abstract.cpp181
-rwxr-xr-xFreeFileSync/Source/fs/abstract.h203
-rwxr-xr-xFreeFileSync/Source/fs/concrete_impl.h8
-rwxr-xr-xFreeFileSync/Source/fs/native.cpp55
-rwxr-xr-xFreeFileSync/Source/ui/batch_status_handler.cpp17
-rwxr-xr-xFreeFileSync/Source/ui/cfg_grid.cpp8
-rwxr-xr-xFreeFileSync/Source/ui/cfg_grid.h2
-rwxr-xr-xFreeFileSync/Source/ui/command_box.cpp4
-rwxr-xr-xFreeFileSync/Source/ui/file_view.cpp2
-rwxr-xr-xFreeFileSync/Source/ui/folder_history_box.cpp4
-rwxr-xr-xFreeFileSync/Source/ui/folder_history_box.h4
-rwxr-xr-xFreeFileSync/Source/ui/folder_selector.cpp2
-rwxr-xr-xFreeFileSync/Source/ui/gui_generated.cpp18
-rwxr-xr-xFreeFileSync/Source/ui/gui_status_handler.cpp27
-rwxr-xr-xFreeFileSync/Source/ui/log_panel.cpp2
-rwxr-xr-xFreeFileSync/Source/ui/main_dlg.cpp148
-rwxr-xr-xFreeFileSync/Source/ui/main_dlg.h4
-rwxr-xr-xFreeFileSync/Source/ui/progress_indicator.cpp8
-rwxr-xr-xFreeFileSync/Source/ui/search_grid.cpp2
-rwxr-xr-xFreeFileSync/Source/ui/small_dlgs.cpp30
-rwxr-xr-xFreeFileSync/Source/ui/small_dlgs.h8
-rwxr-xr-xFreeFileSync/Source/ui/sync_cfg.cpp32
-rwxr-xr-xFreeFileSync/Source/ui/sync_cfg.h2
-rwxr-xr-xFreeFileSync/Source/ui/tray_icon.cpp2
-rwxr-xr-xFreeFileSync/Source/ui/tree_grid.cpp2
-rwxr-xr-xFreeFileSync/Source/version/version.h2
-rwxr-xr-xwx+/async_task.h4
-rwxr-xr-xwx+/graph.cpp18
-rwxr-xr-xwx+/grid.cpp20
-rwxr-xr-xwx+/grid.h4
-rwxr-xr-xwx+/image_holder.h2
-rwxr-xr-xwx+/image_resources.cpp2
-rwxr-xr-xzen/basic_math.h51
-rwxr-xr-xzen/dir_watcher.cpp10
-rwxr-xr-xzen/file_access.cpp142
-rwxr-xr-xzen/file_access.h20
-rwxr-xr-xzen/file_io.cpp2
-rwxr-xr-xzen/legacy_compiler.h36
-rwxr-xr-xzen/recycler.cpp2
-rwxr-xr-xzen/ring_buffer.h55
-rwxr-xr-xzen/serialize.h18
-rwxr-xr-xzen/stl_tools.h63
-rwxr-xr-xzen/string_base.h4
-rwxr-xr-xzen/string_tools.h29
-rwxr-xr-xzen/string_traits.h2
-rwxr-xr-xzen/thread.h21
-rwxr-xr-xzen/zstring.cpp22
-rwxr-xr-xzen/zstring.h64
-rwxr-xr-xzenXml/zenxml/parser.h2
-rwxr-xr-xzenXml/zenxml/xml.h16
127 files changed, 5361 insertions, 3741 deletions
diff --git a/Changelog.txt b/Changelog.txt
index 004aca9d..185970a9 100755
--- a/Changelog.txt
+++ b/Changelog.txt
@@ -1,9 +1,31 @@
+FreeFileSync 10.6 [2018-11-12]
+------------------------------
+Detect and skip traversing folder path aliases
+Report conflict when names differ only in Unicode normalization
+Unified 32 and 64 bit into single package (Linux)
+Notarized application package (macOS)
+Save configuration files in user-specific paths (Linux)
+Use XDG-style config file paths (Linux)
+Fixed (fake) intermittent hangs during comparison (Linux, macOS)
+Detect SMB mount points as separate devices (Linux)
+Consider /mnt subfolders as device root paths (Linux)
+Create missing default log folder upon first run
+Don't consider final status for error/warning count
+Discard invalid SFTP session after max channel determination
+Fixed main dialog position not being remembered (Linux)
+Fixed imprecise FTP times due to MLST parsing issue
+Fixed application menu not being localized (macOS)
+Fixed temp file name hitting file system length limitations
+Fixed fatal errors not being written to console (Debian Linux)
+Updated translation files
+
+
FreeFileSync 10.5 [2018-10-11]
------------------------------
New file matching algorithm considering Unicode normalization
User-configurable timeout for FTP and SFTP connections
-Obsoleted old CHM manual in favor of PDF
Ignore case sensitivity during filter matching (Linux)
+Obsoleted old CHM manual in favor of PDF
Unicode-normalized and faster case-insensitive grid search
New button to save current view filter settings as default
Both slash and backslash can be used in filter expressions
diff --git a/FreeFileSync/Build/Languages/arabic.lng b/FreeFileSync/Build/Languages/arabic.lng
index 97143023..1d4f1ac1 100755
--- a/FreeFileSync/Build/Languages/arabic.lng
+++ b/FreeFileSync/Build/Languages/arabic.lng
@@ -164,6 +164,9 @@
<source>Items differ in attributes only</source>
<target>العناصر مختلفة في السمات فقط</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>الاسم %x تم استخدامه اكثر من مرة فى نفس المجلد.</target>
+
<source>Resolving symbolic link %x</source>
<target>جاري حل المسار الرمزي %x</target>
@@ -191,9 +194,6 @@
<source>File time tolerance</source>
<target>التفاوت في وقت الملف</target>
-<source>Folder access timeout</source>
-<target>مهلة وصول المجلد</target>
-
<source>Run with background priority</source>
<target>تشغيل مع أولوية في الخلفية</target>
@@ -342,8 +342,11 @@
<source>Update attributes on right</source>
<target>تحديث السمات على اليسار</target>
-<source>Warning</source>
-<target>تحذير</target>
+<source>Errors:</source>
+<target>أخطاء:</target>
+
+<source>Warnings:</source>
+<target>تحذيرات:</target>
<source>Items processed:</source>
<target>معالجة العناصر:</target>
@@ -354,6 +357,9 @@
<source>Total time:</source>
<target>مجموع الوقت:</target>
+<source>Warning</source>
+<target>تحذير</target>
+
<source>Stopped</source>
<target>توقف</target>
@@ -363,6 +369,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>حدث خطأ أثناء تحليل الملف %x، الصف %y، و العمود %z.</target>
+<source>Services</source>
+<target>خدمات</target>
+
+<source>Show All</source>
+<target>اظهر الكل</target>
+
+<source>Hide Others</source>
+<target>اخفاء الاخرين</target>
+
+<source>Hide %x</source>
+<target>اخفاء %x</target>
+
+<source>Quit %x</source>
+<target>الغاء %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>لا يمكن وضع قفل المسار للمجلدات الأتية:</target>
@@ -382,11 +403,11 @@
<source>Cannot read directory %x.</source>
<target>لا يمكن قراءة الدليل %x.</target>
-<source>/sec</source>
-<target>\ثانية</target>
+<source>%x/sec</source>
+<target>%x ثانية</target>
-<source>%x items/sec</source>
-<target>%x عنصر\الثانية</target>
+<source>%x items</source>
+<target>%x عناصر</target>
<source>Show in Explorer</source>
<target>إظهار في المستكشف</target>
@@ -535,11 +556,11 @@
<source>Generating database...</source>
<target>إنشاء قاعدة بيانات...</target>
-<source>Searching for excess file versions:</source>
-<target>البحث عن اصدارات الملف الزائدة:</target>
+<source>Searching for old file versions:</source>
+<target>البحث عن اصدارات قديمة للملف:</target>
-<source>Removing excess file versions:</source>
-<target>إزالة اصدارات الملف الزائدة:</target>
+<source>Removing old file versions:</source>
+<target>ازالة اصدارات قديمة للملف:</target>
<source>Unable to create time stamp for versioning:</source>
<target>تعذر إنشاء بصمة زمنية من أجل المفاضلة الزمنية:</target>
@@ -597,6 +618,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>تعذر نقل %x إلى سلة المحذوفات.</target>
+<source>Unable to access %x.</source>
+<target>لا يمكن الوصول إلى %x.</target>
+
+<source>Authentication completed.</source>
+<target>نجحت المصادقة.</target>
+
+<source>Authentication failed.</source>
+<target>فشلت المصادقة.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>يمكنك غلق هذه الصفحة والاستمرار باستخدام FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>الخادم ارسل الخطأ:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>لا يمكن تحديد مساحة القرص الحرة لـ %x.</target>
+
<source>Cannot find %x.</source>
<target>لا يمكن العثور على %x.</target>
@@ -609,18 +648,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>لا يمكن حذف الرابط الرمزي %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>لا يمكن تحديد مساحة القرص الحرة لـ %x.</target>
-
<source>Incorrect command line:</source>
<target>سطر أوامر خاطئ:</target>
<source>The server does not support authentication via %x.</source>
<target>لا يدعم الخادم المصادقة عبر %x.</target>
-<source>Unable to access %x.</source>
-<target>لا يمكن الوصول إلى %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -653,28 +686,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>فشل فتح قناة SFTP رقم %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>‎0 byte</pluralform>
-<pluralform>‎1 byte</pluralform>
-<pluralform>‎2 bytes</pluralform>
-<pluralform>‎%x bytes</pluralform>
-<pluralform>‎%x bytes</pluralform>
-<pluralform>‎%x bytes</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>‎%x MB</target>
-
-<source>%x KB</source>
-<target>‎%x KB</target>
-
-<source>%x GB</source>
-<target>‎%x GB</target>
-
<source>Drag && drop</source>
<target>سحب و إفلات</target>
@@ -788,6 +799,28 @@ The command is triggered if:
<source>&Retry</source>
<target>إ&عادة المحاولة</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>‎0 byte</pluralform>
+<pluralform>‎1 byte</pluralform>
+<pluralform>‎2 bytes</pluralform>
+<pluralform>‎%x bytes</pluralform>
+<pluralform>‎%x bytes</pluralform>
+<pluralform>‎%x bytes</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>‎%x MB</target>
+
+<source>%x KB</source>
+<target>‎%x KB</target>
+
+<source>%x GB</source>
+<target>‎%x GB</target>
+
<source>Loading...</source>
<target>تحميل...</target>
@@ -937,7 +970,7 @@ The command is triggered if:
<target>&حفظ كمهمة دفعية...</target>
<source>Show &log</source>
-<target></target>
+<target>أظهر &السجل</target>
<source>Start &comparison</source>
<target>بدأ الم&قارنة</target>
@@ -999,9 +1032,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>الوصول إلى التخزين عبر الإنترنت</target>
-<source>Swap sides</source>
-<target>مبادلة الجانبين</target>
-
<source>Close search bar</source>
<target>إغلاق شريط البحث</target>
@@ -1026,6 +1056,9 @@ The command is triggered if:
<source>View type:</source>
<target>عرض النوع:</target>
+<source>Save as default</source>
+<target>حفظ كافتراضي</target>
+
<source>Select view:</source>
<target>اختيار نمط العرض:</target>
@@ -1083,6 +1116,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>تعامل مع التوقيت الصيفي</target>
+<source>Ignore errors</source>
+<target>تجاهل الأخطاء</target>
+
+<source>Retry count:</source>
+<target>تعداد محاولات الإعادة:</target>
+
+<source>Delay (in seconds):</source>
+<target>التأخير (بالثواني):</target>
+
<source>Performance improvements:</source>
<target>تحسينات الأداء:</target>
@@ -1157,17 +1199,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>اخر x أيام:</target>
-<source>Ignore errors</source>
-<target>تجاهل الأخطاء</target>
+<source>&Override default log path:</source>
+<target>&تجاوز المسار الافتراضى للسجل:</target>
-<source>Retry count:</source>
-<target>تعداد محاولات الإعادة:</target>
-
-<source>Delay (in seconds):</source>
-<target>التأخير (بالثواني):</target>
-
-<source>Run a command after synchronization:</source>
-<target>تشغيل أمر بعد المزامنة:</target>
+<source>Run a command:</source>
+<target>تشغيل الأمر:</target>
<source>OK</source>
<target>موافق</target>
@@ -1217,6 +1253,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>المسار على الخادم:</target>
+<source>Access timeout (in seconds):</source>
+<target>أقصى زمن للوصول(ثانية):</target>
+
<source>SFTP channels per connection:</source>
<target>قنوات SFTP لكل اتصال:</target>
@@ -1295,12 +1334,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>إحباط المزامنة عند أول خطأ</target>
-<source>Save log:</source>
-<target>حفظ السجل:</target>
-
-<source>Limit number of log files:</source>
-<target>حد عدد ملفات السجل:</target>
-
<source>How can I schedule a batch job?</source>
<target>كيف يمكنني جدولة مهمة دفعية؟</target>
@@ -1337,8 +1370,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>إعادة إظهار جميع التنبهات و نوافذ الحوار التي تم إخفاؤها</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>المسار الافتراضى للسجل:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&ازالة السجلات بعد x يوم:</target>
<source>Customize context menu:</source>
<target>تخصيص القائمة المحلية:</target>
@@ -1349,8 +1385,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>الا&فتراضي</target>
-<source>Feedback and suggestions are welcome</source>
-<target>التعليقات و الاقتراحات موضع ترحيب</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>ردود الفعل والاقتراحات مرحب بها:</target>
<source>Home page</source>
<target>الصفحة الرئيسية</target>
@@ -1376,8 +1412,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>الرماز المصدري مكتوب بلغة C++‎ باستخدام:</target>
-<source>Published under the GNU General Public License</source>
-<target>نشر تحت رخصة GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>نشر باستخدام رخصة جنو العمومية العامة:</target>
<source>Many thanks for localization:</source>
<target>شكرا جزيلا للترجمة:</target>
@@ -1434,7 +1470,7 @@ This guarantees a consistent state even in case of a serious error.
<target>معلومات</target>
<source>No log entries</source>
-<target></target>
+<target>لا يوجد سجلات</target>
<source>Select all</source>
<target>اختيار الجميع</target>
@@ -1460,6 +1496,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>نظرة عامة</target>
+<source>Swap sides</source>
+<target>مبادلة الجانبين</target>
+
<source>Show "%x"</source>
<target>إظهار "%x"</target>
@@ -1650,9 +1689,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>إظهار الملفات التي تم فلترتها أو استبعادها بشكل مؤقت</target>
-<source>Save as default</source>
-<target>حفظ كافتراضي</target>
-
<source>Filter</source>
<target>عامل الفلترة</target>
@@ -2018,12 +2054,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>فشل تصفح سلة المهملات من أجل الملف %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>عناصر XML التالية لا يمكن قراءتها:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>ملف التكوين %x غير مكتمل. سيتم إعادة تعيين العناصر المفقودة إلى قيمها الافتراضية.</target>
-
<source>Prepare installation</source>
<target>الاستعداد للتثبيت</target>
diff --git a/FreeFileSync/Build/Languages/bulgarian.lng b/FreeFileSync/Build/Languages/bulgarian.lng
index 935fa43f..6334a693 100755
--- a/FreeFileSync/Build/Languages/bulgarian.lng
+++ b/FreeFileSync/Build/Languages/bulgarian.lng
@@ -155,11 +155,14 @@
<target>Размер:</target>
<source>Content comparison was skipped for excluded files.</source>
-<target>Сравняването на съдържанието е прескочено за изключените файлове.</target>
+<target>Сравняването на съдържание се прескача за изключените файлове.</target>
<source>Items differ in attributes only</source>
<target>Елементите се различават само по атрибути</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Името %x се използва от повече от един елемент в папката.</target>
+
<source>Resolving symbolic link %x</source>
<target>Проследява символна връзка %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Толеранс на файловото време</target>
-<source>Folder access timeout</source>
-<target>Време за достъп до папката</target>
-
<source>Run with background priority</source>
<target>Изпълнявай с фонов приоритет</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Актуализирай атрибутите на десния елемент</target>
-<source>Warning</source>
-<target>Предупреждение</target>
+<source>Errors:</source>
+<target>Грешки:</target>
+
+<source>Warnings:</source>
+<target>Предупреждения:</target>
<source>Items processed:</source>
<target>Обработени елементи:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Общо време:</target>
+<source>Warning</source>
+<target>Предупреждение</target>
+
<source>Stopped</source>
<target>Спряно</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Грешка при анализ на файл %x, ред %y, колона %z.</target>
+<source>Services</source>
+<target>Услуги</target>
+
+<source>Show All</source>
+<target>Покажи всички</target>
+
+<source>Hide Others</source>
+<target>Скрий другите</target>
+
+<source>Hide %x</source>
+<target>Скрий %x</target>
+
+<source>Quit %x</source>
+<target>Край на %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Не може да заключи директориите за следните папки:</target>
@@ -370,17 +391,17 @@
<source>Cannot read directory %x.</source>
<target>Не може да прочете директория %x.</target>
-<source>/sec</source>
-<target>/сек.</target>
+<source>%x/sec</source>
+<target>%x/сек.</target>
-<source>%x items/sec</source>
-<target>%x елем./сек.</target>
+<source>%x items</source>
+<target>%x елемента</target>
<source>Show in Explorer</source>
<target>Покажи в Експлорера</target>
<source>Open with default application</source>
-<target>Отвори с приложение по подразбиране</target>
+<target>Отвори с подразбираното приложение</target>
<source>Browse directory</source>
<target>Преглед на директорията</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Създава база данни...</target>
-<source>Searching for excess file versions:</source>
-<target>Търси излишни файлови версии:</target>
+<source>Searching for old file versions:</source>
+<target>Търси стари версии на файлове:</target>
-<source>Removing excess file versions:</source>
-<target>Премахва излишни файлови версии:</target>
+<source>Removing old file versions:</source>
+<target>Премахва стари версии на файлове:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Не може да маркира времето на версиите:</target>
@@ -585,6 +606,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Не може да премести %x в кошчето.</target>
+<source>Unable to access %x.</source>
+<target>Няма достъп до %x.</target>
+
+<source>Authentication completed.</source>
+<target>Аутентификацията приключи.</target>
+
+<source>Authentication failed.</source>
+<target>Аутентификацията неуспешна.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Може да затворите тази страница и да продължите с FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Сървърът връща грешка:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Не може да определи свободното дисково пространство за %x.</target>
+
<source>Cannot find %x.</source>
<target>Не е намерен %x.</target>
@@ -597,18 +636,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Не може да изтрие символната връзка %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Не може да определи свободното дисково пространство за %x.</target>
-
<source>Incorrect command line:</source>
<target>Невалиден команден ред:</target>
<source>The server does not support authentication via %x.</source>
<target>Сървърът не поддържа идентификация чрез %x.</target>
-<source>Unable to access %x.</source>
-<target>Няма достъп до %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Не може да отвори SFTP-канал номер %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 байт</pluralform>
-<pluralform>%x байта</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x МБ</target>
-
-<source>%x KB</source>
-<target>%x КБ</target>
-
-<source>%x GB</source>
-<target>%x ГБ</target>
-
<source>Drag && drop</source>
<target>Влачете и пуснете</target>
@@ -764,6 +779,24 @@ The command is triggered if:
<source>&Retry</source>
<target>&Повтори</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 байт</pluralform>
+<pluralform>%x байта</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x МБ</target>
+
+<source>%x KB</source>
+<target>%x КБ</target>
+
+<source>%x GB</source>
+<target>%x ГБ</target>
+
<source>Loading...</source>
<target>Зареждане...</target>
@@ -909,7 +942,7 @@ The command is triggered if:
<target>Запази &като пакетна задача...</target>
<source>Show &log</source>
-<target></target>
+<target>Покажи &log</target>
<source>Start &comparison</source>
<target>Почни &сравняване</target>
@@ -921,10 +954,10 @@ The command is triggered if:
<target>Настройки на &филтъра</target>
<source>S&ynchronization settings</source>
-<target>Настройки на с&инхронизиране</target>
+<target>Настройки на с&инхронизация</target>
<source>Start &synchronization</source>
-<target>Почни &синхронизиране</target>
+<target>Почни &синхронизация</target>
<source>&Actions</source>
<target>&Действия</target>
@@ -951,7 +984,7 @@ The command is triggered if:
<target>&Провери за обновления сега</target>
<source>Check &automatically once a week</source>
-<target>Провери автоматично &ежеседмично</target>
+<target>Проверявай автоматично &ежеседмично</target>
<source>Cancel</source>
<target>Отказ</target>
@@ -969,10 +1002,7 @@ The command is triggered if:
<target>Отнеми двойка папки</target>
<source>Access online storage</source>
-<target>Достъп до онлайн-запазване</target>
-
-<source>Swap sides</source>
-<target>Размени страните</target>
+<target>Достъп до онлайн-съхранение</target>
<source>Close search bar</source>
<target>Затвори полето за търсене</target>
@@ -998,8 +1028,11 @@ The command is triggered if:
<source>View type:</source>
<target>Тип изглед:</target>
+<source>Save as default</source>
+<target>Запази като подразбирано</target>
+
<source>Select view:</source>
-<target>Избери изглед:</target>
+<target>Изберете изглед:</target>
<source>Statistics:</source>
<target>Статистика:</target>
@@ -1055,6 +1088,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Отчети лятното време</target>
+<source>Ignore errors</source>
+<target>Игнорирай грешките</target>
+
+<source>Retry count:</source>
+<target>Брой повторения:</target>
+
+<source>Delay (in seconds):</source>
+<target>Задръжка (сек.):</target>
+
<source>Performance improvements:</source>
<target>Подобряване на производителността:</target>
@@ -1129,17 +1171,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>Последните x дни:</target>
-<source>Ignore errors</source>
-<target>Игнорирай грешките</target>
-
-<source>Retry count:</source>
-<target>Брой повторения:</target>
-
-<source>Delay (in seconds):</source>
-<target>Задръжка (сек.):</target>
+<source>&Override default log path:</source>
+<target>&Замени подразбирания log-път:</target>
-<source>Run a command after synchronization:</source>
-<target>След синхронизация изпълни команда:</target>
+<source>Run a command:</source>
+<target>Изпълни команда:</target>
<source>OK</source>
<target>ОК</target>
@@ -1189,6 +1225,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Директория на сървъра:</target>
+<source>Access timeout (in seconds):</source>
+<target>Време за достъп (в секунди):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP-канали на връзка:</target>
@@ -1267,12 +1306,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>Спри синхронизацията при първа грешка</target>
-<source>Save log:</source>
-<target>Запази протокол:</target>
-
-<source>Limit number of log files:</source>
-<target>Ограничи броя протоколни файлове:</target>
-
<source>How can I schedule a batch job?</source>
<target>Как да планирам пакетна задача?</target>
@@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Покажи всички постоянно скрити диалози и предупреждения отново</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Подразбиран log-път:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Изтрий протоколите след x дни:</target>
<source>Customize context menu:</source>
<target>Настрой контекстното меню:</target>
@@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&По подразбиране</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Забележки и предложения са добре дошли</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Отзиви и предложения са добре дошли:</target>
<source>Home page</source>
<target>Домашна страница</target>
@@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>Изходния код е написан на C++ със:</target>
-<source>Published under the GNU General Public License</source>
-<target>Публикува се по лиценза GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Публикуван под GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Благодарност за локализацията:</target>
@@ -1358,7 +1394,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Активирайте дарителско издание на FreeFileSync по един от следните методи:</target>
<source>1. Activate via internet now:</source>
-<target>1. Активиране по Интернет:</target>
+<target>1. Активирайте по Интернет сега:</target>
<source>Activate online</source>
<target>Активирайте онлайн</target>
@@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Информация</target>
<source>No log entries</source>
-<target></target>
+<target>Няма log-записи</target>
<source>Select all</source>
<target>Маркирай всичко</target>
@@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>Преглед</target>
+<source>Swap sides</source>
+<target>Размени страните</target>
+
<source>Show "%x"</source>
<target>Покажи "%x"</target>
@@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>Покажи филтрираните или временно изключени файлове</target>
-<source>Save as default</source>
-<target>Запази като подразбирано</target>
-
<source>Filter</source>
<target>Филтър</target>
@@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>Неуспешна проверка на кошчето за папка %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Не могат да се прочетат следните XML-елементи:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Конфигурационният файл %x е непълен. Ще бъдат зададени подразбирани стойности за липсващите елементи.</target>
-
<source>Prepare installation</source>
<target>Подготовка за инсталиране</target>
diff --git a/FreeFileSync/Build/Languages/chinese_simple.lng b/FreeFileSync/Build/Languages/chinese_simple.lng
index a4350d39..5716cb9a 100755
--- a/FreeFileSync/Build/Languages/chinese_simple.lng
+++ b/FreeFileSync/Build/Languages/chinese_simple.lng
@@ -159,6 +159,9 @@
<source>Items differ in attributes only</source>
<target>项目仅是文件属性不同</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>%x 此名称在文件夹中被一个以上的项目所使用.</target>
+
<source>Resolving symbolic link %x</source>
<target>正在解决符号连接 %x</target>
@@ -186,9 +189,6 @@
<source>File time tolerance</source>
<target>文件时间容差</target>
-<source>Folder access timeout</source>
-<target>文件夹访问超时</target>
-
<source>Run with background priority</source>
<target>以后台优先级运行</target>
@@ -332,8 +332,11 @@
<source>Update attributes on right</source>
<target>更新右侧的文件属性</target>
-<source>Warning</source>
-<target>警告</target>
+<source>Errors:</source>
+<target>错误:</target>
+
+<source>Warnings:</source>
+<target>警告:</target>
<source>Items processed:</source>
<target>已处理的项目:</target>
@@ -344,6 +347,9 @@
<source>Total time:</source>
<target>总共时间:</target>
+<source>Warning</source>
+<target>警告</target>
+
<source>Stopped</source>
<target>已停止</target>
@@ -353,6 +359,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>当分析文件 %x , 行 %y, 列 %z 时出错.</target>
+<source>Services</source>
+<target>服务</target>
+
+<source>Show All</source>
+<target>显示所有</target>
+
+<source>Hide Others</source>
+<target>隐藏其他</target>
+
+<source>Hide %x</source>
+<target>隐藏 %x</target>
+
+<source>Quit %x</source>
+<target>退出 %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>无法为如下文件夹设置 目录锁定:</target>
@@ -367,11 +388,11 @@
<source>Cannot read directory %x.</source>
<target>无法读取目录 %x.</target>
-<source>/sec</source>
-<target>/秒</target>
+<source>%x/sec</source>
+<target>%x/秒</target>
-<source>%x items/sec</source>
-<target>%x 个项目/秒</target>
+<source>%x items</source>
+<target>%x 个项目</target>
<source>Show in Explorer</source>
<target>在Explorer中显示</target>
@@ -520,11 +541,11 @@
<source>Generating database...</source>
<target>正在生成数据库...</target>
-<source>Searching for excess file versions:</source>
-<target>正在搜索多余的文件版本:</target>
+<source>Searching for old file versions:</source>
+<target>正在搜索旧的文件版本:</target>
-<source>Removing excess file versions:</source>
-<target>正在删除多余的文件版本:</target>
+<source>Removing old file versions:</source>
+<target>正在移除旧的文件版本:</target>
<source>Unable to create time stamp for versioning:</source>
<target>无法为历史版本创建时间戳:</target>
@@ -582,6 +603,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>无法将 %x 移动到回收站.</target>
+<source>Unable to access %x.</source>
+<target>无法访问 %x.</target>
+
+<source>Authentication completed.</source>
+<target>验证完成.</target>
+
+<source>Authentication failed.</source>
+<target>验证失败.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>你可以现在关闭本页面并继续使用 FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>服务器返回一个错误:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>无法确定 %x 上的可用磁盘空间.</target>
+
<source>Cannot find %x.</source>
<target>无法找到 %x.</target>
@@ -594,18 +633,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>无法删除符号链接 %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>无法确定 %x 上的可用磁盘空间.</target>
-
<source>Incorrect command line:</source>
<target>不正确的命令行:</target>
<source>The server does not support authentication via %x.</source>
<target>此服务器并不支持通过 %x 进行认证.</target>
-<source>Unable to access %x.</source>
-<target>无法访问 %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -628,23 +661,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>打开SFTP通道号 %x 时失败.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x 字节</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>拖放</target>
@@ -758,6 +774,23 @@ The command is triggered if:
<source>&Retry</source>
<target>重试(&R)</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x 字节</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>正在载入...</target>
@@ -902,7 +935,7 @@ The command is triggered if:
<target>另存为批处理作业(&B)...</target>
<source>Show &log</source>
-<target></target>
+<target>显示日志(&L)</target>
<source>Start &comparison</source>
<target>开始比较(&C)</target>
@@ -964,9 +997,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>访问在线存储空间</target>
-<source>Swap sides</source>
-<target>两侧互换</target>
-
<source>Close search bar</source>
<target>关闭搜索条</target>
@@ -991,6 +1021,9 @@ The command is triggered if:
<source>View type:</source>
<target>视图类型:</target>
+<source>Save as default</source>
+<target>保存为默认值</target>
+
<source>Select view:</source>
<target>选择视图:</target>
@@ -1048,6 +1081,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>处理夏令时</target>
+<source>Ignore errors</source>
+<target>忽略错误</target>
+
+<source>Retry count:</source>
+<target>重试计数:</target>
+
+<source>Delay (in seconds):</source>
+<target>延时 (秒):</target>
+
<source>Performance improvements:</source>
<target>性能改进:</target>
@@ -1122,17 +1164,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>最近 x 天:</target>
-<source>Ignore errors</source>
-<target>忽略错误</target>
-
-<source>Retry count:</source>
-<target>重试计数:</target>
-
-<source>Delay (in seconds):</source>
-<target>延时 (秒):</target>
+<source>&Override default log path:</source>
+<target>覆盖默认日志路径(&O):</target>
-<source>Run a command after synchronization:</source>
-<target>同步之后运行一个命令:</target>
+<source>Run a command:</source>
+<target>执行一个命令:</target>
<source>OK</source>
<target>确定</target>
@@ -1182,6 +1218,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>服务器上的目录:</target>
+<source>Access timeout (in seconds):</source>
+<target>访问超时(秒):</target>
+
<source>SFTP channels per connection:</source>
<target>每个连接的SFTP通道数:</target>
@@ -1260,12 +1299,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>在遇到第一个错误时停止同步</target>
-<source>Save log:</source>
-<target>保存日志:</target>
-
-<source>Limit number of log files:</source>
-<target>限制日志文件数量:</target>
-
<source>How can I schedule a batch job?</source>
<target>我如何计划一个批处理作业?</target>
@@ -1299,8 +1332,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>重新显示所有被永久性隐藏的 对话框和警告信息</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>默认日志路径:</target>
+
+<source>&Delete logs after x days:</source>
+<target>在 x 天后删除日志(&D):</target>
<source>Customize context menu:</source>
<target>自定义右键菜单:</target>
@@ -1311,8 +1347,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>默认(&D)</target>
-<source>Feedback and suggestions are welcome</source>
-<target>欢迎反馈意见和提出建议</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>欢迎提出反馈意见和建议:</target>
<source>Home page</source>
<target>主页</target>
@@ -1338,8 +1374,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>源代码使用如下工具由C++写成:</target>
-<source>Published under the GNU General Public License</source>
-<target>在GNU通用公共许可下发布</target>
+<source>Published under the GNU General Public License:</source>
+<target>根据GNU通用公共许可证发布:</target>
<source>Many thanks for localization:</source>
<target>非常感谢以下本地化翻译者:</target>
@@ -1396,7 +1432,7 @@ This guarantees a consistent state even in case of a serious error.
<target>信息</target>
<source>No log entries</source>
-<target></target>
+<target>没有日志条目</target>
<source>Select all</source>
<target>选择全部</target>
@@ -1422,6 +1458,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>摘要</target>
+<source>Swap sides</source>
+<target>两侧互换</target>
+
<source>Show "%x"</source>
<target>显示 "%x"</target>
@@ -1592,9 +1631,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>显示已被过滤或被临时排除的文件</target>
-<source>Save as default</source>
-<target>保存为默认值</target>
-
<source>Filter</source>
<target>过滤器</target>
@@ -1935,12 +1971,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>检查文件夹 %x 的回收站失败.</target>
-<source>The following XML elements could not be read:</source>
-<target>下列的XML元素无法被读取:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>配置文件 %x 不完整. 丢失的元素将被设置为它们的默认值.</target>
-
<source>Prepare installation</source>
<target>准备安装</target>
diff --git a/FreeFileSync/Build/Languages/chinese_traditional.lng b/FreeFileSync/Build/Languages/chinese_traditional.lng
index 2dc930c4..2705c499 100755
--- a/FreeFileSync/Build/Languages/chinese_traditional.lng
+++ b/FreeFileSync/Build/Languages/chinese_traditional.lng
@@ -159,6 +159,9 @@
<source>Items differ in attributes only</source>
<target>只有項目的屬性不同</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>名稱 %x 已被資料夾中的多個項目使用。</target>
+
<source>Resolving symbolic link %x</source>
<target>正在解析符號連結 %x</target>
@@ -186,9 +189,6 @@
<source>File time tolerance</source>
<target>檔案時間容差範圍</target>
-<source>Folder access timeout</source>
-<target>資料夾存取超時</target>
-
<source>Run with background priority</source>
<target>背景優先執行</target>
@@ -226,7 +226,7 @@
<target>資料庫檔案 %x 不相容。</target>
<source>Initial synchronization:</source>
-<target>初始化同步:</target>
+<target>初始同步:</target>
<source>Database file %x does not yet exist.</source>
<target>資料庫檔案 %x 不存在。</target>
@@ -253,7 +253,7 @@
<target>正在搜尋資料夾 %x…</target>
<source>Timeout while searching for folder %x.</source>
-<target>搜尋資料夾 %x 時超時。</target>
+<target>搜尋資料夾 %x 時逾時。</target>
<source>Cannot get process information.</source>
<target>無法取得處理訊息。</target>
@@ -332,8 +332,11 @@
<source>Update attributes on right</source>
<target>更新右邊的屬性</target>
-<source>Warning</source>
-<target>警告</target>
+<source>Errors:</source>
+<target>錯誤:</target>
+
+<source>Warnings:</source>
+<target>警告:</target>
<source>Items processed:</source>
<target>已處理項目:</target>
@@ -344,15 +347,33 @@
<source>Total time:</source>
<target>全部時間:</target>
+<source>Warning</source>
+<target>警告</target>
+
<source>Stopped</source>
<target>已停止</target>
<source>Cleaning up log files:</source>
-<target>清除日誌檔:</target>
+<target>清除紀錄檔:</target>
<source>Error parsing file %x, row %y, column %z.</source>
<target>解析 %x 檔案,第 %y 列,第 %z 行出現錯誤。</target>
+<source>Services</source>
+<target>服務</target>
+
+<source>Show All</source>
+<target>顯示全部</target>
+
+<source>Hide Others</source>
+<target>隱藏其他</target>
+
+<source>Hide %x</source>
+<target>隱藏 %x</target>
+
+<source>Quit %x</source>
+<target>結束 %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>無法為以下資料夾設定目錄鎖定:</target>
@@ -361,17 +382,17 @@
<pluralform>%x threads</pluralform>
</source>
<target>
-<pluralform>%x 執行緒</pluralform>
+<pluralform>%x 個執行緒</pluralform>
</target>
<source>Cannot read directory %x.</source>
<target>無法讀取目錄 %x。</target>
-<source>/sec</source>
-<target>(秒)</target>
+<source>%x/sec</source>
+<target>%x/秒</target>
-<source>%x items/sec</source>
-<target>%x 個項目(秒)</target>
+<source>%x items</source>
+<target>%x 項目</target>
<source>Show in Explorer</source>
<target>在資源管理器中顯示</target>
@@ -520,11 +541,11 @@
<source>Generating database...</source>
<target>正在產生資料庫…</target>
-<source>Searching for excess file versions:</source>
-<target>正在搜尋多餘的檔案版本:</target>
+<source>Searching for old file versions:</source>
+<target>正在搜尋舊檔版本:</target>
-<source>Removing excess file versions:</source>
-<target>正在刪除多餘的檔案版本:</target>
+<source>Removing old file versions:</source>
+<target>正在移除舊檔版本:</target>
<source>Unable to create time stamp for versioning:</source>
<target>無法建立時間戳記的版本控制:</target>
@@ -582,6 +603,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>無法將 %x 移動到資源回收筒。</target>
+<source>Unable to access %x.</source>
+<target>無法存取 %x。</target>
+
+<source>Authentication completed.</source>
+<target>認證完成。</target>
+
+<source>Authentication failed.</source>
+<target>認證失敗。</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>您可以立即關閉此頁面並繼續使用FreeFileSync。</target>
+
+<source>The server returned an error:</source>
+<target>伺服器傳回錯誤:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>無法確定 %x 的可用磁碟空間。</target>
+
<source>Cannot find %x.</source>
<target>找不到 %x。</target>
@@ -594,24 +633,18 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>無法刪除符號連結 %x。</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>無法確定 %x 的可用磁碟空間。</target>
-
<source>Incorrect command line:</source>
<target>不正確的命令列:</target>
<source>The server does not support authentication via %x.</source>
-<target>伺服器不支援透過 %x 進行身份驗證。</target>
-
-<source>Unable to access %x.</source>
-<target>無法存取 %x。</target>
+<target>伺服器不支援透過 %x 進行認證。</target>
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
</source>
<target>
-<pluralform>在 %x 秒後操作超時。</pluralform>
+<pluralform>在 %x 秒後操作逾時。</pluralform>
</target>
<source>
@@ -628,23 +661,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>無法開啟SFTP通道號 %x。</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x 位元組</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>拖放</target>
@@ -758,6 +774,23 @@ The command is triggered if:
<source>&Retry</source>
<target>重試(&R)</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x 位元組</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>正在載入…</target>
@@ -824,7 +857,7 @@ The command is triggered if:
<target>上次同步</target>
<source>Log</source>
-<target>日誌</target>
+<target>紀錄</target>
<source>Folder</source>
<target>資料夾</target>
@@ -902,7 +935,7 @@ The command is triggered if:
<target>另存為批次工作(&B)…</target>
<source>Show &log</source>
-<target></target>
+<target>顯示紀錄(&L)</target>
<source>Start &comparison</source>
<target>開始比對(&C)</target>
@@ -964,9 +997,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>存取線上儲存空間</target>
-<source>Swap sides</source>
-<target>兩邊交換</target>
-
<source>Close search bar</source>
<target>關閉搜尋欄位</target>
@@ -991,6 +1021,9 @@ The command is triggered if:
<source>View type:</source>
<target>檢視類型:</target>
+<source>Save as default</source>
+<target>儲存為預設值</target>
+
<source>Select view:</source>
<target>選擇視圖:</target>
@@ -1028,7 +1061,7 @@ The command is triggered if:
<target>包含符號連結(&S):</target>
<source>&Follow</source>
-<target>跟隨(&F)</target>
+<target>追隨(&F)</target>
<source>&Direct</source>
<target>直接(&D)</target>
@@ -1048,6 +1081,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>處理日光節約時間</target>
+<source>Ignore errors</source>
+<target>忽略錯誤</target>
+
+<source>Retry count:</source>
+<target>重試次數:</target>
+
+<source>Delay (in seconds):</source>
+<target>延遲(以秒為單位):</target>
+
<source>Performance improvements:</source>
<target>效能改善:</target>
@@ -1122,17 +1164,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>最近 x 天:</target>
-<source>Ignore errors</source>
-<target>忽略錯誤</target>
-
-<source>Retry count:</source>
-<target>重試次數:</target>
-
-<source>Delay (in seconds):</source>
-<target>延遲(以秒為單位):</target>
+<source>&Override default log path:</source>
+<target>覆蓋預設紀錄路徑(&O):</target>
-<source>Run a command after synchronization:</source>
-<target>同步後執行一個命令:</target>
+<source>Run a command:</source>
+<target>執行命令:</target>
<source>OK</source>
<target>確定</target>
@@ -1159,7 +1195,7 @@ The command is triggered if:
<target>外顯式SSL/TLS(&E)</target>
<source>Authentication:</source>
-<target>身份驗證:</target>
+<target>認證:</target>
<source>&Password</source>
<target>密碼(&P):</target>
@@ -1182,6 +1218,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>在伺服器上的目錄:</target>
+<source>Access timeout (in seconds):</source>
+<target>存取逾時(以秒為單位):</target>
+
<source>SFTP channels per connection:</source>
<target>每個連接SFTP通道數:</target>
@@ -1260,12 +1299,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>在第一個錯誤時停止同步</target>
-<source>Save log:</source>
-<target>儲存日誌:</target>
-
-<source>Limit number of log files:</source>
-<target>限制日誌檔數量:</target>
-
<source>How can I schedule a batch job?</source>
<target>如何排程批次工作?</target>
@@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>再次顯示所有永久 隱藏的對話框和警告訊息</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>預設紀錄路徑:</target>
+
+<source>&Delete logs after x days:</source>
+<target>在 x 天後刪除紀錄(&D):</target>
<source>Customize context menu:</source>
<target>自訂內容功能表:</target>
@@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>預設(&D)</target>
-<source>Feedback and suggestions are welcome</source>
-<target>歡迎反映意見和建議</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>歡迎回饋和建議:</target>
<source>Home page</source>
<target>主頁</target>
@@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>使用C++編寫的原始碼:</target>
-<source>Published under the GNU General Public License</source>
-<target>在GNU通用公共許可證下發佈</target>
+<source>Published under the GNU General Public License:</source>
+<target>根據GNU通用公共授權條款發佈:</target>
<source>Many thanks for localization:</source>
<target>非常感謝在地化翻譯人員:</target>
@@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error.
<target>訊息</target>
<source>No log entries</source>
-<target></target>
+<target>沒有紀錄</target>
<source>Select all</source>
<target>全選</target>
@@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>摘要</target>
+<source>Swap sides</source>
+<target>兩邊交換</target>
+
<source>Show "%x"</source>
<target>顯示「%x」</target>
@@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>顯示已篩選或暫時排除的檔案</target>
-<source>Save as default</source>
-<target>儲存為預設值</target>
-
<source>Filter</source>
<target>篩選器</target>
@@ -1644,7 +1680,7 @@ This guarantees a consistent state even in case of a serious error.
<target>進度</target>
<source>Thank you, %x, for your donation and support!</source>
-<target>%x, 感謝您的贊助與支持!</target>
+<target>%x,感謝您的贊助與支持!</target>
<source>Connections</source>
<target>連線數</target>
@@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>檢查資源回收筒的資料夾 %x 失敗。</target>
-<source>The following XML elements could not be read:</source>
-<target>無法讀取下列XML元件:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>配置檔案 %x 不完整。缺少的元件將設定為其本身預設值。</target>
-
<source>Prepare installation</source>
<target>準備安裝</target>
@@ -2014,7 +2044,7 @@ This guarantees a consistent state even in case of a serious error.
<target>使用FreeFileSync進行編輯</target>
<source>Instead of an ad, here's an animal.</source>
-<target>這是一隻動物,而不是廣告。</target>
+<target>這是一隻動物,不是廣告。</target>
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>FreeFileSync可攜式版本無法安裝到 %x 的子資料夾。</target>
diff --git a/FreeFileSync/Build/Languages/croatian.lng b/FreeFileSync/Build/Languages/croatian.lng
index 78107ef3..ee6721a2 100755
--- a/FreeFileSync/Build/Languages/croatian.lng
+++ b/FreeFileSync/Build/Languages/croatian.lng
@@ -161,6 +161,9 @@
<source>Items differ in attributes only</source>
<target>Stavke se razlikuju samo u atributima</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Naziv %x se koristi za više od jedne stavke u mapi.</target>
+
<source>Resolving symbolic link %x</source>
<target>Rješavam simboličku vezu %x</target>
@@ -188,9 +191,6 @@
<source>File time tolerance</source>
<target>Tolerancija vremena datoteke</target>
-<source>Folder access timeout</source>
-<target>Istek vremena pristupa mapi</target>
-
<source>Run with background priority</source>
<target>Pokreni s pozadinskim prioritetom</target>
@@ -336,8 +336,11 @@
<source>Update attributes on right</source>
<target>Osvježi atribute desno</target>
-<source>Warning</source>
-<target>Upozorenje</target>
+<source>Errors:</source>
+<target>Pogreške:</target>
+
+<source>Warnings:</source>
+<target>Upozorenja:</target>
<source>Items processed:</source>
<target>Obrađene stavke:</target>
@@ -348,6 +351,9 @@
<source>Total time:</source>
<target>Ukupno vrijeme:</target>
+<source>Warning</source>
+<target>Upozorenje</target>
+
<source>Stopped</source>
<target>Zaustavljeno</target>
@@ -357,6 +363,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Greška u analizi datoteke %x, red %y, stupac %z.</target>
+<source>Services</source>
+<target>Servisi</target>
+
+<source>Show All</source>
+<target>Prikaži sve</target>
+
+<source>Hide Others</source>
+<target>Sakrij ostale</target>
+
+<source>Hide %x</source>
+<target>Sakrij %x</target>
+
+<source>Quit %x</source>
+<target>Napusti %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Nije moguće zaključavanje slijedećih foldera:</target>
@@ -373,11 +394,11 @@
<source>Cannot read directory %x.</source>
<target>Nije moguće učitati mapu %x.</target>
-<source>/sec</source>
-<target>/sek</target>
+<source>%x/sec</source>
+<target>%x/sek</target>
-<source>%x items/sec</source>
-<target>%x stavki/sek</target>
+<source>%x items</source>
+<target>%x stavki</target>
<source>Show in Explorer</source>
<target>Prikaži u Exploreru</target>
@@ -526,11 +547,11 @@
<source>Generating database...</source>
<target>Izrađivanje baze podataka...</target>
-<source>Searching for excess file versions:</source>
-<target>Traženje višestrukih verzija datoteka:</target>
+<source>Searching for old file versions:</source>
+<target>Pretraživanje starih verzija datoteka:</target>
-<source>Removing excess file versions:</source>
-<target>Uklanjanje višestrukih verzija datoteka:</target>
+<source>Removing old file versions:</source>
+<target>Uklanjanje starih verzija datoteka:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Nije moguća izrada vremenske oznake za označavanje:</target>
@@ -588,6 +609,24 @@ Stvarno: %y bajta
<source>Unable to move %x to the recycle bin.</source>
<target>Nije moguće premjestiti %x u koš za smeće.</target>
+<source>Unable to access %x.</source>
+<target>Nije moguće pristupiti %x.</target>
+
+<source>Authentication completed.</source>
+<target>Provjera autentičnosti dovršena.</target>
+
+<source>Authentication failed.</source>
+<target>Provjera autentičnosti nije uspjela.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Sada možete zatvoriti ovu stranicu i nastaviti sa FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Pogreška poslužitelja:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Nije moguće izračunati slobodan prostor diska za %x.</target>
+
<source>Cannot find %x.</source>
<target>Nije moguće pronaći %x.</target>
@@ -600,18 +639,12 @@ Stvarno: %y bajta
<source>Cannot delete symbolic link %x.</source>
<target>Nije moguće brisanje simbolične poveznice %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Nije moguće izračunati slobodan prostor diska za %x.</target>
-
<source>Incorrect command line:</source>
<target>Netočna naredbena linija:</target>
<source>The server does not support authentication via %x.</source>
<target>Server ne podržava provjeru autentičnosti preko %x.</target>
-<source>Unable to access %x.</source>
-<target>Nije moguće pristupiti %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -638,25 +671,6 @@ Stvarno: %y bajta
<source>Failed to open SFTP channel number %x.</source>
<target>Nije uspjelo povezivanje na SFTP kanal broj %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x bajt</pluralform>
-<pluralform>%x bajta</pluralform>
-<pluralform>%x bajtova</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Povuci && ispusti</target>
@@ -770,6 +784,25 @@ Naredba će biti pokrenuta ako se:
<source>&Retry</source>
<target>&Ponovi</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x bajt</pluralform>
+<pluralform>%x bajta</pluralform>
+<pluralform>%x bajtova</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Učitavam...</target>
@@ -916,7 +949,7 @@ Naredba će biti pokrenuta ako se:
<target>Spremi kao &Slijedni zadatak...</target>
<source>Show &log</source>
-<target></target>
+<target>Prikaži &zapisnik</target>
<source>Start &comparison</source>
<target>Započni &usporedbu</target>
@@ -978,9 +1011,6 @@ Naredba će biti pokrenuta ako se:
<source>Access online storage</source>
<target>Pristupi online pohrani</target>
-<source>Swap sides</source>
-<target>Zamjeni strane</target>
-
<source>Close search bar</source>
<target>Zatvori traku pretraživanja</target>
@@ -1005,6 +1035,9 @@ Naredba će biti pokrenuta ako se:
<source>View type:</source>
<target>Vrsta prikaza:</target>
+<source>Save as default</source>
+<target>Spremi kao standardno</target>
+
<source>Select view:</source>
<target>Odaberite prikaz:</target>
@@ -1062,6 +1095,15 @@ Naredba će biti pokrenuta ako se:
<source>Handle daylight saving time</source>
<target>Upravljaj ljetnim računanjem vremena</target>
+<source>Ignore errors</source>
+<target>Zanemariti pogreške</target>
+
+<source>Retry count:</source>
+<target>Broj pokušaja:</target>
+
+<source>Delay (in seconds):</source>
+<target>Odgoda (u sekundama):</target>
+
<source>Performance improvements:</source>
<target>Poboljšanje perfomansi:</target>
@@ -1136,17 +1178,11 @@ Naredba će biti pokrenuta ako se:
<source>Last x days:</source>
<target>Zadnjih nekoliko dana:</target>
-<source>Ignore errors</source>
-<target>Zanemariti pogreške</target>
-
-<source>Retry count:</source>
-<target>Broj pokušaja:</target>
-
-<source>Delay (in seconds):</source>
-<target>Odgoda (u sekundama):</target>
+<source>&Override default log path:</source>
+<target>&Prepiši zadanu putanju zapisnika:</target>
-<source>Run a command after synchronization:</source>
-<target>Pokreni naredbu nakon sinkronizacije:</target>
+<source>Run a command:</source>
+<target>Izvrši naredbu:</target>
<source>OK</source>
<target>U redu</target>
@@ -1196,6 +1232,9 @@ Naredba će biti pokrenuta ako se:
<source>Directory on server:</source>
<target>Mapa na serveru:</target>
+<source>Access timeout (in seconds):</source>
+<target>Prekid pristupa nakon (u sekundama):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP kanala po konekciji:</target>
@@ -1274,12 +1313,6 @@ Naredba će biti pokrenuta ako se:
<source>Stop synchronization at first error</source>
<target>Zaustavi sinkronizaciju pri prvoj pogrešci</target>
-<source>Save log:</source>
-<target>Spremi izvještaj:</target>
-
-<source>Limit number of log files:</source>
-<target>Limitiranje broja log datoteka:</target>
-
<source>How can I schedule a batch job?</source>
<target>Kako zakazati slijedni zadatak?</target>
@@ -1316,8 +1349,11 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Prikaži sve trajno skrivene prozore i poruke upozorenja ponovno</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Zadana putanja zapisnika:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Izbrisati zapisnike nakon x dana:</target>
<source>Customize context menu:</source>
<target>Prilagodite kontekstni izbornik:</target>
@@ -1328,8 +1364,8 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške.
<source>&Default</source>
<target>&Zadano</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Povratne informacije i prijedlozi su dobrodošli</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Povratne informacije i prijedlozi su dobrodošli:</target>
<source>Home page</source>
<target>Web stranica</target>
@@ -1355,8 +1391,8 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške.
<source>Source code written in C++ using:</source>
<target>Izvorni kod napisan u C++ uz korištenje:</target>
-<source>Published under the GNU General Public License</source>
-<target>Objavljeno pod licencom GNU General Public</target>
+<source>Published under the GNU General Public License:</source>
+<target>Objavljeno pod GNU General Public Licencom:</target>
<source>Many thanks for localization:</source>
<target>Velike zahvale idu:</target>
@@ -1413,7 +1449,7 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Nema izvještaja</target>
<source>Select all</source>
<target>Odaberi sve</target>
@@ -1439,6 +1475,9 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške.
<source>Overview</source>
<target>Pregled</target>
+<source>Swap sides</source>
+<target>Zamjeni strane</target>
+
<source>Show "%x"</source>
<target>Prikaži "%x"</target>
@@ -1617,9 +1656,6 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške.
<source>Show filtered or temporarily excluded files</source>
<target>Prikaži filtrirane ili privremeno odvojene datoteke</target>
-<source>Save as default</source>
-<target>Spremi kao standardno</target>
-
<source>Filter</source>
<target>Filtriranje</target>
@@ -1970,12 +2006,6 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške.
<source>Checking recycle bin failed for folder %x.</source>
<target>Provjeravanje koša za smeće neuspješno za mapu %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Slijedeći XML elementi se nisu mogli učitati:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Konfiguracijska datoteka %x je nepotpuna. Nedostajući elementi će biti postavljeni na početne vrijednosti.</target>
-
<source>Prepare installation</source>
<target>Pripremam instalaciju</target>
diff --git a/FreeFileSync/Build/Languages/czech.lng b/FreeFileSync/Build/Languages/czech.lng
index 3403389a..1cf9fb65 100755
--- a/FreeFileSync/Build/Languages/czech.lng
+++ b/FreeFileSync/Build/Languages/czech.lng
@@ -161,6 +161,9 @@
<source>Items differ in attributes only</source>
<target>Položky se liší pouze ve vlastnostech</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Název %x je použit více jak jednou položkou ve složce.</target>
+
<source>Resolving symbolic link %x</source>
<target>Hledání odkazu symbolického zástupce %x</target>
@@ -188,9 +191,6 @@
<source>File time tolerance</source>
<target>Časová tolerance</target>
-<source>Folder access timeout</source>
-<target>Časový limit přístupu ke složce</target>
-
<source>Run with background priority</source>
<target>Spuštění s prioritou na pozadí</target>
@@ -336,8 +336,11 @@
<source>Update attributes on right</source>
<target>Nastavit vlastnosti vpravo</target>
-<source>Warning</source>
-<target>Varování</target>
+<source>Errors:</source>
+<target>Chyb:</target>
+
+<source>Warnings:</source>
+<target>Varování:</target>
<source>Items processed:</source>
<target>Zpracováno položek:</target>
@@ -348,6 +351,9 @@
<source>Total time:</source>
<target>Celkový čas:</target>
+<source>Warning</source>
+<target>Varování</target>
+
<source>Stopped</source>
<target>Zastaveno</target>
@@ -357,6 +363,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Chyba zpracování souboru %x: na řádku %y ve sloupci %z.</target>
+<source>Services</source>
+<target>Služby</target>
+
+<source>Show All</source>
+<target>Zobrazit vše</target>
+
+<source>Hide Others</source>
+<target>Skrýt ostatní</target>
+
+<source>Hide %x</source>
+<target>Skrýt %x</target>
+
+<source>Quit %x</source>
+<target>Ukončit %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Nelze uzamčít následující adresáře:</target>
@@ -373,11 +394,11 @@
<source>Cannot read directory %x.</source>
<target>Nelze číst adresář %x.</target>
-<source>/sec</source>
-<target>/s</target>
+<source>%x/sec</source>
+<target>%x/s</target>
-<source>%x items/sec</source>
-<target>%x položek/s</target>
+<source>%x items</source>
+<target>%x položek</target>
<source>Show in Explorer</source>
<target>Zobrazit v Průzkumníkovi</target>
@@ -526,11 +547,11 @@
<source>Generating database...</source>
<target>Vytváření databáze...</target>
-<source>Searching for excess file versions:</source>
-<target>Hledání nadbytečných verzí:</target>
+<source>Searching for old file versions:</source>
+<target>Hledání starších verzí:</target>
-<source>Removing excess file versions:</source>
-<target>Odstraňování nadbytečných verzí:</target>
+<source>Removing old file versions:</source>
+<target>Odstraňování starších verzí:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Nelze vytvořit časové značky verzování:</target>
@@ -588,6 +609,24 @@ Aktuálně: %y b
<source>Unable to move %x to the recycle bin.</source>
<target>Není možné přesunout %x do Koše.</target>
+<source>Unable to access %x.</source>
+<target>Nepodařil se přístup k %x.</target>
+
+<source>Authentication completed.</source>
+<target>Přístup povolen.</target>
+
+<source>Authentication failed.</source>
+<target>Přístup odmítnut.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Nyní můžete tuto stránku zavřít a pokračovat s FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Server vrátil chybu:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Nelze zjistit volné místo na disku %x.</target>
+
<source>Cannot find %x.</source>
<target>Nelze najít %x.</target>
@@ -600,18 +639,12 @@ Aktuálně: %y b
<source>Cannot delete symbolic link %x.</source>
<target>Nelze smazat symbolický odkaz %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Nelze zjistit volné místo na disku %x.</target>
-
<source>Incorrect command line:</source>
<target>Neplatný příkaz:</target>
<source>The server does not support authentication via %x.</source>
<target>Server nepodporuje ověření pomocí %x.</target>
-<source>Unable to access %x.</source>
-<target>Nepodařil se přístup k %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -638,25 +671,6 @@ Aktuálně: %y b
<source>Failed to open SFTP channel number %x.</source>
<target>Nelze otevřít SFTP kanál %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x B</pluralform>
-<pluralform>%x B</pluralform>
-<pluralform>%x B</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Přetáhni sem && pusť</target>
@@ -770,6 +784,25 @@ Příkaz je spuštěn když:
<source>&Retry</source>
<target>&Opakovat</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x B</pluralform>
+<pluralform>%x B</pluralform>
+<pluralform>%x B</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Načítání...</target>
@@ -916,7 +949,7 @@ Příkaz je spuštěn když:
<target>Uložit jako &dávku...</target>
<source>Show &log</source>
-<target></target>
+<target>&Zobrazit žurnál</target>
<source>Start &comparison</source>
<target>Začít &porovnání</target>
@@ -978,9 +1011,6 @@ Příkaz je spuštěn když:
<source>Access online storage</source>
<target>Přístup k uložišti</target>
-<source>Swap sides</source>
-<target>Změna stran</target>
-
<source>Close search bar</source>
<target>Zavřít hledání</target>
@@ -1005,6 +1035,9 @@ Příkaz je spuštěn když:
<source>View type:</source>
<target>Typ zobrazení:</target>
+<source>Save as default</source>
+<target>Uložit jako výchozí</target>
+
<source>Select view:</source>
<target>Výběr zobrazení:</target>
@@ -1062,6 +1095,15 @@ Příkaz je spuštěn když:
<source>Handle daylight saving time</source>
<target>Používat letní čas</target>
+<source>Ignore errors</source>
+<target>Přeskočit chyby</target>
+
+<source>Retry count:</source>
+<target>Počet opakování:</target>
+
+<source>Delay (in seconds):</source>
+<target>Prodleva (v sekundách):</target>
+
<source>Performance improvements:</source>
<target>Vylepšení rychlosti:</target>
@@ -1136,17 +1178,11 @@ Příkaz je spuštěn když:
<source>Last x days:</source>
<target>Posledních dní:</target>
-<source>Ignore errors</source>
-<target>Přeskočit chyby</target>
-
-<source>Retry count:</source>
-<target>Počet opakování:</target>
-
-<source>Delay (in seconds):</source>
-<target>Prodleva (v sekundách):</target>
+<source>&Override default log path:</source>
+<target>&Nahradit výchozí cestu žurnálu:</target>
-<source>Run a command after synchronization:</source>
-<target>Po dokončení synchronizace spustit příkaz:</target>
+<source>Run a command:</source>
+<target>Spustit příkaz:</target>
<source>OK</source>
<target>OK</target>
@@ -1196,6 +1232,9 @@ Příkaz je spuštěn když:
<source>Directory on server:</source>
<target>Adresář na serveru:</target>
+<source>Access timeout (in seconds):</source>
+<target>Časový limit přístupu (v sekundách):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP kanálů na spojení:</target>
@@ -1274,12 +1313,6 @@ Příkaz je spuštěn když:
<source>Stop synchronization at first error</source>
<target>Ukončit synchronizaci při první chybě</target>
-<source>Save log:</source>
-<target>Uložit žurnál:</target>
-
-<source>Limit number of log files:</source>
-<target>Omezit množství žurnálů:</target>
-
<source>How can I schedule a batch job?</source>
<target>Jak nastavit spouštění dávky?</target>
@@ -1313,8 +1346,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Zobrazit znovu všechny trvale skryté dialogy a varovná hlášení</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Výchozí cesta žurnálu:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Smazat žurnály po (dnech):</target>
<source>Customize context menu:</source>
<target>Přizpůsobit kontextovou nabídku:</target>
@@ -1325,8 +1361,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&Předdefinované</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Komentáře a náměty jsou vždy vítány</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Komentáře a náměty jsou vždy vítány:</target>
<source>Home page</source>
<target>Navštivte</target>
@@ -1352,8 +1388,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>Zdrojový kód byl napsán kompletně v C++ pomocí:</target>
-<source>Published under the GNU General Public License</source>
-<target>Vydáno pod GNU General Public License (GPL)</target>
+<source>Published under the GNU General Public License:</source>
+<target>Vydáno pod GNU General Public License (GPL):</target>
<source>Many thanks for localization:</source>
<target>Poděkování za překlad FreeFileSync:</target>
@@ -1410,7 +1446,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Žádné záznamy</target>
<source>Select all</source>
<target>Vybrat vše</target>
@@ -1436,6 +1472,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>Přehled</target>
+<source>Swap sides</source>
+<target>Změna stran</target>
+
<source>Show "%x"</source>
<target>Zobrazit "%x"</target>
@@ -1614,9 +1653,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>Zobrazit filtrované nebo dočasně vynechané soubory</target>
-<source>Save as default</source>
-<target>Uložit jako výchozí</target>
-
<source>Filter</source>
<target>Filtr</target>
@@ -1967,12 +2003,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>Selhala kontrola Koše pro složku %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Nelze načíst následující XML elementy:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Konfigurační soubor %x je nekompletní. Chybějící položky budou nahrazeny výchozími hodnotami.</target>
-
<source>Prepare installation</source>
<target>Příprava instalace</target>
diff --git a/FreeFileSync/Build/Languages/danish.lng b/FreeFileSync/Build/Languages/danish.lng
index fe6ed455..c3ed5775 100755
--- a/FreeFileSync/Build/Languages/danish.lng
+++ b/FreeFileSync/Build/Languages/danish.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Enhederne har kun attributter til forskel</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Navnet %x bruges af flere emner i mappen.</target>
+
<source>Resolving symbolic link %x</source>
<target>Løser symbolsk link %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Tidstolerance</target>
-<source>Folder access timeout</source>
-<target>Tidsgrænse mappeadgang</target>
-
<source>Run with background priority</source>
<target>Kør med baggrundsprioritet</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Opdater attributter mod højre</target>
-<source>Warning</source>
-<target>Advarsel</target>
+<source>Errors:</source>
+<target>Fejl:</target>
+
+<source>Warnings:</source>
+<target>Advarsler:</target>
<source>Items processed:</source>
<target>Emner behandlet:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Samlet tid:</target>
+<source>Warning</source>
+<target>Advarsel</target>
+
<source>Stopped</source>
<target>Afbrudt</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Behandlingsfejl i filen %x, række %y, kolonne %z.</target>
+<source>Services</source>
+<target>Tjenester</target>
+
+<source>Show All</source>
+<target>Vis alle</target>
+
+<source>Hide Others</source>
+<target>Skjul andre</target>
+
+<source>Hide %x</source>
+<target>Skjul %x</target>
+
+<source>Quit %x</source>
+<target>Luk %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Kan ikke sætte mappelåse for følgende mapper:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Kan ikke læse mappen %x.</target>
-<source>/sec</source>
-<target>/sek</target>
+<source>%x/sec</source>
+<target>%x/sek</target>
-<source>%x items/sec</source>
-<target>%x emner/sek</target>
+<source>%x items</source>
+<target>%x emner</target>
<source>Show in Explorer</source>
<target>Åben filplacering</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Opretter database...</target>
-<source>Searching for excess file versions:</source>
-<target>Finder overskredne filversioner:</target>
+<source>Searching for old file versions:</source>
+<target>Søger efter gamle filversioner:</target>
-<source>Removing excess file versions:</source>
-<target>Fjerner overskredne filversioner:</target>
+<source>Removing old file versions:</source>
+<target>Fjerner gamle filversioner:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Kan ikke oprette tidsstempel til versionering:</target>
@@ -585,6 +606,24 @@ Aktuel: %y byte
<source>Unable to move %x to the recycle bin.</source>
<target>Kunne ikke flytte %x til papirkurv.</target>
+<source>Unable to access %x.</source>
+<target>Kan ikke tilgå %x.</target>
+
+<source>Authentication completed.</source>
+<target>Godkendelse gennemført.</target>
+
+<source>Authentication failed.</source>
+<target>Godkendelse fejlede.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Du kan lukke siden og returnere til FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Serverfejl:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Kan ikke definere ledig plads på %x.</target>
+
<source>Cannot find %x.</source>
<target>Kan ikke finde %x.</target>
@@ -597,18 +636,12 @@ Aktuel: %y byte
<source>Cannot delete symbolic link %x.</source>
<target>Kan ikke slette det symbolske link %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Kan ikke definere ledig plads på %x.</target>
-
<source>Incorrect command line:</source>
<target>Ugyldig kommando:</target>
<source>The server does not support authentication via %x.</source>
<target>Serveren støtter ikke godkendelse via %x.</target>
-<source>Unable to access %x.</source>
-<target>Kan ikke tilgå %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Aktuel: %y byte
<source>Failed to open SFTP channel number %x.</source>
<target>Fejl ved åbning af SFTP kanal nr. %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x byte</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Træk emner hertil</target>
@@ -764,6 +779,24 @@ Kommandoen udføres hvis:
<source>&Retry</source>
<target>&Prøv igen</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x byte</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Indlæser...</target>
@@ -909,7 +942,7 @@ Kommandoen udføres hvis:
<target>Gem som &batchfil...</target>
<source>Show &log</source>
-<target></target>
+<target>Vis &log</target>
<source>Start &comparison</source>
<target>Start &analyse</target>
@@ -971,9 +1004,6 @@ Kommandoen udføres hvis:
<source>Access online storage</source>
<target>Åben onlinelager</target>
-<source>Swap sides</source>
-<target>Byt side</target>
-
<source>Close search bar</source>
<target>Luk søgelinie</target>
@@ -998,6 +1028,9 @@ Kommandoen udføres hvis:
<source>View type:</source>
<target>Visning:</target>
+<source>Save as default</source>
+<target>Gem som standard</target>
+
<source>Select view:</source>
<target>Vælg visning:</target>
@@ -1055,6 +1088,15 @@ Kommandoen udføres hvis:
<source>Handle daylight saving time</source>
<target>Tag hensyn til sommertid</target>
+<source>Ignore errors</source>
+<target>Ignorér fejl</target>
+
+<source>Retry count:</source>
+<target>Antal forsøg:</target>
+
+<source>Delay (in seconds):</source>
+<target>Forsinkelse (sek):</target>
+
<source>Performance improvements:</source>
<target>Ydelsesforbedring:</target>
@@ -1129,17 +1171,11 @@ Kommandoen udføres hvis:
<source>Last x days:</source>
<target>Sidste x dage:</target>
-<source>Ignore errors</source>
-<target>Ignorér fejl</target>
-
-<source>Retry count:</source>
-<target>Antal forsøg:</target>
-
-<source>Delay (in seconds):</source>
-<target>Forsinkelse (sek):</target>
+<source>&Override default log path:</source>
+<target>&Tilpas logplacering:</target>
-<source>Run a command after synchronization:</source>
-<target>Kør kommando efter synkronisering:</target>
+<source>Run a command:</source>
+<target>Kør kommando:</target>
<source>OK</source>
<target>OK</target>
@@ -1189,6 +1225,9 @@ Kommandoen udføres hvis:
<source>Directory on server:</source>
<target>Servermappe:</target>
+<source>Access timeout (in seconds):</source>
+<target>Timeout (sekunder):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP kanaler pr. forbindelse:</target>
@@ -1267,12 +1306,6 @@ Kommandoen udføres hvis:
<source>Stop synchronization at first error</source>
<target>Stop synkronisering ved første fejl</target>
-<source>Save log:</source>
-<target>Gem log:</target>
-
-<source>Limit number of log files:</source>
-<target>Begræns antal logfiler:</target>
-
<source>How can I schedule a batch job?</source>
<target>Hvordan oprettes en batchfil?</target>
@@ -1309,8 +1342,11 @@ Sikrer processen ved alvorlige fejl.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Vis skjulte advarsler og beskeder igen</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Logplacering:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Slet logs efter x dage:</target>
<source>Customize context menu:</source>
<target>Tilpas kontekstmenu:</target>
@@ -1321,8 +1357,8 @@ Sikrer processen ved alvorlige fejl.
<source>&Default</source>
<target>S&tandard</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Kritik og forslag er meget velkomne</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Kritik og foreslag er meget velkomne:</target>
<source>Home page</source>
<target>Hjemmeside</target>
@@ -1348,8 +1384,8 @@ Sikrer processen ved alvorlige fejl.
<source>Source code written in C++ using:</source>
<target>Kildekoden er skrevet i C++ med hjælp fra:</target>
-<source>Published under the GNU General Public License</source>
-<target>Udgivet under GNU General Public Licence</target>
+<source>Published under the GNU General Public License:</source>
+<target>Udgivet under GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Tak for oversættelse til:</target>
@@ -1406,7 +1442,7 @@ Sikrer processen ved alvorlige fejl.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Ingen logemner</target>
<source>Select all</source>
<target>Vælg alt</target>
@@ -1432,6 +1468,9 @@ Sikrer processen ved alvorlige fejl.
<source>Overview</source>
<target>Oversigt</target>
+<source>Swap sides</source>
+<target>Byt side</target>
+
<source>Show "%x"</source>
<target>Vis "%x"</target>
@@ -1606,9 +1645,6 @@ Sikrer processen ved alvorlige fejl.
<source>Show filtered or temporarily excluded files</source>
<target>Vis filtrerede eller midlertidigt ekskluderede filer</target>
-<source>Save as default</source>
-<target>Gem som standard</target>
-
<source>Filter</source>
<target>Filter</target>
@@ -1954,12 +1990,6 @@ Sikrer processen ved alvorlige fejl.
<source>Checking recycle bin failed for folder %x.</source>
<target>Check papirkurv fejlede for mappen %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Kunne ikke læse følgende XML emner:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Indstillingsfilen %x er ikke komplet. Manglende elementer sættes til standard.</target>
-
<source>Prepare installation</source>
<target>Forbereder installering</target>
diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/dutch.lng
index b68a5d70..74467d30 100755
--- a/FreeFileSync/Build/Languages/dutch.lng
+++ b/FreeFileSync/Build/Languages/dutch.lng
@@ -101,7 +101,7 @@
<target>globaal configuratiebestand:</target>
<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source>
-<target>Een willekeurig aantal FreeFileSync. ffs_gui en/of "ffs_batch" configuratiebestanden.</target>
+<target>Een willekeurig aantal FreeFileSync "ffs_gui" en/of "ffs_batch" configuratiebestanden.</target>
<source>Any number of alternative directory pairs for at most one config file.</source>
<target>Een willekeurig aantal alternatieve mapparen voor maximaal een configuratiebestand.</target>
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>De items verschillen alleen in de kenmerken</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>De naam %x wordt gebruikt door meer dan één item in de map.</target>
+
<source>Resolving symbolic link %x</source>
<target>Symbolische link %x oplossen</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Tolerantie bestandstijd</target>
-<source>Folder access timeout</source>
-<target>Time-out map toegang</target>
-
<source>Run with background priority</source>
<target>Uitvoeren met achtergrond prioriteit</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Update kenmerken aan de rechterzijde</target>
-<source>Warning</source>
-<target>Waarschuwing</target>
+<source>Errors:</source>
+<target>Fouten:</target>
+
+<source>Warnings:</source>
+<target>Waarschuwingen:</target>
<source>Items processed:</source>
<target>Items verwerkt:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Totale tijd:</target>
+<source>Warning</source>
+<target>Waarschuwing</target>
+
<source>Stopped</source>
<target>Gestopt</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Fout bij het analyseren van bestand %x, rij %y, kolom %z.</target>
+<source>Services</source>
+<target>Diensten</target>
+
+<source>Show All</source>
+<target>Toon alles</target>
+
+<source>Hide Others</source>
+<target>Verberg anderen</target>
+
+<source>Hide %x</source>
+<target>Verberg %x</target>
+
+<source>Quit %x</source>
+<target>Sluit %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Kan map vergrendelingen niet instellen voor de volgende mappen:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Kan de map %x niet lezen.</target>
-<source>/sec</source>
-<target>/sec</target>
+<source>%x/sec</source>
+<target>%x/sec</target>
-<source>%x items/sec</source>
-<target>%x items/sec</target>
+<source>%x items</source>
+<target>%x items</target>
<source>Show in Explorer</source>
<target>In Explorer weergeven</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Genereren database...</target>
-<source>Searching for excess file versions:</source>
-<target>Zoeken naar overtollige bestandsversies:</target>
+<source>Searching for old file versions:</source>
+<target>Naar oude bestandsversies zoeken:</target>
-<source>Removing excess file versions:</source>
-<target>Overmatige bestandsversies verwijderen:</target>
+<source>Removing old file versions:</source>
+<target>Oude bestandsversies verwijderen:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Kan geen tijdstempel maken voor versiebeheer:</target>
@@ -585,6 +606,24 @@ Werkelijk: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Niet in staat om %x naar de prullenbak te verplaatsen.</target>
+<source>Unable to access %x.</source>
+<target>Geen toegang tot %x.</target>
+
+<source>Authentication completed.</source>
+<target>Authenticatie voltooid.</target>
+
+<source>Authentication failed.</source>
+<target>Verificatie mislukt.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>U kunt deze pagina nu sluiten en doorgaan met FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>De server heeft een fout teruggestuurd:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Kan de vrije schijfruimte voor %x niet bepalen.</target>
+
<source>Cannot find %x.</source>
<target>Kan %x niet vinden.</target>
@@ -597,18 +636,12 @@ Werkelijk: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Kan de symbolische koppeling %x niet verwijderen.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Kan de vrije schijfruimte voor %x niet bepalen.</target>
-
<source>Incorrect command line:</source>
<target>Onjuiste opdrachtregel:</target>
<source>The server does not support authentication via %x.</source>
<target>De server ondersteunt geen verificatie via %x.</target>
-<source>Unable to access %x.</source>
-<target>Geen toegang tot %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Werkelijk: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Fout bij het openen van SFTP kanaalnummer %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Slepen && neerzetten</target>
@@ -764,6 +779,24 @@ De opdracht wordt geactiveerd als:
<source>&Retry</source>
<target>&Probeer opnieuw</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Laden...</target>
@@ -867,7 +900,7 @@ De opdracht wordt geactiveerd als:
<target>Lokale vergelijking instellingen</target>
<source>Local synchronization settings</source>
-<target>Lokale synchronisatie-instellingen</target>
+<target>Lokale synchronisatie instellingen</target>
<source>Local filter</source>
<target>Lokaal filter</target>
@@ -909,7 +942,7 @@ De opdracht wordt geactiveerd als:
<target>Opslaan als &batchverwerking...</target>
<source>Show &log</source>
-<target></target>
+<target>Bekijk &logbestand</target>
<source>Start &comparison</source>
<target>Start &vergelijking</target>
@@ -971,9 +1004,6 @@ De opdracht wordt geactiveerd als:
<source>Access online storage</source>
<target>Toegang tot online opslag</target>
-<source>Swap sides</source>
-<target>Verwisselen van zijden</target>
-
<source>Close search bar</source>
<target>Sluit de zoekbalk</target>
@@ -998,6 +1028,9 @@ De opdracht wordt geactiveerd als:
<source>View type:</source>
<target>Type bekijken:</target>
+<source>Save as default</source>
+<target>Opslaan als standaard</target>
+
<source>Select view:</source>
<target>Selecteer weergave:</target>
@@ -1055,6 +1088,15 @@ De opdracht wordt geactiveerd als:
<source>Handle daylight saving time</source>
<target>Zomertijd gebruiken</target>
+<source>Ignore errors</source>
+<target>Negeer fouten</target>
+
+<source>Retry count:</source>
+<target>Probeer opnieuw te tellen:</target>
+
+<source>Delay (in seconds):</source>
+<target>Vertraging (in seconden):</target>
+
<source>Performance improvements:</source>
<target>Prestatieverbeteringen:</target>
@@ -1129,17 +1171,11 @@ De opdracht wordt geactiveerd als:
<source>Last x days:</source>
<target>Laatste x dagen:</target>
-<source>Ignore errors</source>
-<target>Negeer fouten</target>
-
-<source>Retry count:</source>
-<target>Probeer opnieuw te tellen:</target>
-
-<source>Delay (in seconds):</source>
-<target>Vertraging (in seconden):</target>
+<source>&Override default log path:</source>
+<target>&Overschrijf standaard logboekpad:</target>
-<source>Run a command after synchronization:</source>
-<target>Voer een opdracht uit na de synchronisatie:</target>
+<source>Run a command:</source>
+<target>Voer een opdracht uit:</target>
<source>OK</source>
<target>OK</target>
@@ -1181,7 +1217,7 @@ De opdracht wordt geactiveerd als:
<target>Gebruikersnaam:</target>
<source>Private key file:</source>
-<target>Prive-sleutel bestand:</target>
+<target>Prive sleutelbestand:</target>
<source>&Show password</source>
<target>&Toon wachtwoord</target>
@@ -1189,6 +1225,9 @@ De opdracht wordt geactiveerd als:
<source>Directory on server:</source>
<target>Map op de server:</target>
+<source>Access timeout (in seconds):</source>
+<target>Toegang timeout (in seconden):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP kanalen per verbinding:</target>
@@ -1267,12 +1306,6 @@ De opdracht wordt geactiveerd als:
<source>Stop synchronization at first error</source>
<target>Synchronisatie bij eerste fout stoppen</target>
-<source>Save log:</source>
-<target>Logbestand opslaan:</target>
-
-<source>Limit number of log files:</source>
-<target>Beperk het aantal logbestanden:</target>
-
<source>How can I schedule a batch job?</source>
<target>Hoe kan ik een batchverwerking plannen?</target>
@@ -1309,8 +1342,11 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Alle permanent verborgen dialogen en waarschuwingsberichten opnieuw weergeven</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Standaard logboek pad:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Verwijder logboeken na x dagen:</target>
<source>Customize context menu:</source>
<target>Contextmenu aanpassen:</target>
@@ -1321,8 +1357,8 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>&Default</source>
<target>&Standaard</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Terugkoppeling en suggesties zijn welkom</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Terugkoppeling en suggesties zijn welkom:</target>
<source>Home page</source>
<target>Startpagina</target>
@@ -1348,8 +1384,8 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Source code written in C++ using:</source>
<target>Broncode werd in C++ geschreven met:</target>
-<source>Published under the GNU General Public License</source>
-<target>Gepubliceerd onder de GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Gepubliceerd onder de GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Hartelijk dank voor de lokalisatie:</target>
@@ -1379,7 +1415,7 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<target>Markeer configuraties die niet meer dan het volgende aantal dagen zijn uitgevoerd:</target>
<source>Synchronization Settings</source>
-<target>Synchronisatie-instellingen</target>
+<target>Synchronisatie instellingen</target>
<source>Access Online Storage</source>
<target>Toegang Online Opslag</target>
@@ -1406,7 +1442,7 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Geen vermeldingen in het logboek</target>
<source>Select all</source>
<target>Alles selecteren</target>
@@ -1432,6 +1468,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Overview</source>
<target>Overzicht</target>
+<source>Swap sides</source>
+<target>Verwisselen van zijden</target>
+
<source>Show "%x"</source>
<target>Toon "%x"</target>
@@ -1606,9 +1645,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Show filtered or temporarily excluded files</source>
<target>Toon gefilterde of tijdelijk uitgesloten bestanden</target>
-<source>Save as default</source>
-<target>Opslaan als standaard</target>
-
<source>Filter</source>
<target>Filter</target>
@@ -1954,12 +1990,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout.
<source>Checking recycle bin failed for folder %x.</source>
<target>Controle van de prullenbak voor map %x is mislukt.</target>
-<source>The following XML elements could not be read:</source>
-<target>De volgende XML-elementen kunnen niet worden gelezen:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Configuratiebestand %x is onvolledig. De ontbrekende elementen worden ingesteld op hun standaardwaarden.</target>
-
<source>Prepare installation</source>
<target>Installatie voorbereiden</target>
diff --git a/FreeFileSync/Build/Languages/english_uk.lng b/FreeFileSync/Build/Languages/english_uk.lng
index 23785c8e..9b2c4f75 100755
--- a/FreeFileSync/Build/Languages/english_uk.lng
+++ b/FreeFileSync/Build/Languages/english_uk.lng
@@ -7,15 +7,6 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>No log entries</source>
-<target></target>
-
-<source>Remove old log files after x days:</source>
-<target></target>
-
-<source>Show &log</source>
-<target></target>
-
<source>Both sides have changed since last synchronization.</source>
<target>Both sides have changed since last synchronisation.</target>
@@ -169,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Items differ in attributes only</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>The name %x is used by more than one item in the folder.</target>
+
<source>Resolving symbolic link %x</source>
<target>Resolving symbolic link %x</target>
@@ -196,9 +190,6 @@
<source>File time tolerance</source>
<target>File time tolerance</target>
-<source>Folder access timeout</source>
-<target>Folder access timeout</target>
-
<source>Run with background priority</source>
<target>Run with background priority</target>
@@ -343,8 +334,11 @@
<source>Update attributes on right</source>
<target>Update attributes on right</target>
-<source>Warning</source>
-<target>Warning</target>
+<source>Errors:</source>
+<target>Errors:</target>
+
+<source>Warnings:</source>
+<target>Warnings:</target>
<source>Items processed:</source>
<target>Elements processed:</target>
@@ -355,6 +349,9 @@
<source>Total time:</source>
<target>Total time:</target>
+<source>Warning</source>
+<target>Warning</target>
+
<source>Stopped</source>
<target>Stopped</target>
@@ -364,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Error parsing file %x, row %y, column %z.</target>
+<source>Services</source>
+<target>Services</target>
+
+<source>Show All</source>
+<target>Show All</target>
+
+<source>Hide Others</source>
+<target>Hide Others</target>
+
+<source>Hide %x</source>
+<target>Hide %x</target>
+
+<source>Quit %x</source>
+<target>Quit %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Cannot set directory locks for the following folders:</target>
@@ -379,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Cannot read directory %x.</target>
-<source>/sec</source>
-<target>/sec</target>
+<source>%x/sec</source>
+<target>%x/sec</target>
-<source>%x items/sec</source>
-<target>%x items/sec</target>
+<source>%x items</source>
+<target>%x items</target>
<source>Show in Explorer</source>
<target>Show in Explorer</target>
@@ -532,11 +544,11 @@
<source>Generating database...</source>
<target>Generating database...</target>
-<source>Searching for excess file versions:</source>
-<target>Searching for excess file versions:</target>
+<source>Searching for old file versions:</source>
+<target>Searching for old file versions:</target>
-<source>Removing excess file versions:</source>
-<target>Removing excess file versions:</target>
+<source>Removing old file versions:</source>
+<target>Removing old file versions:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Unable to create time stamp for versioning:</target>
@@ -594,6 +606,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Unable to move %x to the recycle bin.</target>
+<source>Unable to access %x.</source>
+<target>Unable to access %x.</target>
+
+<source>Authentication completed.</source>
+<target>Authentication completed.</target>
+
+<source>Authentication failed.</source>
+<target>Authentication failed.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>You may close this page now and continue with FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>The server returned an error:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Cannot determine free disk space for %x.</target>
+
<source>Cannot find %x.</source>
<target>Cannot find %x.</target>
@@ -606,18 +636,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Cannot delete symbolic link %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Cannot determine free disk space for %x.</target>
-
<source>Incorrect command line:</source>
<target>Incorrect command line:</target>
<source>The server does not support authentication via %x.</source>
<target>The server does not support authentication via %x.</target>
-<source>Unable to access %x.</source>
-<target>Unable to access %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -642,24 +666,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Failed to open SFTP channel number %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Drag && drop</target>
@@ -773,6 +779,24 @@ The command is triggered if:
<source>&Retry</source>
<target>&Retry</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Loading...</target>
@@ -917,6 +941,9 @@ The command is triggered if:
<source>Save as &batch job...</source>
<target>Save as &batch job...</target>
+<source>Show &log</source>
+<target>Show &log</target>
+
<source>Start &comparison</source>
<target>Start &comparison</target>
@@ -977,9 +1004,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>Access online storage</target>
-<source>Swap sides</source>
-<target>Swap sides</target>
-
<source>Close search bar</source>
<target>Close search bar</target>
@@ -1004,6 +1028,9 @@ The command is triggered if:
<source>View type:</source>
<target>View type:</target>
+<source>Save as default</source>
+<target>Save as default</target>
+
<source>Select view:</source>
<target>Select view:</target>
@@ -1061,6 +1088,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Handle daylight saving time</target>
+<source>Ignore errors</source>
+<target>Ignore errors</target>
+
+<source>Retry count:</source>
+<target>Retry count:</target>
+
+<source>Delay (in seconds):</source>
+<target>Delay (in seconds):</target>
+
<source>Performance improvements:</source>
<target>Performance improvements:</target>
@@ -1135,17 +1171,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>Last x days:</target>
-<source>Ignore errors</source>
-<target>Ignore errors</target>
-
-<source>Retry count:</source>
-<target>Retry count:</target>
+<source>&Override default log path:</source>
+<target>&Override default log path:</target>
-<source>Delay (in seconds):</source>
-<target>Delay (in seconds):</target>
-
-<source>Run a command after synchronization:</source>
-<target>Run a command after synchronization:</target>
+<source>Run a command:</source>
+<target>Run a command:</target>
<source>OK</source>
<target>OK</target>
@@ -1195,6 +1225,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Directory on server:</target>
+<source>Access timeout (in seconds):</source>
+<target>Access timeout (in seconds):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP channels per connection:</target>
@@ -1273,12 +1306,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>Stop synchronisation at first error</target>
-<source>Save log:</source>
-<target>Save log:</target>
-
-<source>Limit number of log files:</source>
-<target>Limit number of log files:</target>
-
<source>How can I schedule a batch job?</source>
<target>How can I schedule a batch job?</target>
@@ -1315,6 +1342,12 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Show all permanently hidden dialogues and warning messages again</target>
+<source>Default log path:</source>
+<target>Default log path:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Delete logs after x days:</target>
+
<source>Customize context menu:</source>
<target>Customise context menu:</target>
@@ -1324,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&Default</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Feedback and suggestions are welcome</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Feedback and suggestions are welcome:</target>
<source>Home page</source>
<target>Home page</target>
@@ -1351,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>Source code written in C++ using:</target>
-<source>Published under the GNU General Public License</source>
-<target>Published under the GNU General Public Licence</target>
+<source>Published under the GNU General Public License:</source>
+<target>Published under the GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Many thanks for localisation:</target>
@@ -1408,6 +1441,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Info</source>
<target>Info</target>
+<source>No log entries</source>
+<target>No log entries</target>
+
<source>Select all</source>
<target>Select all</target>
@@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>Overview</target>
+<source>Swap sides</source>
+<target>Swap sides</target>
+
<source>Show "%x"</source>
<target>Show "%x"</target>
@@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>Show filtered or temporarily excluded files</target>
-<source>Save as default</source>
-<target>Save as default</target>
-
<source>Filter</source>
<target>Filter</target>
@@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>Checking recycle bin failed for folder %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>The following XML elements could not be read:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Configuration file %x is incomplete. The missing elements will be set to their default values.</target>
-
<source>Prepare installation</source>
<target>Prepare installation</target>
diff --git a/FreeFileSync/Build/Languages/french.lng b/FreeFileSync/Build/Languages/french.lng
index fe53e352..ca08d5b3 100755
--- a/FreeFileSync/Build/Languages/french.lng
+++ b/FreeFileSync/Build/Languages/french.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Seuls les attributs des éléments diffèrent</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Le nom %x est utilisé par plusieurs éléments du dossier.</target>
+
<source>Resolving symbolic link %x</source>
<target>Résolution du lien symbolique %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Tolérance horaire</target>
-<source>Folder access timeout</source>
-<target>Dépassement du délai d'accès au dossier</target>
-
<source>Run with background priority</source>
<target>Exécution avec la priorité de tâche de fond.</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Mise à jour des attributs à droite</target>
-<source>Warning</source>
-<target>Attention</target>
+<source>Errors:</source>
+<target>Erreurs :</target>
+
+<source>Warnings:</source>
+<target>Avertissements :</target>
<source>Items processed:</source>
<target>Élements traités :</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Durée totale :</target>
+<source>Warning</source>
+<target>Attention</target>
+
<source>Stopped</source>
<target>Arrêté</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Erreur lors de l'analyse du fichier %x, ligne %y, colonne %z.</target>
+<source>Services</source>
+<target>Services</target>
+
+<source>Show All</source>
+<target>Tout afficher</target>
+
+<source>Hide Others</source>
+<target>Masquer les autres</target>
+
+<source>Hide %x</source>
+<target>Masquer %x</target>
+
+<source>Quit %x</source>
+<target>Quitter %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Impossible de verrouiller les répertoires des dossiers suivants :</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Impossible de lire le répertoire %x.</target>
-<source>/sec</source>
-<target>/sec</target>
+<source>%x/sec</source>
+<target>%x/sec</target>
-<source>%x items/sec</source>
-<target>%x éléments/sec</target>
+<source>%x items</source>
+<target>%x éléments</target>
<source>Show in Explorer</source>
<target>Montrer dans l'explorateur</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Génération de la base de données ...</target>
-<source>Searching for excess file versions:</source>
-<target>Recherche des versions de fichiers excédentaires :</target>
+<source>Searching for old file versions:</source>
+<target>Recherche d'anciennes versions :</target>
-<source>Removing excess file versions:</source>
-<target>Suppression des versions de fichiers excédentaires :</target>
+<source>Removing old file versions:</source>
+<target>Suppression d'anciennes versions :</target>
<source>Unable to create time stamp for versioning:</source>
<target>Impossible de générer l'horodatage pour la gestion des versions :</target>
@@ -585,6 +606,24 @@ Trouvé : %y octets
<source>Unable to move %x to the recycle bin.</source>
<target>Impossible de déplacer %x dans la Corbeille.</target>
+<source>Unable to access %x.</source>
+<target>Impossible d'accéder à %x.</target>
+
+<source>Authentication completed.</source>
+<target>Identification terminée.</target>
+
+<source>Authentication failed.</source>
+<target>Echec de l'identification.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Vous pouvez fermer cette page maintenant et poursuivre avec FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Le serveur a renvoyé une erreur :</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Impossible de calculer l'espace libre du disque %x.</target>
+
<source>Cannot find %x.</source>
<target>Impossible de trouver %x.</target>
@@ -597,18 +636,12 @@ Trouvé : %y octets
<source>Cannot delete symbolic link %x.</source>
<target>Impossible de supprimer le lien symbolique %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Impossible de calculer l'espace libre du disque %x.</target>
-
<source>Incorrect command line:</source>
<target>Ligne de commande incorrecte :</target>
<source>The server does not support authentication via %x.</source>
<target>Le serveur refuse l'authentification par %x.</target>
-<source>Unable to access %x.</source>
-<target>Impossible d'accéder à %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Trouvé : %y octets
<source>Failed to open SFTP channel number %x.</source>
<target>Impossible d'ouvrir le port SFTP %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x octet</pluralform>
-<pluralform>%x octets</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x Mo</target>
-
-<source>%x KB</source>
-<target>%x Ko</target>
-
-<source>%x GB</source>
-<target>%x Go</target>
-
<source>Drag && drop</source>
<target>Glisser && Déposer</target>
@@ -764,6 +779,24 @@ La commande est déclenchée si :
<source>&Retry</source>
<target>&Réessayer</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x octet</pluralform>
+<pluralform>%x octets</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x Mo</target>
+
+<source>%x KB</source>
+<target>%x Ko</target>
+
+<source>%x GB</source>
+<target>%x Go</target>
+
<source>Loading...</source>
<target>Chargement ...</target>
@@ -909,7 +942,7 @@ La commande est déclenchée si :
<target>Enregistrer en tant que fichier de &commandes ...</target>
<source>Show &log</source>
-<target></target>
+<target>Afficher &journal</target>
<source>Start &comparison</source>
<target>Démarrer la &comparaison</target>
@@ -971,9 +1004,6 @@ La commande est déclenchée si :
<source>Access online storage</source>
<target>Accès au stockage en ligne</target>
-<source>Swap sides</source>
-<target>Permuter les côtés</target>
-
<source>Close search bar</source>
<target>Fermer la barre de recherche</target>
@@ -998,6 +1028,9 @@ La commande est déclenchée si :
<source>View type:</source>
<target>Type de vue :</target>
+<source>Save as default</source>
+<target>Sauvegarde par défaut</target>
+
<source>Select view:</source>
<target>Choisir la vue :</target>
@@ -1055,6 +1088,15 @@ La commande est déclenchée si :
<source>Handle daylight saving time</source>
<target>Gérer l'heure d'été</target>
+<source>Ignore errors</source>
+<target>Ignorer les erreurs</target>
+
+<source>Retry count:</source>
+<target>Nombre de tentatives :</target>
+
+<source>Delay (in seconds):</source>
+<target>Délai (en secondes) :</target>
+
<source>Performance improvements:</source>
<target>Amélioration des performances :</target>
@@ -1129,17 +1171,11 @@ La commande est déclenchée si :
<source>Last x days:</source>
<target>Derniers x jours :</target>
-<source>Ignore errors</source>
-<target>Ignorer les erreurs</target>
-
-<source>Retry count:</source>
-<target>Nombre de tentatives :</target>
-
-<source>Delay (in seconds):</source>
-<target>Délai (en secondes) :</target>
+<source>&Override default log path:</source>
+<target>&Remplacer le chemin par défaut du journal :</target>
-<source>Run a command after synchronization:</source>
-<target>Exécuter une commande après la synchronisation :</target>
+<source>Run a command:</source>
+<target>Éxecuter une commande :</target>
<source>OK</source>
<target>OK</target>
@@ -1189,6 +1225,9 @@ La commande est déclenchée si :
<source>Directory on server:</source>
<target>Répertoire sur le serveur :</target>
+<source>Access timeout (in seconds):</source>
+<target>Délai d'accès (en secondes) :</target>
+
<source>SFTP channels per connection:</source>
<target>Ports SFTP par connexion :</target>
@@ -1267,12 +1306,6 @@ La commande est déclenchée si :
<source>Stop synchronization at first error</source>
<target>Arrêter la synchronisation à la première erreur</target>
-<source>Save log:</source>
-<target>Sauvegarde du fichier log :</target>
-
-<source>Limit number of log files:</source>
-<target>Limiter le nombre de journaux :</target>
-
<source>How can I schedule a batch job?</source>
<target>Comment planifier un fichier de commandes ?</target>
@@ -1309,8 +1342,11 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Réafficher en permanence les boîtes de dialogue et les avertissements</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Chemin du journal par défaut :</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Supprimer les journaux après x jours :</target>
<source>Customize context menu:</source>
<target>Personnaliser le menu contextuel :</target>
@@ -1321,8 +1357,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>&Default</source>
<target>&Défaut</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Vos commentaires et vos suggestions sont les bienvenus</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Commentaires et suggestions sont les bienvenus :</target>
<source>Home page</source>
<target>Page d'accueil</target>
@@ -1348,8 +1384,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Source code written in C++ using:</source>
<target>Code source écrit en C++ utilisant :</target>
-<source>Published under the GNU General Public License</source>
-<target>Publié sous licence GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Publié sous licence publique générale GNU :</target>
<source>Many thanks for localization:</source>
<target>Un grand merci pour la traduction à :</target>
@@ -1406,7 +1442,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Aucune entrée journal</target>
<source>Select all</source>
<target>Tout sélectionner</target>
@@ -1432,6 +1468,9 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Overview</source>
<target>Aperçu</target>
+<source>Swap sides</source>
+<target>Permuter les côtés</target>
+
<source>Show "%x"</source>
<target>Afficher "%x"</target>
@@ -1606,9 +1645,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Show filtered or temporarily excluded files</source>
<target>Afficher les fichiers filtrés ou temporairement exclus</target>
-<source>Save as default</source>
-<target>Sauvegarde par défaut</target>
-
<source>Filter</source>
<target>Filtre</target>
@@ -1954,12 +1990,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave.
<source>Checking recycle bin failed for folder %x.</source>
<target>Échec de la vérification de la Corbeille pour le dossier %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Les éléments XML suivants ne peuvent être lus :</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Le fichier de configuration %x est incomplet. Les éléments manquants sont fixés à leur valeur par défaut.</target>
-
<source>Prepare installation</source>
<target>Préparation de l'installation</target>
diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng
index 4b79745a..093be509 100755
--- a/FreeFileSync/Build/Languages/german.lng
+++ b/FreeFileSync/Build/Languages/german.lng
@@ -7,21 +7,6 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>Access timeout (in seconds):</source>
-<target>Zeitlimit für Zugriff(in Sekunden):</target>
-
-<source>The server returned an error:</source>
-<target>Der Server hat einen Fehler zurückgegeben:</target>
-
-<source>You may close this page now and continue with FreeFileSync.</source>
-<target>Sie können diese Seite nun schließen und mit FreeFileSync fortfahren.</target>
-
-<source>Authentication failed.</source>
-<target>Authentifizierung fehlgeschlagen.</target>
-
-<source>Authentication completed.</source>
-<target>Authentifizierung abgeschlossen.</target>
-
<source>Both sides have changed since last synchronization.</source>
<target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target>
@@ -175,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Die Elemente unterscheiden sich nur in Attributen</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Der Name %x wird von mehr als einem Element im Ordner verwendet.</target>
+
<source>Resolving symbolic link %x</source>
<target>Folge der symbolischen Verknüpfung %x</target>
@@ -346,8 +334,11 @@
<source>Update attributes on right</source>
<target>Aktualisiere Attribute des rechten Elements</target>
-<source>Warning</source>
-<target>Warnung</target>
+<source>Errors:</source>
+<target>Fehler:</target>
+
+<source>Warnings:</source>
+<target>Warnungen:</target>
<source>Items processed:</source>
<target>Verarbeitete Elemente:</target>
@@ -358,6 +349,9 @@
<source>Total time:</source>
<target>Gesamtzeit:</target>
+<source>Warning</source>
+<target>Warnung</target>
+
<source>Stopped</source>
<target>Gestoppt</target>
@@ -367,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Fehler beim Auswerten der Datei %x, Zeile %y, Spalte %z.</target>
+<source>Services</source>
+<target>Dienste</target>
+
+<source>Show All</source>
+<target>Alle einblenden</target>
+
+<source>Hide Others</source>
+<target>Andere ausblenden</target>
+
+<source>Hide %x</source>
+<target>%x ausblenden</target>
+
+<source>Quit %x</source>
+<target>%x beenden</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Die Verzeichnissperren können für die folgenden Ordner nicht gesetzt werden:</target>
@@ -382,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Das Verzeichnis %x kann nicht gelesen werden.</target>
-<source>/sec</source>
-<target>/sek</target>
+<source>%x/sec</source>
+<target>%x/sek</target>
-<source>%x items/sec</source>
-<target>%x Elemente/sek</target>
+<source>%x items</source>
+<target>%x Elemente</target>
<source>Show in Explorer</source>
<target>Im Explorer anzeigen</target>
@@ -535,11 +544,11 @@
<source>Generating database...</source>
<target>Erzeuge Synchronisationsdatenbank...</target>
-<source>Searching for excess file versions:</source>
-<target>Suche überzählige Dateiversionen:</target>
+<source>Searching for old file versions:</source>
+<target>Suche alte Dateiversionen:</target>
-<source>Removing excess file versions:</source>
-<target>Entferne überzählige Dateiversionen:</target>
+<source>Removing old file versions:</source>
+<target>Entferne alte Dateiversionen:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Der Zeitstempel für die Versionierung kann nicht erstellt werden:</target>
@@ -600,6 +609,18 @@ Tatsächlich: %y bytes
<source>Unable to access %x.</source>
<target>Auf %x kann nicht zugegriffen werden.</target>
+<source>Authentication completed.</source>
+<target>Authentifizierung abgeschlossen.</target>
+
+<source>Authentication failed.</source>
+<target>Authentifizierung fehlgeschlagen.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Sie können diese Seite nun schließen und mit FreeFileSync fortfahren.</target>
+
+<source>The server returned an error:</source>
+<target>Der Server hat einen Fehler zurückgegeben:</target>
+
<source>Cannot determine free disk space for %x.</source>
<target>Der freie Speicherplatz für %x konnte nicht ermittelt werden.</target>
@@ -983,9 +1004,6 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Access online storage</source>
<target>Auf Onlinespeicher zugreifen</target>
-<source>Swap sides</source>
-<target>Seiten vertauschen</target>
-
<source>Close search bar</source>
<target>Suchleiste schließen</target>
@@ -1010,6 +1028,9 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>View type:</source>
<target>Ansichtstyp:</target>
+<source>Save as default</source>
+<target>Als Standard speichern</target>
+
<source>Select view:</source>
<target>Ansicht wählen:</target>
@@ -1153,8 +1174,8 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>&Override default log path:</source>
<target>&Standardprotokollpfad überschreiben:</target>
-<source>Run a command after synchronization:</source>
-<target>Befehl nach Synchronisation ausführen:</target>
+<source>Run a command:</source>
+<target>Befehl ausführen:</target>
<source>OK</source>
<target>OK</target>
@@ -1204,6 +1225,9 @@ Die Befehlszeile wird ausgelöst, wenn:
<source>Directory on server:</source>
<target>Verzeichnis auf Server:</target>
+<source>Access timeout (in seconds):</source>
+<target>Zeitlimit für Zugriff(in Sekunden):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP Kanäle je Verbindung:</target>
@@ -1321,8 +1345,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Default log path:</source>
<target>Standardprotokollpfad:</target>
-<source>Remove old log files after x days:</source>
-<target>Alte Protokolldateien nach x Tagen entfernen:</target>
+<source>&Delete logs after x days:</source>
+<target>&Protokolldateien nach x Tagen löschen:</target>
<source>Customize context menu:</source>
<target>Kontextmenü anpassen:</target>
@@ -1333,8 +1357,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>&Default</source>
<target>&Standard</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Feedback und Vorschläge sind willkommen</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Hinweise und Vorschläge sind willkommen:</target>
<source>Home page</source>
<target>Homepage</target>
@@ -1360,8 +1384,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Source code written in C++ using:</source>
<target>Der Quellcode wurde in C++ geschrieben mit:</target>
-<source>Published under the GNU General Public License</source>
-<target>Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz</target>
+<source>Published under the GNU General Public License:</source>
+<target>Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz:</target>
<source>Many thanks for localization:</source>
<target>Vielen Dank für die Lokalisation:</target>
@@ -1444,6 +1468,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Overview</source>
<target>Übersicht</target>
+<source>Swap sides</source>
+<target>Seiten vertauschen</target>
+
<source>Show "%x"</source>
<target>Zeige "%x"</target>
@@ -1618,9 +1645,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert.
<source>Show filtered or temporarily excluded files</source>
<target>Gefilterte oder temporär ausgeschlossene Dateien zeigen</target>
-<source>Save as default</source>
-<target>Als Standard speichern</target>
-
<source>Filter</source>
<target>Filter</target>
diff --git a/FreeFileSync/Build/Languages/greek.lng b/FreeFileSync/Build/Languages/greek.lng
index c09db39b..ab0c8dfb 100755
--- a/FreeFileSync/Build/Languages/greek.lng
+++ b/FreeFileSync/Build/Languages/greek.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Τα στοιχεία διαφέρουν μόνο ως προς τα χαρακτηριστικά τους</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Το όνομα %x χρησιμοποιείται για περισσότερα από ένα αρχεία στον φάκελο.</target>
+
<source>Resolving symbolic link %x</source>
<target>Επίλυση του συμβολικού συνδέσμου %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Ανοχή στην ημερομηνία αρχείου</target>
-<source>Folder access timeout</source>
-<target>Χρονικό όριο πρόσβασης στο φάκελο</target>
-
<source>Run with background priority</source>
<target>Εκτέλεση με προτεραιότητα παρασκηνίου</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Ενημέρωση των χαρακτηριστικών στα δεξιά</target>
-<source>Warning</source>
-<target>Προειδοποίηση</target>
+<source>Errors:</source>
+<target>Σφάλματα:</target>
+
+<source>Warnings:</source>
+<target>Προειδοποιήσεις:</target>
<source>Items processed:</source>
<target>Επεξεργάστηκαν στοιχεία:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Συνολική διάρκεια:</target>
+<source>Warning</source>
+<target>Προειδοποίηση</target>
+
<source>Stopped</source>
<target>Διακοπή</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Σφάλμα κατά την ανάλυση του αρχείου %x, γραμμή %y, στήλη %z.</target>
+<source>Services</source>
+<target>Υπηρεσίες</target>
+
+<source>Show All</source>
+<target>Εμφάνιση Όλων</target>
+
+<source>Hide Others</source>
+<target>Απόκρυψη Άλλων</target>
+
+<source>Hide %x</source>
+<target>Απόκρυψη %x</target>
+
+<source>Quit %x</source>
+<target>Έξοδος από %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Δεν μπορούν να κλειδωθούν οι ακόλουθοι υποκατάλογοι:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Δεν μπορεί να γίνει ανάγνωση του καταλόγου %x.</target>
-<source>/sec</source>
-<target>/δευτερόλεπτο</target>
+<source>%x/sec</source>
+<target>%x/δευτερόλεπτο</target>
-<source>%x items/sec</source>
-<target>%x στοιχεία/δευτερόλεπτο</target>
+<source>%x items</source>
+<target>%x στοιχεία</target>
<source>Show in Explorer</source>
<target>Εμφάνιση στην Εξερεύνηση</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Δημιουργία βάσης δεδομένων...</target>
-<source>Searching for excess file versions:</source>
-<target>Αναζήτηση υπεράριθμων παλιών εκδόσεων:</target>
+<source>Searching for old file versions:</source>
+<target>Αναζήτηση παλιών εκδόσεων των αρχείων:</target>
-<source>Removing excess file versions:</source>
-<target>Αφαίρεση υπεράριθμων παλιών εκδόσεων:</target>
+<source>Removing old file versions:</source>
+<target>Διαγραφή παλιών εκδόσεων των αρχείων:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Δεν ήταν δυνατή η δημιουργία χρονικής σήμανσης για τη διατήρηση παλιών εκδόσεων:</target>
@@ -585,6 +606,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Δεν ήταν δυνατή η μεταφορά του %x στον κάδο ανακύκλωσης.</target>
+<source>Unable to access %x.</source>
+<target>Δεν είναι δυνατή η πρόσβαση στο %x.</target>
+
+<source>Authentication completed.</source>
+<target>Ολοκληρώθηκε η επαλήθευση ταυτότητας.</target>
+
+<source>Authentication failed.</source>
+<target>Η επαλήθευση ταυτότητας απέτυχε.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Τώρα μπορείτε να κλείσετε αυτή τη σελίδα και να συνεχίσετε το FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Ο διακομιστής επέστρεψε το λάθος:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Δεν μπορεί να υπολογιστεί ο ελεύθερος χώρος του δίσκου %x.</target>
+
<source>Cannot find %x.</source>
<target>Το %x δεν μπορεί να βρεθεί.</target>
@@ -597,17 +636,11 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Δεν μπορεί να διαγραφεί ο συμβολικός σύνδεσμος %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Δεν μπορεί να υπολογιστεί ο ελεύθερος χώρος του δίσκου %x.</target>
-
<source>Incorrect command line:</source>
<target>Εσφαλμένη γραμμή εντολών:</target>
<source>The server does not support authentication via %x.</source>
-<target>Ο εξυπηρετητής δεν υποστηρίζει πιστοποίηση μέσω %x.</target>
-
-<source>Unable to access %x.</source>
-<target>Δεν είναι δυνατή η πρόσβαση στο %x.</target>
+<target>Ο εξυπηρετητής δεν υποστηρίζει επαλήθευση ταυτότητας μέσω %x.</target>
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
@@ -633,24 +666,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Αποτυχία ανοίγματος του SFTP καναλιού με αριθμό %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Μεταφορά && Απόθεση</target>
@@ -764,6 +779,24 @@ The command is triggered if:
<source>&Retry</source>
<target>&Επανάληψη</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Φόρτωση...</target>
@@ -909,7 +942,7 @@ The command is triggered if:
<target>Αποθήκευση ως δέσ&μη ενεργειών...</target>
<source>Show &log</source>
-<target></target>
+<target>Εμφάνιση αρχείου &καταγραφής</target>
<source>Start &comparison</source>
<target>Έ&ναρξη σύγκρισης</target>
@@ -971,9 +1004,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>Πρόσβαση σε online χώρο αποθήκευσης</target>
-<source>Swap sides</source>
-<target>Ανταλλαγή πλευρών</target>
-
<source>Close search bar</source>
<target>Κλείσιμο γραμμής αναζήτησης</target>
@@ -998,6 +1028,9 @@ The command is triggered if:
<source>View type:</source>
<target>Τύπος εμφάνισης:</target>
+<source>Save as default</source>
+<target>Αποθήκευση ως προεπιλογής</target>
+
<source>Select view:</source>
<target>Επιλογή εμφάνισης:</target>
@@ -1055,6 +1088,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Διαχείριση θερινής ώρας</target>
+<source>Ignore errors</source>
+<target>Αγνόηση σφαλμάτων</target>
+
+<source>Retry count:</source>
+<target>Αριθμός προσπαθειών:</target>
+
+<source>Delay (in seconds):</source>
+<target>Καθυστέρηση (σε δευτερόλεπτα):</target>
+
<source>Performance improvements:</source>
<target>Βελτιώσεις της απόδοσης:</target>
@@ -1129,17 +1171,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>Τις τελευταίες x μέρες:</target>
-<source>Ignore errors</source>
-<target>Αγνόηση σφαλμάτων</target>
-
-<source>Retry count:</source>
-<target>Αριθμός προσπαθειών:</target>
-
-<source>Delay (in seconds):</source>
-<target>Καθυστέρηση (σε δευτερόλεπτα):</target>
+<source>&Override default log path:</source>
+<target>&Παράκαμψη προεπιλεγμένης διαδρομής για το αρχείο καταγραφής:</target>
-<source>Run a command after synchronization:</source>
-<target>Εκτέλεση εντολής μετά το συγχρονισμό:</target>
+<source>Run a command:</source>
+<target>Εκτέλεση της εντολής:</target>
<source>OK</source>
<target>OK</target>
@@ -1166,7 +1202,7 @@ The command is triggered if:
<target>&Ρητά SSL/TLS</target>
<source>Authentication:</source>
-<target>Πιστοποίηση:</target>
+<target>Επαλήθευση ταυτότητας:</target>
<source>&Password</source>
<target>&Κωδικός πρόσβασης</target>
@@ -1189,6 +1225,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Υποκατάλογος στον διακομιστή:</target>
+<source>Access timeout (in seconds):</source>
+<target>Χρονικό όριο σύνδεσης (σε δευτερόλεπτα):</target>
+
<source>SFTP channels per connection:</source>
<target>Αριθμός καναλιών SFTP ανά σύνδεση:</target>
@@ -1267,12 +1306,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>Διακοπή του συγχρονισμού με το πρώτο σφάλμα</target>
-<source>Save log:</source>
-<target>Αποθήκευση αρχείου καταγραφής:</target>
-
-<source>Limit number of log files:</source>
-<target>Περιορισμός αριθμού αρχείων καταγραφής:</target>
-
<source>How can I schedule a batch job?</source>
<target>Πώς μπορώ να προγραμματίσω μια εργασία με δέσμη ενεργειών;</target>
@@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Επανεμφάνιση όλων των μόνιμα κρυμμένων ειδοποιήσεων και προειδοποιητικών μηνυμάτων</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Προεπιλεγμένη διαδρομή για το αρχείο καταγραφής:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Διαγραφή των αρχείων καταγραφής μετά από x ημέρες:</target>
<source>Customize context menu:</source>
<target>Προσαρμογή μενού περιβάλλοντος:</target>
@@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&Προεπιλογή</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Τα σχόλια και οι προτάσεις σας είναι ευπρόσδεκτα</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Σχόλια και προτάσεις είναι ευπρόσδεκτα:</target>
<source>Home page</source>
<target>Αρχική σελίδα</target>
@@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>Ο πηγαίος κώδικας γράφτηκε σε C++ χρησιμοποιώντας τα:</target>
-<source>Published under the GNU General Public License</source>
-<target>Διανέμεται υπό την Γενική Άδεια Δημόσιας Χρήσης GNU</target>
+<source>Published under the GNU General Public License:</source>
+<target>Δημοσιευμένο υπό την Γενική Άδεια Δημόσιας Χρήσης GNU:</target>
<source>Many thanks for localization:</source>
<target>Πολλές ευχαριστίες για τις μεταφράσεις:</target>
@@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Πληροφορίες</target>
<source>No log entries</source>
-<target></target>
+<target>Καμία καταγραφή</target>
<source>Select all</source>
<target>Επιλογή όλων</target>
@@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>Σύνοψη</target>
+<source>Swap sides</source>
+<target>Ανταλλαγή πλευρών</target>
+
<source>Show "%x"</source>
<target>Εμφάνιση της γραμμής "%x"</target>
@@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>Εμφάνιση φιλτραρισμένων ή προσωρινά εξαιρεθέντων αρχείων</target>
-<source>Save as default</source>
-<target>Αποθήκευση ως προεπιλογής</target>
-
<source>Filter</source>
<target>Φίλτρο</target>
@@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>Ο έλεγχος του κάδου ανακύκλωσης απέτυχε για τον υποκατάλογο %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Τα ακόλουθα στοιχεία XML δεν ήταν δυνατό να αναγνωσθούν:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Το αρχείο παραμέτρων %x είναι ατελές. Τα στοιχεία που λείπουν θα αντικατασταθούν με τις προεπιλεγμένες τιμές τους.</target>
-
<source>Prepare installation</source>
<target>Προετοιμασία εγκατάστασης</target>
diff --git a/FreeFileSync/Build/Languages/hebrew.lng b/FreeFileSync/Build/Languages/hebrew.lng
index 7885afd1..8327f635 100755
--- a/FreeFileSync/Build/Languages/hebrew.lng
+++ b/FreeFileSync/Build/Languages/hebrew.lng
@@ -7,15 +7,6 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>No log entries</source>
-<target></target>
-
-<source>Remove old log files after x days:</source>
-<target></target>
-
-<source>Show &log</source>
-<target></target>
-
<source>Both sides have changed since last synchronization.</source>
<target>שני הצדדים שונו מאז הסנכרון האחרון.</target>
@@ -169,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>פריטים שונים בתכונות בלבד</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>בשם %x נעשה שימוש יותר מפעם אחת בתיקייה.</target>
+
<source>Resolving symbolic link %x</source>
<target>פותר קישור סימבולי %x</target>
@@ -196,9 +190,6 @@
<source>File time tolerance</source>
<target>אפיצות זמן קובץ</target>
-<source>Folder access timeout</source>
-<target>זמן קצוב לגישה לתיייה</target>
-
<source>Run with background priority</source>
<target>בצע עם עדיפות רקע</target>
@@ -343,8 +334,11 @@
<source>Update attributes on right</source>
<target>עדכן תכונות בצד שמאל</target>
-<source>Warning</source>
-<target>אזהרה</target>
+<source>Errors:</source>
+<target>שגיאות:</target>
+
+<source>Warnings:</source>
+<target>אזהרות:</target>
<source>Items processed:</source>
<target>אלמנטים עובדו:</target>
@@ -355,6 +349,9 @@
<source>Total time:</source>
<target>זמן כולל:</target>
+<source>Warning</source>
+<target>אזהרה</target>
+
<source>Stopped</source>
<target>נעצר</target>
@@ -364,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>שגיאה בפענוח קובץ %x, שורה %y, טור %z.</target>
+<source>Services</source>
+<target>שרותים</target>
+
+<source>Show All</source>
+<target>הראה הכל</target>
+
+<source>Hide Others</source>
+<target>מסתיר אחרים</target>
+
+<source>Hide %x</source>
+<target>מסתיר %x</target>
+
+<source>Quit %x</source>
+<target>יוצא %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>אין אפשרות להגדיר נעילות מחיצה עבור התיקיות הבאות:</target>
@@ -379,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>לא ניתן לקרוא מחיצה %x.</target>
-<source>/sec</source>
-<target>/שנ</target>
+<source>%x/sec</source>
+<target>%x /שניות</target>
-<source>%x items/sec</source>
-<target>%x פריטים לשניה</target>
+<source>%x items</source>
+<target>%x פריטים</target>
<source>Show in Explorer</source>
<target>הראה בסייר הקבצים</target>
@@ -532,11 +544,11 @@
<source>Generating database...</source>
<target>מייצר מסד נתונים...</target>
-<source>Searching for excess file versions:</source>
-<target>מחפש גרסאות קובץ עודפות:</target>
+<source>Searching for old file versions:</source>
+<target>מחפש גרסאות קובץ ישנות:</target>
-<source>Removing excess file versions:</source>
-<target>מסיר גרסאות קובץ עודפות:</target>
+<source>Removing old file versions:</source>
+<target>מסיר גרסאות קובץ ישנות:</target>
<source>Unable to create time stamp for versioning:</source>
<target>לא ניתן ליצור תג זמן לגרסאות:</target>
@@ -594,6 +606,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>לא יכול להעביר את %x לסל המחזור.</target>
+<source>Unable to access %x.</source>
+<target>לא ניתן לגשת אל %x.</target>
+
+<source>Authentication completed.</source>
+<target>האימות הושלם.</target>
+
+<source>Authentication failed.</source>
+<target>האימות נכשל.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>אתה יכול לסגור דף זה עכשיו ולהמשיך עם FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>השרת החזיר שגיאה:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>לא ניתן לקבוע שטח דיסק פנוי עבור %x.</target>
+
<source>Cannot find %x.</source>
<target>לא מוצא %x.</target>
@@ -606,18 +636,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>לא ניתן למחוק קישור סימבולי %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>לא ניתן לקבוע שטח דיסק פנוי עבור %x.</target>
-
<source>Incorrect command line:</source>
<target>שורת פקודה לא תקינה:</target>
<source>The server does not support authentication via %x.</source>
<target>השרת אינו תומך באימות באמצעות %x.</target>
-<source>Unable to access %x.</source>
-<target>לא ניתן לגשת אל %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -642,24 +666,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>נכשלה פתיחת ערוץ SFTP מספר %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>בית 1</pluralform>
-<pluralform>%x בתים</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x מגה בייט</target>
-
-<source>%x KB</source>
-<target>%x קילו בייט</target>
-
-<source>%x GB</source>
-<target>%x גיגה בייט</target>
-
<source>Drag && drop</source>
<target>גרור ושחרר</target>
@@ -773,6 +779,24 @@ The command is triggered if:
<source>&Retry</source>
<target>&נסה שנית</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>בית 1</pluralform>
+<pluralform>%x בתים</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x מגה בייט</target>
+
+<source>%x KB</source>
+<target>%x קילו בייט</target>
+
+<source>%x GB</source>
+<target>%x גיגה בייט</target>
+
<source>Loading...</source>
<target>טוען...</target>
@@ -917,6 +941,9 @@ The command is triggered if:
<source>Save as &batch job...</source>
<target>שמור כעבודת &אצווה...</target>
+<source>Show &log</source>
+<target>הצג &יומנים:</target>
+
<source>Start &comparison</source>
<target>התחל &והשווה</target>
@@ -977,9 +1004,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>גש לאחסון מכוון</target>
-<source>Swap sides</source>
-<target>החלף צדדים</target>
-
<source>Close search bar</source>
<target>סגור סרגל חיפוש</target>
@@ -1004,6 +1028,9 @@ The command is triggered if:
<source>View type:</source>
<target>הצג סוג:</target>
+<source>Save as default</source>
+<target>שמור כברירת מחדל</target>
+
<source>Select view:</source>
<target>בחר תצוגה:</target>
@@ -1061,6 +1088,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>התמודד עם שעון קיץ</target>
+<source>Ignore errors</source>
+<target>התעלם משגיאות</target>
+
+<source>Retry count:</source>
+<target>מונה נסיונות חוזרים:</target>
+
+<source>Delay (in seconds):</source>
+<target>השהייה (בשניות):</target>
+
<source>Performance improvements:</source>
<target>שיפורים בביצועים:</target>
@@ -1135,17 +1171,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>x ימים אחרונים:</target>
-<source>Ignore errors</source>
-<target>התעלם משגיאות</target>
-
-<source>Retry count:</source>
-<target>מונה נסיונות חוזרים:</target>
+<source>&Override default log path:</source>
+<target>&דרוס נתיב ברירת מחדל של יומן:</target>
-<source>Delay (in seconds):</source>
-<target>השהייה (בשניות):</target>
-
-<source>Run a command after synchronization:</source>
-<target>הרץ פקודה לאחר סינכרון:</target>
+<source>Run a command:</source>
+<target>הפעל פקודה:</target>
<source>OK</source>
<target>אשר</target>
@@ -1195,6 +1225,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>מחיצה על שרת:</target>
+<source>Access timeout (in seconds):</source>
+<target>זמן קצוב לתפוגה (בשניות):</target>
+
<source>SFTP channels per connection:</source>
<target>ערוצי SFTP לחיבור:</target>
@@ -1273,12 +1306,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>עצור סנכרון עם הופעת שגיאה ראשונה</target>
-<source>Save log:</source>
-<target>שמור יומן:</target>
-
-<source>Limit number of log files:</source>
-<target>הגבל מספר של קבצי יומן:</target>
-
<source>How can I schedule a batch job?</source>
<target>כיצד לתזמן משימת אצווה?</target>
@@ -1315,6 +1342,12 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>הראה שוב את כל הדיאלוגים והודאות האזהרה המוסתרים באופן קבוע</target>
+<source>Default log path:</source>
+<target>נתיב יומן ברירת מחדל:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&מחק יומנים לאחר x ימים:</target>
+
<source>Customize context menu:</source>
<target>התאמה אישית של תפריט הקשר:</target>
@@ -1324,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&ברירת מחדל</target>
-<source>Feedback and suggestions are welcome</source>
-<target>משוב והצעות יתקבלו בברכה</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>משוב והצעות יתקבלו בברכה:</target>
<source>Home page</source>
<target>דף הבית</target>
@@ -1351,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>קוד מקור נכתב ב- C++‎ באמצעות:</target>
-<source>Published under the GNU General Public License</source>
-<target>מפורסם במסגרת GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>פורסם תחת הרישיון הציבורי הכללי של GNU:</target>
<source>Many thanks for localization:</source>
<target>תודות עבור תרגום שפות:</target>
@@ -1408,6 +1441,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Info</source>
<target>מידע</target>
+<source>No log entries</source>
+<target>אין רשומות יומן</target>
+
<source>Select all</source>
<target>בחר הכל</target>
@@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>מבט כללי</target>
+<source>Swap sides</source>
+<target>החלף צדדים</target>
+
<source>Show "%x"</source>
<target>הראה "%x"</target>
@@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>הראה קבצים שסוננו או לא נכללו זמנית</target>
-<source>Save as default</source>
-<target>שמור כברירת מחדל</target>
-
<source>Filter</source>
<target>מסנן</target>
@@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>בודק כשלון בתא מחזור עבור תיקייה %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>לא ניתן לקרוא את רכיבי XML הבאים:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>קובץ תצורה %x אינו שלם. הרכיבים החסרים יוגדרו עם ערכי ברירת המחדל.</target>
-
<source>Prepare installation</source>
<target>מכין התקנה</target>
diff --git a/FreeFileSync/Build/Languages/hindi.lng b/FreeFileSync/Build/Languages/hindi.lng
index 67a3bfcc..f338466b 100755
--- a/FreeFileSync/Build/Languages/hindi.lng
+++ b/FreeFileSync/Build/Languages/hindi.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>आइटम्स के केवल गुण ही भिन्न हैं।</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>%x नाम का उपयोग फ़ोल्डर में एक से अधिक आइटम द्वारा किया गया है।</target>
+
<source>Resolving symbolic link %x</source>
<target>%x प्रतीकात्मक कड़ी को हल किया जा रहा है</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>फ़ाइल समय टॉलरेंस (छूट)</target>
-<source>Folder access timeout</source>
-<target>निर्देशिका पहुँच काल समापन</target>
-
<source>Run with background priority</source>
<target>पृष्ठभूमि प्राथमिकता के साथ चलाएँ</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>दाईं तरफ के गुण अद्यतन करें</target>
-<source>Warning</source>
-<target>चेतावनी</target>
+<source>Errors:</source>
+<target>त्रुटियाँ:</target>
+
+<source>Warnings:</source>
+<target>चेतावनियाँ:</target>
<source>Items processed:</source>
<target>संसाधित आइटम्स:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>कुल समय:</target>
+<source>Warning</source>
+<target>चेतावनी</target>
+
<source>Stopped</source>
<target>रुका</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>फ़ाइल %x, पंक्ति %y, स्तंभ %z पदच्छेदन में त्रुटि।</target>
+<source>Services</source>
+<target>सेवाएँ</target>
+
+<source>Show All</source>
+<target>सब दिखाओ</target>
+
+<source>Hide Others</source>
+<target>अन्य छुपाएं</target>
+
+<source>Hide %x</source>
+<target>%x छुपाएं</target>
+
+<source>Quit %x</source>
+<target>%x छोड़ें</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>निम्न निर्देशिकाओं के लिए निर्देशिका अवरोध सेट नहीं कर सकते:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>निर्देशिका %x को पढ़ा नहीं जा सकता।</target>
-<source>/sec</source>
-<target>/सेकंड</target>
+<source>%x/sec</source>
+<target>%x/सेकंड</target>
-<source>%x items/sec</source>
-<target>%x आइटम्स/सेकंड</target>
+<source>%x items</source>
+<target>%x आइटम्स</target>
<source>Show in Explorer</source>
<target>एक्सप्लोरर में दिखाएं</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>डेटाबेस बनाया जा रहा है...</target>
-<source>Searching for excess file versions:</source>
-<target>अतिरिक्त फ़ाइल संस्करणों की खोज हो रही है:</target>
+<source>Searching for old file versions:</source>
+<target>पुराने फ़ाइल संस्करणों की खोज हो रही है:</target>
-<source>Removing excess file versions:</source>
-<target>अतिरिक्त फ़ाइल संस्करणों को हटाया जा रहा है:</target>
+<source>Removing old file versions:</source>
+<target>पुराने फ़ाइल संस्करणों को हटाया जा रहा है:</target>
<source>Unable to create time stamp for versioning:</source>
<target>संस्करण के लिए समय मोहर बनाने में असमर्थ:</target>
@@ -585,6 +606,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>%x को रीसायकल बिन ले जाने में असमर्थ।</target>
+<source>Unable to access %x.</source>
+<target>%x तक पहुँचने में असमर्थ।</target>
+
+<source>Authentication completed.</source>
+<target>प्रमाणन पूर्ण।</target>
+
+<source>Authentication failed.</source>
+<target>प्रमाणन विफल।</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>आप अब इस पृष्ठ को बंद कर FreeFileSync जारी रख सकते हैं।</target>
+
+<source>The server returned an error:</source>
+<target>सर्वर ने एक त्रुटि वापस कर दी:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>%x के लिए खाली डिस्क जगह निर्धारित नहीं कर सकते।</target>
+
<source>Cannot find %x.</source>
<target>%x नहीं मिला।</target>
@@ -597,18 +636,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>प्रतीकात्मक कड़ी %x हटाया नहीं जा सकता।</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>%x के लिए खाली डिस्क जगह निर्धारित नहीं कर सकते।</target>
-
<source>Incorrect command line:</source>
<target>अनुचित आदेश-पंक्ति:</target>
<source>The server does not support authentication via %x.</source>
<target>सर्वर पर %x द्वारा प्रमाणन समर्थित नहीं है।</target>
-<source>Unable to access %x.</source>
-<target>%x तक पहुँचने में असमर्थ।</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>SFTP चैनल संख्या %x को खोलने में विफल।</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 बाइट</pluralform>
-<pluralform>%x बाइट्स</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>ड्रॅग एण्ड ड्रॉप</target>
@@ -764,6 +779,24 @@ The command is triggered if:
<source>&Retry</source>
<target>पुनः प्रयास (&R)</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 बाइट</pluralform>
+<pluralform>%x बाइट्स</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>लोड हो रहा है...</target>
@@ -909,7 +942,7 @@ The command is triggered if:
<target>बॅच जॉब के रूप में सहेजें (&b)...</target>
<source>Show &log</source>
-<target></target>
+<target>लॉग दिखाएं (&l)</target>
<source>Start &comparison</source>
<target>तुलना शुरू करें (&c)</target>
@@ -971,9 +1004,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>ऑनलाइन संग्रहण पहुँच प्राप्त करें</target>
-<source>Swap sides</source>
-<target>पक्ष गमागम करें</target>
-
<source>Close search bar</source>
<target>खोज पट्टी बंद करें</target>
@@ -998,6 +1028,9 @@ The command is triggered if:
<source>View type:</source>
<target>दृश्य प्रकार:</target>
+<source>Save as default</source>
+<target>डिफ़ॉल्ट के रूप में सहेजें</target>
+
<source>Select view:</source>
<target>दृश्य चुनें:</target>
@@ -1055,6 +1088,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>दिवालोक बचत समय (डेलाईट सेविंग टाइम) प्रहस्तन करें</target>
+<source>Ignore errors</source>
+<target>त्रुटियों को अनदेखा करें</target>
+
+<source>Retry count:</source>
+<target>पुनः प्रयास गणनांक:</target>
+
+<source>Delay (in seconds):</source>
+<target>विलंब (सेकंड में):</target>
+
<source>Performance improvements:</source>
<target>प्रदर्शन सुधार:</target>
@@ -1129,17 +1171,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>पिछले x दिन:</target>
-<source>Ignore errors</source>
-<target>त्रुटियों को अनदेखा करें</target>
-
-<source>Retry count:</source>
-<target>पुनः प्रयास गणनांक:</target>
-
-<source>Delay (in seconds):</source>
-<target>विलंब (सेकंड में):</target>
+<source>&Override default log path:</source>
+<target>डिफ़ॉल्ट लॉग पथ अधिभूत करें (&O):</target>
-<source>Run a command after synchronization:</source>
-<target>सिंक्रनाइज़ेशन के बाद कोई आदेश चलाएँ:</target>
+<source>Run a command:</source>
+<target>आदेश चलाएँ:</target>
<source>OK</source>
<target>ठीक</target>
@@ -1189,6 +1225,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>सर्वर पर निर्देशिका:</target>
+<source>Access timeout (in seconds):</source>
+<target>पहुँच समय बाह्य (सेकंड्स में):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP चैनल्स प्रति कनेक्शन:</target>
@@ -1267,12 +1306,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>पहली त्रुटि पर सिंक्रनाइज़ेशन रोकें</target>
-<source>Save log:</source>
-<target>लॉग सहेजें:</target>
-
-<source>Limit number of log files:</source>
-<target>लॉग फ़ाइलों की संख्या सीमित करें:</target>
-
<source>How can I schedule a batch job?</source>
<target>मैं कोई बॅच कार्य कैसे अनुसूचित करूँ?</target>
@@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>स्थायी रूप से छिपाये संवाद बॉक्सेस और चेतावनी संदेश फिर से दिखाएं</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>डिफ़ॉल्ट लॉग पथ:</target>
+
+<source>&Delete logs after x days:</source>
+<target>x दिनों के बाद लॉग हटाएं (&D):</target>
<source>Customize context menu:</source>
<target>प्रासंगिक मेनू अनुकूलित करें:</target>
@@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>डिफ़ॉल्ट (&D)</target>
-<source>Feedback and suggestions are welcome</source>
-<target>प्रतिक्रिया और सुझावों का स्वागत है</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>प्रतिक्रिया और सुझाव का स्वागत है:</target>
<source>Home page</source>
<target>मुख पृष्ठ</target>
@@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>स्रोत कोड C++ में लिखा गया इनके प्रयोग से:</target>
-<source>Published under the GNU General Public License</source>
-<target>जीएनयू जनरल पब्लिक लाइसेंस (GNU General Public License) के अंतर्गत प्रकाशित</target>
+<source>Published under the GNU General Public License:</source>
+<target>जीएनयू जनरल पब्लिक लाइसेंस (GNU General Public License) के तहत प्रकाशित:</target>
<source>Many thanks for localization:</source>
<target>स्थानीयकरण के लिए अनेक आभार:</target>
@@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error.
<target>जानकारी</target>
<source>No log entries</source>
-<target></target>
+<target>कोई लॉग प्रविष्टियां नहीं</target>
<source>Select all</source>
<target>सभी चुने</target>
@@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>अवलोकन</target>
+<source>Swap sides</source>
+<target>पक्ष गमागम करें</target>
+
<source>Show "%x"</source>
<target>"%x" दिखाएं</target>
@@ -1553,7 +1592,7 @@ This guarantees a consistent state even in case of a serious error.
<target>नहीं सहेजें (&n)</target>
<source>Hide configuration</source>
-<target>कॉन्फ़िगरेशन छुपाएँ</target>
+<target>कॉन्फ़िगरेशन छुपाएं</target>
<source>Highlight...</source>
<target>हाइलाइट...</target>
@@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>फ़िल्टर किए या अस्थायी रूप से अपवर्जित फ़ाइल्स दिखाएं</target>
-<source>Save as default</source>
-<target>डिफ़ॉल्ट के रूप में सहेजें</target>
-
<source>Filter</source>
<target>फ़िल्टर</target>
@@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>निर्देशिका %x के लिए रीसायकल बिन की जाँच विफल।</target>
-<source>The following XML elements could not be read:</source>
-<target>निम्न XML तत्वों को पढा नहीं जा सका:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>कॉन्फ़िगरेशन फ़ाइल %x अधूरी है। अनुपस्थित तत्व उनके डिफ़ॉल्ट मानों से सेट होंगे।</target>
-
<source>Prepare installation</source>
<target>स्थापना तैयारी</target>
diff --git a/FreeFileSync/Build/Languages/hungarian.lng b/FreeFileSync/Build/Languages/hungarian.lng
index 5135b1d6..c22560bf 100755
--- a/FreeFileSync/Build/Languages/hungarian.lng
+++ b/FreeFileSync/Build/Languages/hungarian.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Az elemek csak attribútumaikban különböznek</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>A %x nevet nem csak egy elem viseli a könyvtárban.</target>
+
<source>Resolving symbolic link %x</source>
<target>%x szimbolikus hivatkozás feloldása</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Állományok időkülönbségének tűréshatára</target>
-<source>Folder access timeout</source>
-<target>Könyvtár hozzáférés időtúllépés</target>
-
<source>Run with background priority</source>
<target>Futtatás háttér prioritással</target>
@@ -299,7 +299,7 @@
<target>Mindkét oldal azonos</target>
<source>Conflict/item cannot be categorized</source>
-<target>Az ütközés vagy elem nem kategorizálható</target>
+<target>Az ütközés/elem nem kategorizálható</target>
<source>Copy new item to left</source>
<target>Új elem másolása a bal oldalra</target>
@@ -320,10 +320,10 @@
<target>Jobb oldali állomány mozgatása</target>
<source>Update left item</source>
-<target>Bal oldali elemek frissítése</target>
+<target>Bal oldali elem frissítése</target>
<source>Update right item</source>
-<target>Jobb oldali elemek frissítése</target>
+<target>Jobb oldali elem frissítése</target>
<source>Do nothing</source>
<target>Nincs mit végrehajtani</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Attribútumok frissítése a jobb oldalon</target>
-<source>Warning</source>
-<target>Figyelmeztetés</target>
+<source>Errors:</source>
+<target>Hiba:</target>
+
+<source>Warnings:</source>
+<target>Figyelmeztetés:</target>
<source>Items processed:</source>
<target>Feldolgozott elemek száma:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Összes időszükséglet:</target>
+<source>Warning</source>
+<target>Figyelmeztetés</target>
+
<source>Stopped</source>
<target>Leállítva</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Hiba történt a feldolgozás közben: %x állomány, %y sor, %z oszlop.</target>
+<source>Services</source>
+<target>Szervizek</target>
+
+<source>Show All</source>
+<target>Összest Mutassa</target>
+
+<source>Hide Others</source>
+<target>Rejtse a többit</target>
+
+<source>Hide %x</source>
+<target>Rejt %x</target>
+
+<source>Quit %x</source>
+<target>Leállít %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Nem lehet beállítani a könyvtárak lockolását a következő könyvtárakhoz:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>%x könyvtár nem olvasható.</target>
-<source>/sec</source>
-<target>/mp</target>
+<source>%x/sec</source>
+<target>%x/sec</target>
-<source>%x items/sec</source>
-<target>%x elem/mp</target>
+<source>%x items</source>
+<target>%x elem</target>
<source>Show in Explorer</source>
<target>Mutassa az Intézőben</target>
@@ -392,7 +413,7 @@
<target>Sikeresen végrehajtva</target>
<source>Completed with warnings</source>
-<target>Figyelmeztetések mellett végrehajtva</target>
+<target>Figyelmeztetéssel végrehajtva</target>
<source>Completed with errors</source>
<target>Hibák mellett végrehajtva</target>
@@ -401,7 +422,7 @@
<target>Nem elérhető a Kötet Árnyék-másolat szolgáltatás.</target>
<source>Please run the 64-bit version of FreeFileSync to create shadow copies on this system.</source>
-<target>Árnyék másolatok készítéséhez ezen a rendszeren futtassa a FreeFileSync 64 bites verzióját.</target>
+<target>Ezen a rendszeren árnyék másolatok készítéséhez futtassa a FreeFileSync 64 bites verzióját.</target>
<source>Cannot determine volume name for %x.</source>
<target>%x számára nem lehet a kötet-nevet meghatározni.</target>
@@ -488,7 +509,7 @@
<target>Adja meg a célkönyvtárat a verziókövetéshez.</target>
<source>The following items have unresolved conflicts and will not be synchronized:</source>
-<target>A következő tételek feloldatlan ütközést tartalmaznak, így nem lesznek szinkronizálva:</target>
+<target>A következő elemek feloldatlan ütközést tartalmaznak, így nem lesznek szinkronizálva:</target>
<source>The following folders are significantly different. Please check that the correct folders are selected for synchronization.</source>
<target>Az érintett könyvtárak lényegileg különböznek. Kérem ellenőrizze, a megfelelő könyvtárakat választotta-e ki szinkronizálásra.</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Adatbázis generálása...</target>
-<source>Searching for excess file versions:</source>
-<target>A felesleges fájl-verziók keresése:</target>
+<source>Searching for old file versions:</source>
+<target>Régi állomány-változatok keresése:</target>
-<source>Removing excess file versions:</source>
-<target>A felesleges fájl-verziók törlése:</target>
+<source>Removing old file versions:</source>
+<target>Régi állomány-változatok törlése:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Nem képes időbélyegzés létrehozására a verzióképzéshez:</target>
@@ -585,6 +606,24 @@ Tényleges: %y bájt
<source>Unable to move %x to the recycle bin.</source>
<target>%x nem helyezhető Lomtárba.</target>
+<source>Unable to access %x.</source>
+<target>%x nem elérhető.</target>
+
+<source>Authentication completed.</source>
+<target>Hitelesítés befejezve.</target>
+
+<source>Authentication failed.</source>
+<target>Hitelesítés sikertelen.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Most bezárhatja ezt az oldalt és folytathatja a FreeFileSync-et.</target>
+
+<source>The server returned an error:</source>
+<target>A szerver hibát adott vissza:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>%x-en nem határozható meg a szabad tárterület.</target>
+
<source>Cannot find %x.</source>
<target>%x nem található.</target>
@@ -597,18 +636,12 @@ Tényleges: %y bájt
<source>Cannot delete symbolic link %x.</source>
<target>%x szimbolikus hivatkozás nem törölhető.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>%x-en nem határozható meg a szabad tárterület.</target>
-
<source>Incorrect command line:</source>
<target>Hibás parancssor:</target>
<source>The server does not support authentication via %x.</source>
<target>A szerver nem támogatja a %x általi autentikációt.</target>
-<source>Unable to access %x.</source>
-<target>%x nem elérhető.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Tényleges: %y bájt
<source>Failed to open SFTP channel number %x.</source>
<target>Hiba a %x számú SFTP csatorna megnyitása során.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 bájt</pluralform>
-<pluralform>%x bájt</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x kB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Húzd && Ejtsd</target>
@@ -764,6 +779,24 @@ A parancs végrehajtódik, ha:
<source>&Retry</source>
<target>&Ismét</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 bájt</pluralform>
+<pluralform>%x bájt</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x kB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Tölti...</target>
@@ -846,7 +879,7 @@ A parancs végrehajtódik, ha:
<target>Relatív elérési útvonal</target>
<source>Item name</source>
-<target>Tétel neve</target>
+<target>Elem neve</target>
<source>Size</source>
<target>Méret</target>
@@ -909,7 +942,7 @@ A parancs végrehajtódik, ha:
<target>Mentse &kötegelt feladatként...</target>
<source>Show &log</source>
-<target></target>
+<target>Mutassa a &logokat</target>
<source>Start &comparison</source>
<target>Kezdje az &összehasonlítást</target>
@@ -971,9 +1004,6 @@ A parancs végrehajtódik, ha:
<source>Access online storage</source>
<target>Online tároló elérése</target>
-<source>Swap sides</source>
-<target>Cserélje fel az oldalakat</target>
-
<source>Close search bar</source>
<target>Zárja be a keresési sávot</target>
@@ -998,6 +1028,9 @@ A parancs végrehajtódik, ha:
<source>View type:</source>
<target>Nézet típusa:</target>
+<source>Save as default</source>
+<target>Mentse mint alapértelmezést</target>
+
<source>Select view:</source>
<target>Válasszon nézetet:</target>
@@ -1055,6 +1088,15 @@ A parancs végrehajtódik, ha:
<source>Handle daylight saving time</source>
<target>Kezelje a nyári időszámítás különbségét</target>
+<source>Ignore errors</source>
+<target>Hagyja figyelmen kívül a hibákat</target>
+
+<source>Retry count:</source>
+<target>Visszatérések száma:</target>
+
+<source>Delay (in seconds):</source>
+<target>Késés (másodpercben):</target>
+
<source>Performance improvements:</source>
<target>Teljesítmény növelése:</target>
@@ -1129,17 +1171,11 @@ A parancs végrehajtódik, ha:
<source>Last x days:</source>
<target>legutóbbi x nap:</target>
-<source>Ignore errors</source>
-<target>Hagyja figyelmen kívül a hibákat</target>
-
-<source>Retry count:</source>
-<target>Visszatérések száma:</target>
-
-<source>Delay (in seconds):</source>
-<target>Késés (másodpercben):</target>
+<source>&Override default log path:</source>
+<target>&Felülírja az alapértelmezett log útvonalat:</target>
-<source>Run a command after synchronization:</source>
-<target>Futtasson egy parancsot a szinkronizálás után:</target>
+<source>Run a command:</source>
+<target>Futtasson egy parancsot:</target>
<source>OK</source>
<target>OK</target>
@@ -1189,6 +1225,9 @@ A parancs végrehajtódik, ha:
<source>Directory on server:</source>
<target>Könyvtár az alábbi szerveren:</target>
+<source>Access timeout (in seconds):</source>
+<target>Hozzáférési várakozási idő (másodpercben):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP csatornák száma kapcsolatonként:</target>
@@ -1223,7 +1262,7 @@ A parancs végrehajtódik, ha:
<target>Bájt</target>
<source>Items</source>
-<target>Tétel</target>
+<target>Elem</target>
<source>Synchronizing...</source>
<target>Szinkronizálás folyamatban...</target>
@@ -1267,12 +1306,6 @@ A parancs végrehajtódik, ha:
<source>Stop synchronization at first error</source>
<target>Állítsa le a szinkronizálást az első hibánál</target>
-<source>Save log:</source>
-<target>Mentse a következő naplóállományt:</target>
-
-<source>Limit number of log files:</source>
-<target>A log állományok számának korlátja:</target>
-
<source>How can I schedule a batch job?</source>
<target>Hogyan tudok kötegelt feldolgozást ütemezni?</target>
@@ -1309,8 +1342,11 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Mutassa újra az összes ideiglenesen rejtett párbeszédablakot és figyelmeztetést</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Alapértelmezett logolási útvonal:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Törölje a logokat x nap után:</target>
<source>Customize context menu:</source>
<target>Környezeti menü testreszabása:</target>
@@ -1321,8 +1357,8 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>&Default</source>
<target>&Alapértelmezett</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Várjuk a visszajelzéseket és az ötleteket</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Visszajelzést és javalatokat szívesen látjuk:</target>
<source>Home page</source>
<target>Honlap</target>
@@ -1337,7 +1373,7 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<target>Ha szereted a FreeFileSync-et:</target>
<source>Support with a donation</source>
-<target>Támogassa adománnyal</target>
+<target>Támogasd adománnyal</target>
<source>The auto updater was disabled by the administrator.</source>
<target>Az automatikus frissítést a rendszergazda kapcsolta ki.</target>
@@ -1348,8 +1384,8 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Source code written in C++ using:</source>
<target>A programot C++-ban fejlesztették a következők felhasználásával:</target>
-<source>Published under the GNU General Public License</source>
-<target>Közzétéve a GNU General Public License alatt</target>
+<source>Published under the GNU General Public License:</source>
+<target>Közzétéve GNU GPL alatt:</target>
<source>Many thanks for localization:</source>
<target>Köszönet a lokalizációért:</target>
@@ -1406,7 +1442,7 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<target>Információ</target>
<source>No log entries</source>
-<target></target>
+<target>Nincs log bejegyzés</target>
<source>Select all</source>
<target>Összeset kiválasztja</target>
@@ -1432,6 +1468,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Overview</source>
<target>Áttekintés</target>
+<source>Swap sides</source>
+<target>Cserélje fel az oldalakat</target>
+
<source>Show "%x"</source>
<target>"%x" mutatása</target>
@@ -1606,9 +1645,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Show filtered or temporarily excluded files</source>
<target>Mutassa a szűrt vagy ideiglenesen kizárt állományokat</target>
-<source>Save as default</source>
-<target>Mentse mint alapértelmezést</target>
-
<source>Filter</source>
<target>Szűrő</target>
@@ -1954,12 +1990,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is.
<source>Checking recycle bin failed for folder %x.</source>
<target>%x könyvtár vonatkozásában a lomtár ellenőrzése meghiúsult.</target>
-<source>The following XML elements could not be read:</source>
-<target>A következő XML elemek nem olvashatók:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>%x konfigurációs állomány nem teljes. A hiányzó elemeket az alapértelmezés szerinti értékekkel helyettesíti.</target>
-
<source>Prepare installation</source>
<target>Telepítés előkészítése</target>
diff --git a/FreeFileSync/Build/Languages/italian.lng b/FreeFileSync/Build/Languages/italian.lng
index 7064f93f..d5633191 100755
--- a/FreeFileSync/Build/Languages/italian.lng
+++ b/FreeFileSync/Build/Languages/italian.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Gli oggetti differiscono solo negli attributi</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Il nome %x è utilizzato da più di un elemento nella cartella.</target>
+
<source>Resolving symbolic link %x</source>
<target>Risoluzione collegamento %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>File tolleranza temporale</target>
-<source>Folder access timeout</source>
-<target>Timeout di accesso alle cartelle</target>
-
<source>Run with background priority</source>
<target>Eseguire con priorità in background</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Aggiorna attributi a destra</target>
-<source>Warning</source>
-<target>Attenzione</target>
+<source>Errors:</source>
+<target>Errori:</target>
+
+<source>Warnings:</source>
+<target>Avvertenze:</target>
<source>Items processed:</source>
<target>Oggetti processati:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Tempo totale:</target>
+<source>Warning</source>
+<target>Attenzione</target>
+
<source>Stopped</source>
<target>Arrestato</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Errore nel parsing del file %x, riga %y, colonna %z.</target>
+<source>Services</source>
+<target>Servizi</target>
+
+<source>Show All</source>
+<target>Mostra tutto</target>
+
+<source>Hide Others</source>
+<target>Nascondi gli altri</target>
+
+<source>Hide %x</source>
+<target>Nascondi %x</target>
+
+<source>Quit %x</source>
+<target>Esci da %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Impossibile impostare i blocchi di directory per le seguenti cartelle:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Impossibile leggere la directory %x.</target>
-<source>/sec</source>
-<target>/sec</target>
+<source>%x/sec</source>
+<target>%x/ sec</target>
-<source>%x items/sec</source>
-<target>%x oggetti/sec</target>
+<source>%x items</source>
+<target>%x articoli</target>
<source>Show in Explorer</source>
<target>Mostra in Esplora Risorse</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Generazione database...</target>
-<source>Searching for excess file versions:</source>
-<target>Ricerca delle versioni di file in eccesso:</target>
+<source>Searching for old file versions:</source>
+<target>Ricerca di vecchie versioni di file:</target>
-<source>Removing excess file versions:</source>
-<target>Rimozione delle versioni di file in eccesso:</target>
+<source>Removing old file versions:</source>
+<target>Rimozione delle versioni precedenti dei file:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Impossibile creare l'impronta per il controllo delle versioni:</target>
@@ -585,6 +606,24 @@ Attuale: %y byte
<source>Unable to move %x to the recycle bin.</source>
<target>Impossibile spostare %x nel cestino.</target>
+<source>Unable to access %x.</source>
+<target>Impossibile accedere %x.</target>
+
+<source>Authentication completed.</source>
+<target>Autenticazione completata.</target>
+
+<source>Authentication failed.</source>
+<target>Autenticazione fallita.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Puoi chiudere questa pagina ora e continuare con FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Il server ha restituito un errore:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Impossibile determinare lo spazio libero su disco per %x.</target>
+
<source>Cannot find %x.</source>
<target>Impossibile trovare %x.</target>
@@ -597,18 +636,12 @@ Attuale: %y byte
<source>Cannot delete symbolic link %x.</source>
<target>Impossibile eliminare collegamento %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Impossibile determinare lo spazio libero su disco per %x.</target>
-
<source>Incorrect command line:</source>
<target>Linea di comando non corretta:</target>
<source>The server does not support authentication via %x.</source>
<target>Il server non supporta l'autenticazione tramite %x.</target>
-<source>Unable to access %x.</source>
-<target>Impossibile accedere %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Attuale: %y byte
<source>Failed to open SFTP channel number %x.</source>
<target>Impossibile aprire canale SFTP numero %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x byte</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Trascina e rilascia</target>
@@ -764,6 +779,24 @@ Il comando è attivato se:
<source>&Retry</source>
<target>&Riprova</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x byte</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Caricamento...</target>
@@ -909,7 +942,7 @@ Il comando è attivato se:
<target>Salva come &processo batch...</target>
<source>Show &log</source>
-<target></target>
+<target>Mostrare &log</target>
<source>Start &comparison</source>
<target>Avvio &confronto</target>
@@ -971,9 +1004,6 @@ Il comando è attivato se:
<source>Access online storage</source>
<target>Accesso all'archiviazione online</target>
-<source>Swap sides</source>
-<target>Inverti i lati</target>
-
<source>Close search bar</source>
<target>Chiudi barra di ricerca</target>
@@ -998,6 +1028,9 @@ Il comando è attivato se:
<source>View type:</source>
<target>Visualizza tipo:</target>
+<source>Save as default</source>
+<target>Salva come predefinito</target>
+
<source>Select view:</source>
<target>Seleziona vista:</target>
@@ -1055,6 +1088,15 @@ Il comando è attivato se:
<source>Handle daylight saving time</source>
<target>Maneggiare l'ora legale</target>
+<source>Ignore errors</source>
+<target>Ignora errori</target>
+
+<source>Retry count:</source>
+<target>Riprova conteggio:</target>
+
+<source>Delay (in seconds):</source>
+<target>Ritardo (in secondi):</target>
+
<source>Performance improvements:</source>
<target>Miglioramenti delle prestazioni:</target>
@@ -1129,17 +1171,11 @@ Il comando è attivato se:
<source>Last x days:</source>
<target>Ultimi x giorni:</target>
-<source>Ignore errors</source>
-<target>Ignora errori</target>
-
-<source>Retry count:</source>
-<target>Riprova conteggio:</target>
-
-<source>Delay (in seconds):</source>
-<target>Ritardo (in secondi):</target>
+<source>&Override default log path:</source>
+<target>&Sostituisci il percorso di registrazione predefinito:</target>
-<source>Run a command after synchronization:</source>
-<target>Esegui un comando dopo la sincronizzazione:</target>
+<source>Run a command:</source>
+<target>Esegui un comando:</target>
<source>OK</source>
<target>OK</target>
@@ -1189,6 +1225,9 @@ Il comando è attivato se:
<source>Directory on server:</source>
<target>Directory su server:</target>
+<source>Access timeout (in seconds):</source>
+<target>Timeout di accesso (in secondi):</target>
+
<source>SFTP channels per connection:</source>
<target>Canali SFTP per il collegamento:</target>
@@ -1267,12 +1306,6 @@ Il comando è attivato se:
<source>Stop synchronization at first error</source>
<target>Interrompere la sincronizzazione al primo errore</target>
-<source>Save log:</source>
-<target>Salva log:</target>
-
-<source>Limit number of log files:</source>
-<target>Limita il numero di file di registro:</target>
-
<source>How can I schedule a batch job?</source>
<target>Come posso programmare un lavoro batch?</target>
@@ -1309,8 +1342,11 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Mostra di nuovo tutti i dialoghi nascosti in modo permanente e i messaggi di allarme</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Percorso di log predefinito:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Elimina i log dopo x giorni:</target>
<source>Customize context menu:</source>
<target>Personalizzare menu contestuale:</target>
@@ -1321,8 +1357,8 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>&Default</source>
<target>&Predefinito</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Ogni commento o suggerimento è ben accetto</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Feedback e suggerimenti sono benvenuti:</target>
<source>Home page</source>
<target>Pagina Iniziale</target>
@@ -1348,8 +1384,8 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Source code written in C++ using:</source>
<target>Codice sorgente scritto in C++ utilizzando:</target>
-<source>Published under the GNU General Public License</source>
-<target>Pubblicato con licenza GNU General Public</target>
+<source>Published under the GNU General Public License:</source>
+<target>Pubblicato sotto la GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Ringraziamenti per la traduzione:</target>
@@ -1406,7 +1442,7 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Nessuna voce di registro</target>
<source>Select all</source>
<target>Seleziona tutto</target>
@@ -1432,6 +1468,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Overview</source>
<target>Anteprima</target>
+<source>Swap sides</source>
+<target>Inverti i lati</target>
+
<source>Show "%x"</source>
<target>Mostra "%x"</target>
@@ -1606,9 +1645,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Show filtered or temporarily excluded files</source>
<target>Mostra file filtrati o temporaneamente esclusi</target>
-<source>Save as default</source>
-<target>Salva come predefinito</target>
-
<source>Filter</source>
<target>Filtro</target>
@@ -1954,12 +1990,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave.
<source>Checking recycle bin failed for folder %x.</source>
<target>Controllo cestino non riuscito per la cartella %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>I seguenti elementi XML non possono essere letti:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>File di configurazione %x incompleta. Gli elementi mancanti saranno impostati sui valori predefiniti.</target>
-
<source>Prepare installation</source>
<target>Preparare l'installazione</target>
diff --git a/FreeFileSync/Build/Languages/japanese.lng b/FreeFileSync/Build/Languages/japanese.lng
index c1ca09e4..388bc96b 100755
--- a/FreeFileSync/Build/Languages/japanese.lng
+++ b/FreeFileSync/Build/Languages/japanese.lng
@@ -110,7 +110,7 @@
<target>選択された構成を実行しないで編集用に開きます.</target>
<source>Path to an alternate GlobalSettings.xml file.</source>
-<target></target>
+<target>代替 GlobalSettings.xml ファイルへのパス.</target>
<source>Installation files are corrupted. Please reinstall FreeFileSync.</source>
<target>インストール ファイルが破損しています、FreeFileSync を再インストールしてください.</target>
@@ -159,6 +159,9 @@
<source>Items differ in attributes only</source>
<target>属性のみ異なる項目</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>名前 %x は、フォルダ内複数の項目で使用されています.</target>
+
<source>Resolving symbolic link %x</source>
<target>シンボリックリンク %x を解決中</target>
@@ -186,9 +189,6 @@
<source>File time tolerance</source>
<target>ファイル時間の許容範囲</target>
-<source>Folder access timeout</source>
-<target>フォルダ アクセスのタイムアウト</target>
-
<source>Run with background priority</source>
<target>優先度バックグラウンドで実行</target>
@@ -332,8 +332,11 @@
<source>Update attributes on right</source>
<target>右の属性を更新</target>
-<source>Warning</source>
-<target>警告</target>
+<source>Errors:</source>
+<target>エラー:</target>
+
+<source>Warnings:</source>
+<target>警告:</target>
<source>Items processed:</source>
<target>処理された要素:</target>
@@ -344,6 +347,9 @@
<source>Total time:</source>
<target>合計時間:</target>
+<source>Warning</source>
+<target>警告</target>
+
<source>Stopped</source>
<target>停止</target>
@@ -353,6 +359,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>ファイル %x の構文解析エラー, 行 %y, 列 %z.</target>
+<source>Services</source>
+<target>サービス</target>
+
+<source>Show All</source>
+<target>すべて表示</target>
+
+<source>Hide Others</source>
+<target>他を非表示</target>
+
+<source>Hide %x</source>
+<target>%x を非表示</target>
+
+<source>Quit %x</source>
+<target>%x を終了</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>次のフォルダにあるディレクトリはロックできません:</target>
@@ -367,11 +388,11 @@
<source>Cannot read directory %x.</source>
<target>ディレクトリ %x を読み取れません.</target>
-<source>/sec</source>
-<target>/秒</target>
+<source>%x/sec</source>
+<target>%x/秒</target>
-<source>%x items/sec</source>
-<target>%x 項目/秒</target>
+<source>%x items</source>
+<target>%x 項目</target>
<source>Show in Explorer</source>
<target>エクスプローラで表示</target>
@@ -520,11 +541,11 @@
<source>Generating database...</source>
<target>データベースを作成中...</target>
-<source>Searching for excess file versions:</source>
-<target>ファイルの余剰なバージョンを検索:</target>
+<source>Searching for old file versions:</source>
+<target>古いファイルバージョンの検索:</target>
-<source>Removing excess file versions:</source>
-<target>ファイルの余剰なバージョンを除去:</target>
+<source>Removing old file versions:</source>
+<target>古いファイルバージョンの削除:</target>
<source>Unable to create time stamp for versioning:</source>
<target>バージョン管理のタイムスタンプを作成できません:</target>
@@ -582,6 +603,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>%x をゴミ箱に移動できません.</target>
+<source>Unable to access %x.</source>
+<target>%x にアクセスできません.</target>
+
+<source>Authentication completed.</source>
+<target>認証が完了しました.</target>
+
+<source>Authentication failed.</source>
+<target>認証に失敗しました.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>このページを閉じて FreeFileSync. を続行することができます.</target>
+
+<source>The server returned an error:</source>
+<target>サーバはエラーを返しました:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>%x の空きディスク領域を検出できません.</target>
+
<source>Cannot find %x.</source>
<target>%x がみつかりません.</target>
@@ -594,18 +633,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>シンボリック リンク %x を削除できません.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>%x の空きディスク領域を検出できません.</target>
-
<source>Incorrect command line:</source>
<target>不正なコマンドライン:</target>
<source>The server does not support authentication via %x.</source>
<target>このサーバは %x による認証に対応していません.</target>
-<source>Unable to access %x.</source>
-<target>%x にアクセスできません.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -628,23 +661,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>SFTP チャンネル %x を開けません.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x バイト</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>ドラッグ && ドロップ</target>
@@ -688,7 +704,7 @@ Actual: %y bytes
<target>3. 'スタート'をクリック.</target>
<source>To get started just import a "ffs_batch" file.</source>
-<target></target>
+<target>"ffs_batch" をインポート後、すぐに開始.</target>
<source>Folders to watch:</source>
<target>監視するフォルダ:</target>
@@ -758,6 +774,23 @@ The command is triggered if:
<source>&Retry</source>
<target>再試行(&R)</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x バイト</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>読み込み中...</target>
@@ -902,7 +935,7 @@ The command is triggered if:
<target>一括ジョブで保存(&B)...</target>
<source>Show &log</source>
-<target></target>
+<target>ログを表示(&L)</target>
<source>Start &comparison</source>
<target>比較を開始(&C)</target>
@@ -964,9 +997,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>オンライン ストレージにアクセス</target>
-<source>Swap sides</source>
-<target>パネルを入れ替え</target>
-
<source>Close search bar</source>
<target>検索バーを閉じる</target>
@@ -991,6 +1021,9 @@ The command is triggered if:
<source>View type:</source>
<target>表示形式:</target>
+<source>Save as default</source>
+<target>既定として保存</target>
+
<source>Select view:</source>
<target>選択表示:</target>
@@ -1048,6 +1081,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>夏時間の取り扱い</target>
+<source>Ignore errors</source>
+<target>エラーを無視</target>
+
+<source>Retry count:</source>
+<target>再試行回数:</target>
+
+<source>Delay (in seconds):</source>
+<target>遅延 (秒で指定):</target>
+
<source>Performance improvements:</source>
<target>パフォーマンス向上:</target>
@@ -1122,17 +1164,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>x 日以降:</target>
-<source>Ignore errors</source>
-<target>エラーを無視</target>
-
-<source>Retry count:</source>
-<target>再試行回数:</target>
-
-<source>Delay (in seconds):</source>
-<target>遅延 (秒で指定):</target>
+<source>&Override default log path:</source>
+<target>既定のログ保存パスに上書き(&Q):</target>
-<source>Run a command after synchronization:</source>
-<target>同期処理後に実行するコマンド:</target>
+<source>Run a command:</source>
+<target>コマンドを実行:</target>
<source>OK</source>
<target>OK</target>
@@ -1182,6 +1218,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>サーバ上のディレクトリ:</target>
+<source>Access timeout (in seconds):</source>
+<target>アクセスのタイムアウト(秒):</target>
+
<source>SFTP channels per connection:</source>
<target>接続当たりの SFTP チャンネル数:</target>
@@ -1260,12 +1299,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>最初のエラーで同期処理を停止</target>
-<source>Save log:</source>
-<target>ログ保存:</target>
-
-<source>Limit number of log files:</source>
-<target>ログファイル数の制限:</target>
-
<source>How can I schedule a batch job?</source>
<target>一括ジョブ スケジュールの作成方法</target>
@@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>非表示にしたすべてのダイアログと警告メッセージを再表示</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>既定のログ保存パス:</target>
+
+<source>&Delete logs after x days:</source>
+<target>x 日経過後にログを削除(&D):</target>
<source>Customize context menu:</source>
<target>コンテキストメニューのカスタマイズ:</target>
@@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>デフォルト(&D)</target>
-<source>Feedback and suggestions are welcome</source>
-<target>フィードバック、提案などはこちらから</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>フィードバック、提案はいつでも歓迎します:</target>
<source>Home page</source>
<target>ホーム ページ</target>
@@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>ソースコードは C++ で書かれています:</target>
-<source>Published under the GNU General Public License</source>
-<target>GNU 一般共有使用許諾に基づき公開</target>
+<source>Published under the GNU General Public License:</source>
+<target>GNU 一般公衆ライセンスの下で公開されています:</target>
<source>Many thanks for localization:</source>
<target>ローカライズのご協力に感謝します:</target>
@@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error.
<target>情報</target>
<source>No log entries</source>
-<target></target>
+<target>ログ エントリなし</target>
<source>Select all</source>
<target>すべて選択</target>
@@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>概要</target>
+<source>Swap sides</source>
+<target>パネルを入れ替え</target>
+
<source>Show "%x"</source>
<target>"%x" で表示</target>
@@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>フィルター済、または一時除外ファイルを表示</target>
-<source>Save as default</source>
-<target>既定として保存</target>
-
<source>Filter</source>
<target>フィルター</target>
@@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>フォルダ %x のゴミ箱のチェックに失敗.</target>
-<source>The following XML elements could not be read:</source>
-<target>次の XML 要素は読み取ることができません:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>構成設定ファイル %x は不完全です。存在しない要素が既定値としてセットされています.</target>
-
<source>Prepare installation</source>
<target>インストールの準備</target>
diff --git a/FreeFileSync/Build/Languages/korean.lng b/FreeFileSync/Build/Languages/korean.lng
index 811c7c51..91efd696 100755
--- a/FreeFileSync/Build/Languages/korean.lng
+++ b/FreeFileSync/Build/Languages/korean.lng
@@ -142,7 +142,7 @@
</target>
<source>File %x has an invalid date.</source>
-<target>파일 %x 의 날짜가 유효하지 않습니다.</target>
+<target>파일 %x의 날짜가 유효하지 않습니다.</target>
<source>Date:</source>
<target>날짜:</target>
@@ -159,6 +159,9 @@
<source>Items differ in attributes only</source>
<target>항목들이 속성에서만 차이가 있습니다.</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>이름 %x이(가) 같은 폴더 내 한 개 이상의 항목에서 사용되고 있습니다.</target>
+
<source>Resolving symbolic link %x</source>
<target>심볼릭 링크 %x 해결</target>
@@ -186,9 +189,6 @@
<source>File time tolerance</source>
<target>파일 시간 허용</target>
-<source>Folder access timeout</source>
-<target>폴더 접근 제한</target>
-
<source>Run with background priority</source>
<target>배경 우선 순위로 실행</target>
@@ -332,8 +332,11 @@
<source>Update attributes on right</source>
<target>우측 속성 업데이트</target>
-<source>Warning</source>
-<target>경고</target>
+<source>Errors:</source>
+<target>오류:</target>
+
+<source>Warnings:</source>
+<target>경고:</target>
<source>Items processed:</source>
<target>처리된 항목:</target>
@@ -344,6 +347,9 @@
<source>Total time:</source>
<target>전체 시간:</target>
+<source>Warning</source>
+<target>경고</target>
+
<source>Stopped</source>
<target>중단</target>
@@ -353,6 +359,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>분석 오류 - 파일: %x; 행: %y; 열: %z.</target>
+<source>Services</source>
+<target>서비스</target>
+
+<source>Show All</source>
+<target>모두 보이기</target>
+
+<source>Hide Others</source>
+<target>기타 다른 항목 숨기기</target>
+
+<source>Hide %x</source>
+<target>%x 숨기기</target>
+
+<source>Quit %x</source>
+<target>%x 끝내기</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>다음 폴더의 디렉터리 잠금을 설정할 수 없습니다:</target>
@@ -367,10 +388,10 @@
<source>Cannot read directory %x.</source>
<target>디렉터리 %x을(를) 읽을 수 없습니다.</target>
-<source>/sec</source>
-<target>/초</target>
+<source>%x/sec</source>
+<target>%x/초</target>
-<source>%x items/sec</source>
+<source>%x items</source>
<target>%x 항목</target>
<source>Show in Explorer</source>
@@ -416,7 +437,7 @@
<target>파일 크기</target>
<source>Two way</source>
-<target>양측 방향 (Two Way)</target>
+<target>양측 방향 (투 웨이)</target>
<source>Mirror</source>
<target>미러</target>
@@ -520,11 +541,11 @@
<source>Generating database...</source>
<target>데이터베이스 생성 중...</target>
-<source>Searching for excess file versions:</source>
-<target>초과 파일 버전 검색 중:</target>
+<source>Searching for old file versions:</source>
+<target>이전 파일 버전 검색 중:</target>
-<source>Removing excess file versions:</source>
-<target>초과 파일 버전 제거 중:</target>
+<source>Removing old file versions:</source>
+<target>이전 파일 버전 제거 중:</target>
<source>Unable to create time stamp for versioning:</source>
<target>버전 관리를 위한 타임 스탬프 생성 불가:</target>
@@ -582,6 +603,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>휴지통으로 %x을(를) 이동할 수 없습니다.</target>
+<source>Unable to access %x.</source>
+<target>%x에 접근할 수 없습니다.</target>
+
+<source>Authentication completed.</source>
+<target>인증 완료.</target>
+
+<source>Authentication failed.</source>
+<target>인증 실패.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>지금 이 페이지를 닫고 FreeFileSync를 계속 진행할 수 있습니다.</target>
+
+<source>The server returned an error:</source>
+<target>서버가 오류를 반환했습니다:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>%x에 대한 사용 가능한 디스크 공간을 확인할 수 없습니다.</target>
+
<source>Cannot find %x.</source>
<target>%x을(를) 찾을 수 없습니다.</target>
@@ -594,18 +633,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>심볼릭 링크 %x을(를) 삭제할 수 없습니다.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>%x에 대한 사용 가능한 디스크 공간을 확인할 수 없습니다.</target>
-
<source>Incorrect command line:</source>
<target>부정확한 명령줄:</target>
<source>The server does not support authentication via %x.</source>
<target>서버가 %x을(를) 통한 인증을 지원하지 않습니다.</target>
-<source>Unable to access %x.</source>
-<target>%x에 접근할 수 없습니다.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -628,23 +661,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>SFTP 채널번호 %x을(를) 열지 못 했습니다.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x 바이트</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>끌어서 놓기(&&) [드래그-앤-드랍]</target>
@@ -758,6 +774,23 @@ The command is triggered if:
<source>&Retry</source>
<target>다시 시도(&R)</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x 바이트</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>로드 중...</target>
@@ -902,7 +935,7 @@ The command is triggered if:
<target>일괄 작업으로 저장(&b)...</target>
<source>Show &log</source>
-<target></target>
+<target>로그 표시(&l)</target>
<source>Start &comparison</source>
<target>비교 시작(&C)</target>
@@ -964,9 +997,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>온라인 저장소 접속</target>
-<source>Swap sides</source>
-<target>양측 위치 바꾸기</target>
-
<source>Close search bar</source>
<target>검색 창 닫기</target>
@@ -991,6 +1021,9 @@ The command is triggered if:
<source>View type:</source>
<target>유형 보기:</target>
+<source>Save as default</source>
+<target>기본 값으로 저장</target>
+
<source>Select view:</source>
<target>보기 선택:</target>
@@ -1048,6 +1081,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>서머타임 설정</target>
+<source>Ignore errors</source>
+<target>오류 무시</target>
+
+<source>Retry count:</source>
+<target>재시도 횟수:</target>
+
+<source>Delay (in seconds):</source>
+<target>지연 (초 단위):</target>
+
<source>Performance improvements:</source>
<target>성능 향상:</target>
@@ -1122,17 +1164,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>최근 x일:</target>
-<source>Ignore errors</source>
-<target>오류 무시</target>
-
-<source>Retry count:</source>
-<target>재시도 횟수:</target>
-
-<source>Delay (in seconds):</source>
-<target>지연 (초 단위):</target>
+<source>&Override default log path:</source>
+<target>기본 로그 경로 다시 정의(&O):</target>
-<source>Run a command after synchronization:</source>
-<target>동기화 이후 명령 실행:</target>
+<source>Run a command:</source>
+<target>명령 실행:</target>
<source>OK</source>
<target>확인</target>
@@ -1182,6 +1218,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>서버 디렉터리:</target>
+<source>Access timeout (in seconds):</source>
+<target>액세스 제한 시간(초):</target>
+
<source>SFTP channels per connection:</source>
<target>연결 당 SFTP 채널 수:</target>
@@ -1260,12 +1299,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>첫 오류 발생 시 동기화 중지</target>
-<source>Save log:</source>
-<target>로그 저장:</target>
-
-<source>Limit number of log files:</source>
-<target>로그 파일 개수 제한:</target>
-
<source>How can I schedule a batch job?</source>
<target>일괄 작업 예약 방법은?</target>
@@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>영구적으로 숨겨진 모든 대화 상자 및 경고 메세지 다시 보이기</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>기본 로그 경로:</target>
+
+<source>&Delete logs after x days:</source>
+<target>x일 후에 로그 삭제(&D):</target>
<source>Customize context menu:</source>
<target>컨텍스트 메뉴 커스터마이즈 (사용자 정의):</target>
@@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>기본 설정/값(&D)</target>
-<source>Feedback and suggestions are welcome</source>
-<target>모든 의견 및 건의/제안을 환영합니다</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>모든 의견 및 제안을 환영합니다:</target>
<source>Home page</source>
<target>홈페이지</target>
@@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>소스코드는 C++ 언어로 아래 툴을 사용하여 작성되었습니다:</target>
-<source>Published under the GNU General Public License</source>
-<target>GNU 일반 공용 라이센스에 의한 출시</target>
+<source>Published under the GNU General Public License:</source>
+<target>GNU 일반 공용 라이센스에 의한 출시:</target>
<source>Many thanks for localization:</source>
<target>현지화 작업에 깊은 감사 드립니다:</target>
@@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error.
<target>정보</target>
<source>No log entries</source>
-<target></target>
+<target>로그 항목 없음</target>
<source>Select all</source>
<target>모두 선택</target>
@@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>개요</target>
+<source>Swap sides</source>
+<target>양측 위치 바꾸기</target>
+
<source>Show "%x"</source>
<target>"%x" 표시</target>
@@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>필터링 또는 일시적으로 제외된 파일 보이기</target>
-<source>Save as default</source>
-<target>기본 값으로 저장</target>
-
<source>Filter</source>
<target>필터</target>
@@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>폴더 %x에 관한 휴지통 실패 확인 중.</target>
-<source>The following XML elements could not be read:</source>
-<target>다음 XML 요소를 읽을 수 없습니다:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>구성 파일 %x이(가) 불완전합니다. 누락된 요소는 기본 값으로 설정됩니다.</target>
-
<source>Prepare installation</source>
<target>설치 준비</target>
diff --git a/FreeFileSync/Build/Languages/lithuanian.lng b/FreeFileSync/Build/Languages/lithuanian.lng
index efb1c91f..e7029e08 100755
--- a/FreeFileSync/Build/Languages/lithuanian.lng
+++ b/FreeFileSync/Build/Languages/lithuanian.lng
@@ -161,6 +161,9 @@
<source>Items differ in attributes only</source>
<target>Elementai skiriasi tik atributais</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Šis pavadinimas %x yra naudojamas daugiau nei vieną kartą.</target>
+
<source>Resolving symbolic link %x</source>
<target>Ieškoma simbolinės nuorodos %x</target>
@@ -188,9 +191,6 @@
<source>File time tolerance</source>
<target>Failo laiko nuokrypis</target>
-<source>Folder access timeout</source>
-<target>Pasibaigė skirtas laikas aplankui pasiekti</target>
-
<source>Run with background priority</source>
<target>Vykdyti kaip aukštesnio prioriteto poprograme</target>
@@ -336,8 +336,11 @@
<source>Update attributes on right</source>
<target>Atnaujinti atributus dešinėje</target>
-<source>Warning</source>
-<target>Perspėjimas</target>
+<source>Errors:</source>
+<target>Klaidos:</target>
+
+<source>Warnings:</source>
+<target>Įspėimai:</target>
<source>Items processed:</source>
<target>Elementų apdorota:</target>
@@ -348,6 +351,9 @@
<source>Total time:</source>
<target>Visas laikas:</target>
+<source>Warning</source>
+<target>Perspėjimas</target>
+
<source>Stopped</source>
<target>Sustabdyta</target>
@@ -357,6 +363,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Klaida trinant failą %x, eilė %y, stulpelis %z.</target>
+<source>Services</source>
+<target>Paslaugos</target>
+
+<source>Show All</source>
+<target>Rodyti Visus</target>
+
+<source>Hide Others</source>
+<target>Slėpti Kitus</target>
+
+<source>Hide %x</source>
+<target>Slėpti %x</target>
+
+<source>Quit %x</source>
+<target>Išeiti %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Negalima užrakinti šiuos aplankus kataloge:</target>
@@ -373,11 +394,11 @@
<source>Cannot read directory %x.</source>
<target>Negalima nuskaityti katalogo %x.</target>
-<source>/sec</source>
-<target>/sek.</target>
+<source>%x/sec</source>
+<target>%x/sek</target>
-<source>%x items/sec</source>
-<target>%x elementų/sek.</target>
+<source>%x items</source>
+<target>%x elementų</target>
<source>Show in Explorer</source>
<target>Rodyti naršyklėje</target>
@@ -526,11 +547,11 @@
<source>Generating database...</source>
<target>Sukuriama duomenų bazė...</target>
-<source>Searching for excess file versions:</source>
-<target>Perviršio failų versijos paieška:</target>
+<source>Searching for old file versions:</source>
+<target>Ieškoma senų failo versijų:</target>
-<source>Removing excess file versions:</source>
-<target>Perviršio failų versijos pašąlinimas:</target>
+<source>Removing old file versions:</source>
+<target>Pašalinamos senos failo versijos:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Nepavyko sukurti versijos laiko žymą:</target>
@@ -588,6 +609,24 @@ Esamas: %y baitai
<source>Unable to move %x to the recycle bin.</source>
<target>%x į šiukšliadėžę perkelti nepavyko.</target>
+<source>Unable to access %x.</source>
+<target>Nepavyko prisijungti %x.</target>
+
+<source>Authentication completed.</source>
+<target>Autentifikavimas baigtas.</target>
+
+<source>Authentication failed.</source>
+<target>Nepavyko autentifikuoti.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Jūs dabar galite uždaryti šį langą ir testi FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Serveris gražino klaidą:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Negalima nustatyti laisvos disko dalies %x.</target>
+
<source>Cannot find %x.</source>
<target>Negalima surasti %x.</target>
@@ -600,18 +639,12 @@ Esamas: %y baitai
<source>Cannot delete symbolic link %x.</source>
<target>Negalima ištrinti virtualios nuorodos %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Negalima nustatyti laisvos disko dalies %x.</target>
-
<source>Incorrect command line:</source>
<target>Netaisyklinga Komandinė eilutė:</target>
<source>The server does not support authentication via %x.</source>
<target>Serveris nepalaiko %x autentifikavimo.</target>
-<source>Unable to access %x.</source>
-<target>Nepavyko prisijungti %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -638,25 +671,6 @@ Esamas: %y baitai
<source>Failed to open SFTP channel number %x.</source>
<target>Nepavyko atidaryti SFTP kanalo %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x baitas</pluralform>
-<pluralform>%x baitai</pluralform>
-<pluralform>%x baitų</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Vilkti && Numesti</target>
@@ -770,6 +784,25 @@ Komanda inicijuojama jei:
<source>&Retry</source>
<target>&Bandyti vėl</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x baitas</pluralform>
+<pluralform>%x baitai</pluralform>
+<pluralform>%x baitų</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Įkraunama...</target>
@@ -916,7 +949,7 @@ Komanda inicijuojama jei:
<target>Išsaugoti kaip &užduočių paketą...</target>
<source>Show &log</source>
-<target></target>
+<target>Rodyti &žurnalą</target>
<source>Start &comparison</source>
<target>Pradėti &palyginimą</target>
@@ -978,9 +1011,6 @@ Komanda inicijuojama jei:
<source>Access online storage</source>
<target>Internetinės saugyklos prieiga</target>
-<source>Swap sides</source>
-<target>Sukeisti puses</target>
-
<source>Close search bar</source>
<target>Uždaryti paieškos įrankinę</target>
@@ -1005,6 +1035,9 @@ Komanda inicijuojama jei:
<source>View type:</source>
<target>Rodymo būdai:</target>
+<source>Save as default</source>
+<target>Išsaugoti kaip pagrindinį</target>
+
<source>Select view:</source>
<target>Pasirinkti rodymo būdą:</target>
@@ -1062,6 +1095,15 @@ Komanda inicijuojama jei:
<source>Handle daylight saving time</source>
<target>Naudoti vasaros laiką</target>
+<source>Ignore errors</source>
+<target>Ignoruoti klaidas</target>
+
+<source>Retry count:</source>
+<target>Skaičiuoti is naujo:</target>
+
+<source>Delay (in seconds):</source>
+<target>Uždelsimas (sekundėmis):</target>
+
<source>Performance improvements:</source>
<target>Veiklos gerinimai:</target>
@@ -1136,17 +1178,11 @@ Komanda inicijuojama jei:
<source>Last x days:</source>
<target>Paskutinių x dienų:</target>
-<source>Ignore errors</source>
-<target>Ignoruoti klaidas</target>
-
-<source>Retry count:</source>
-<target>Skaičiuoti is naujo:</target>
-
-<source>Delay (in seconds):</source>
-<target>Uždelsimas (sekundėmis):</target>
+<source>&Override default log path:</source>
+<target>&Pakeisti pradinę žurnalo nuorodą:</target>
-<source>Run a command after synchronization:</source>
-<target>Po sinchronizavimo vykdyti komandą:</target>
+<source>Run a command:</source>
+<target>Vykdyti komandą:</target>
<source>OK</source>
<target>Gerai</target>
@@ -1196,6 +1232,9 @@ Komanda inicijuojama jei:
<source>Directory on server:</source>
<target>Katalogas serveryje:</target>
+<source>Access timeout (in seconds):</source>
+<target>Prieigos laikas pasibaigs (sekundės):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP kanalai per jungtį:</target>
@@ -1274,12 +1313,6 @@ Komanda inicijuojama jei:
<source>Stop synchronization at first error</source>
<target>Stabdyti suvienodinimą, pirmai klaidai įvykus</target>
-<source>Save log:</source>
-<target>Išsaugoti žurnalą:</target>
-
-<source>Limit number of log files:</source>
-<target>Žurnalinių failų riba:</target>
-
<source>How can I schedule a batch job?</source>
<target>Kaip aš galiu užduočių paketą įtraukti į tvarkaraštį?</target>
@@ -1316,8 +1349,11 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Vėl rodyti visus paslėptus dialogo langus ir įspėjamuosius pranešimus</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Pradinė žurnalo nuoroda:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Ištrinti žurnalo įrašus po x dienų:</target>
<source>Customize context menu:</source>
<target>Pagrindinio meniu pasirinkimai:</target>
@@ -1328,8 +1364,8 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>&Default</source>
<target>&Numatyta</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Nuomonė ir patarimai laukiami</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Nuomonės ir pasiūlymai yra laukiami:</target>
<source>Home page</source>
<target>Pagrindinis puslapis</target>
@@ -1355,8 +1391,8 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Source code written in C++ using:</source>
<target>Šaltinio kodas parašytas su C++ naudojant:</target>
-<source>Published under the GNU General Public License</source>
-<target>Platinama su GNU General Public licenzija</target>
+<source>Published under the GNU General Public License:</source>
+<target>Publikuota naudojant GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Labai dėkojame už vertimą:</target>
@@ -1413,7 +1449,7 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<target>Informacija</target>
<source>No log entries</source>
-<target></target>
+<target>Nėra žurnalo įrašų</target>
<source>Select all</source>
<target>Pažymėti visus</target>
@@ -1439,6 +1475,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Overview</source>
<target>Apžvalga</target>
+<source>Swap sides</source>
+<target>Sukeisti puses</target>
+
<source>Show "%x"</source>
<target>Rodyti "%x"</target>
@@ -1617,9 +1656,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Show filtered or temporarily excluded files</source>
<target>Rodyti išfiltruotus ar laikinai išskirtus failus</target>
-<source>Save as default</source>
-<target>Išsaugoti kaip pagrindinį</target>
-
<source>Filter</source>
<target>Filtras</target>
@@ -1970,12 +2006,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai.
<source>Checking recycle bin failed for folder %x.</source>
<target>Tikrinama šiukšliadėžė. Failas %x nerastas.</target>
-<source>The following XML elements could not be read:</source>
-<target>Šie XML elementai yra neperskaitomi:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Konfiguracinis failas %x yra neužbaigtas. Trūkstamiems elementams bus priskirtos pradinės rekšmės.</target>
-
<source>Prepare installation</source>
<target>Ruošiamasi įdiegimui</target>
diff --git a/FreeFileSync/Build/Languages/norwegian.lng b/FreeFileSync/Build/Languages/norwegian.lng
index 134e2215..97e97cce 100755
--- a/FreeFileSync/Build/Languages/norwegian.lng
+++ b/FreeFileSync/Build/Languages/norwegian.lng
@@ -7,15 +7,6 @@
<plural_definition>n == 1 ? 0 : 1</plural_definition>
</header>
-<source>No log entries</source>
-<target></target>
-
-<source>Remove old log files after x days:</source>
-<target></target>
-
-<source>Show &log</source>
-<target></target>
-
<source>Both sides have changed since last synchronization.</source>
<target>Begge sider er endret siden siste synkronisering.</target>
@@ -169,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Elementene har kun forskjellige attributter</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Navnet %x brukes av mer enn ett element i mappen.</target>
+
<source>Resolving symbolic link %x</source>
<target>Løser symbolsk forbindelse %x</target>
@@ -196,9 +190,6 @@
<source>File time tolerance</source>
<target>Filtid toleranse</target>
-<source>Folder access timeout</source>
-<target>Tidsavbrudd for mappetilgang</target>
-
<source>Run with background priority</source>
<target>Kjør med bakgrunns-prioritet</target>
@@ -343,8 +334,11 @@
<source>Update attributes on right</source>
<target>Oppdater attributter til høyre</target>
-<source>Warning</source>
-<target>Advarsel</target>
+<source>Errors:</source>
+<target>Feil:</target>
+
+<source>Warnings:</source>
+<target>Advarsler:</target>
<source>Items processed:</source>
<target>Elementer behandlet:</target>
@@ -355,6 +349,9 @@
<source>Total time:</source>
<target>Samlet tid:</target>
+<source>Warning</source>
+<target>Advarsel</target>
+
<source>Stopped</source>
<target>Stoppet</target>
@@ -364,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Behandlingsfeil i filen %x, rad %y, kolonne %z.</target>
+<source>Services</source>
+<target>Tjenester</target>
+
+<source>Show All</source>
+<target>Vis alt</target>
+
+<source>Hide Others</source>
+<target>Skjul andre</target>
+
+<source>Hide %x</source>
+<target>Skjul %x</target>
+
+<source>Quit %x</source>
+<target>Avslutt %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Kan ikke angi kataloglås for følgende mapper:</target>
@@ -379,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Kan ikke lese katalogen %x.</target>
-<source>/sec</source>
-<target>/sek</target>
+<source>%x/sec</source>
+<target>%x/sek</target>
-<source>%x items/sec</source>
-<target>%x elementer/sek</target>
+<source>%x items</source>
+<target>%x elementer</target>
<source>Show in Explorer</source>
<target>Vis i Explorer</target>
@@ -532,11 +544,11 @@
<source>Generating database...</source>
<target>Lager database...</target>
-<source>Searching for excess file versions:</source>
-<target>Søker etter overskytende filversjoner:</target>
+<source>Searching for old file versions:</source>
+<target>Leter etter gamle filversjoner:</target>
-<source>Removing excess file versions:</source>
-<target>Fjerne overskytende filversjoner:</target>
+<source>Removing old file versions:</source>
+<target>Fjerner gamle filversjoner:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Kan ikke opprette tidsstempel til versjonen:</target>
@@ -594,6 +606,24 @@ Faktisk: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Kunne ikke flytte %x til papirkurven.</target>
+<source>Unable to access %x.</source>
+<target>Får ikke tilgang til %x.</target>
+
+<source>Authentication completed.</source>
+<target>Autentisering er ferdig.</target>
+
+<source>Authentication failed.</source>
+<target>Autentisering mislyktes.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Du kan lukke denne siden nå og fortsette med FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Serveren returnerte en feilmelding:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Kan ikke fastslå ledig diskplass for %x.</target>
+
<source>Cannot find %x.</source>
<target>Kan ikke finne %x.</target>
@@ -606,18 +636,12 @@ Faktisk: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Kan ikke slette symbolsk lenke %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Kan ikke fastslå ledig diskplass for %x.</target>
-
<source>Incorrect command line:</source>
<target>Ugyldig kommando:</target>
<source>The server does not support authentication via %x.</source>
<target>Tjeneren støtter ikke autentisering via %x.</target>
-<source>Unable to access %x.</source>
-<target>Får ikke tilgang til %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -642,24 +666,6 @@ Faktisk: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Kunne ikke åpne SFTP kanalnummer %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x byte</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Dra && slipp</target>
@@ -773,6 +779,24 @@ Kommandoen utføres hvis:
<source>&Retry</source>
<target>&Prøv igjen</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x byte</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Laster...</target>
@@ -917,6 +941,9 @@ Kommandoen utføres hvis:
<source>Save as &batch job...</source>
<target>Lagre som &batchfil...</target>
+<source>Show &log</source>
+<target>Vis &logg</target>
+
<source>Start &comparison</source>
<target>Start &sammenligning</target>
@@ -977,9 +1004,6 @@ Kommandoen utføres hvis:
<source>Access online storage</source>
<target>Få tilgang til nettlagring</target>
-<source>Swap sides</source>
-<target>Bytt sider</target>
-
<source>Close search bar</source>
<target>Lukk søkelinja</target>
@@ -1004,6 +1028,9 @@ Kommandoen utføres hvis:
<source>View type:</source>
<target>Visning:</target>
+<source>Save as default</source>
+<target>Lagre som standard</target>
+
<source>Select view:</source>
<target>Velg visning:</target>
@@ -1061,6 +1088,15 @@ Kommandoen utføres hvis:
<source>Handle daylight saving time</source>
<target>Behandle sommertid</target>
+<source>Ignore errors</source>
+<target>Ignorér feil</target>
+
+<source>Retry count:</source>
+<target>Antall forsøk:</target>
+
+<source>Delay (in seconds):</source>
+<target>Forsinkelse (i sekunder):</target>
+
<source>Performance improvements:</source>
<target>Ytelsesforbedringer:</target>
@@ -1135,17 +1171,11 @@ Kommandoen utføres hvis:
<source>Last x days:</source>
<target>Siste x dager:</target>
-<source>Ignore errors</source>
-<target>Ignorér feil</target>
-
-<source>Retry count:</source>
-<target>Antall forsøk:</target>
+<source>&Override default log path:</source>
+<target>&Velg en annen loggbane:</target>
-<source>Delay (in seconds):</source>
-<target>Forsinkelse (i sekunder):</target>
-
-<source>Run a command after synchronization:</source>
-<target>Kjør en kommando etter synkronisering:</target>
+<source>Run a command:</source>
+<target>Kjør en kommando:</target>
<source>OK</source>
<target>OK</target>
@@ -1195,6 +1225,9 @@ Kommandoen utføres hvis:
<source>Directory on server:</source>
<target>Katalog på server:</target>
+<source>Access timeout (in seconds):</source>
+<target>Få tilgang til tidsavbrudd (i sekunder):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP kanaler per tilkobling:</target>
@@ -1273,12 +1306,6 @@ Kommandoen utføres hvis:
<source>Stop synchronization at first error</source>
<target>Stopp synkronisering ved første feil</target>
-<source>Save log:</source>
-<target>Lagre logg:</target>
-
-<source>Limit number of log files:</source>
-<target>Begrens antall loggfiler:</target>
-
<source>How can I schedule a batch job?</source>
<target>Hvordan kan jeg lage en batchfil?</target>
@@ -1315,6 +1342,12 @@ Sikrer prosessen ved alvorlige feil.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Vis alle skjulte vinduer og advarsler igjen</target>
+<source>Default log path:</source>
+<target>Standard loggbane:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Slett logger etter x dager:</target>
+
<source>Customize context menu:</source>
<target>Tilpass kontekst-meny:</target>
@@ -1324,8 +1357,8 @@ Sikrer prosessen ved alvorlige feil.
<source>&Default</source>
<target>&Standard</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Tilbakemeldinger og forslag er ønsket</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Tilbakemelding og forslag er velkomne:</target>
<source>Home page</source>
<target>Hjemmeside</target>
@@ -1351,8 +1384,8 @@ Sikrer prosessen ved alvorlige feil.
<source>Source code written in C++ using:</source>
<target>Kildekoden er skrevet i C++ med hjelp fra:</target>
-<source>Published under the GNU General Public License</source>
-<target>Utgitt under GNU General Public Licence</target>
+<source>Published under the GNU General Public License:</source>
+<target>Publisert under GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Takk for oversettelse:</target>
@@ -1408,6 +1441,9 @@ Sikrer prosessen ved alvorlige feil.
<source>Info</source>
<target>Info</target>
+<source>No log entries</source>
+<target>Ingen loggfiler</target>
+
<source>Select all</source>
<target>Velg alt</target>
@@ -1432,6 +1468,9 @@ Sikrer prosessen ved alvorlige feil.
<source>Overview</source>
<target>Oversikt</target>
+<source>Swap sides</source>
+<target>Bytt sider</target>
+
<source>Show "%x"</source>
<target>Vis "%x"</target>
@@ -1606,9 +1645,6 @@ Sikrer prosessen ved alvorlige feil.
<source>Show filtered or temporarily excluded files</source>
<target>Vis filtrerte eller midlertidig ekskluderte filer</target>
-<source>Save as default</source>
-<target>Lagre som standard</target>
-
<source>Filter</source>
<target>Filter</target>
@@ -1954,12 +1990,6 @@ Sikrer prosessen ved alvorlige feil.
<source>Checking recycle bin failed for folder %x.</source>
<target>Det å sjekke papirkurven gikk galt for mappen %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>De følgende XML-elementer kunne ikke leses:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Konfigurasjonsfil %x er ufullstendig. De manglende elementene vil bli satt til standardverdiene.</target>
-
<source>Prepare installation</source>
<target>Forbered installering</target>
diff --git a/FreeFileSync/Build/Languages/polish.lng b/FreeFileSync/Build/Languages/polish.lng
index e3dd61a9..30a67068 100755
--- a/FreeFileSync/Build/Languages/polish.lng
+++ b/FreeFileSync/Build/Languages/polish.lng
@@ -161,6 +161,9 @@
<source>Items differ in attributes only</source>
<target>Elementy różnią się wyłącznie atrybutami</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Nazwa %x jest używana przez więcej niż jeden element w folderze.</target>
+
<source>Resolving symbolic link %x</source>
<target>Rozwiązywanie dowiązania symbolicznego %x</target>
@@ -188,9 +191,6 @@
<source>File time tolerance</source>
<target>Tolerancja czasu dla pliku</target>
-<source>Folder access timeout</source>
-<target>Limit czasu odpowiedzi podczas dostępu do katalogu</target>
-
<source>Run with background priority</source>
<target>Uruchom w tle z priorytetem</target>
@@ -336,8 +336,11 @@
<source>Update attributes on right</source>
<target>Aktualizuj atrybuty po prawej stronie</target>
-<source>Warning</source>
-<target>Ostrzeżenie</target>
+<source>Errors:</source>
+<target>Błędy:</target>
+
+<source>Warnings:</source>
+<target>Ostrzeżenia:</target>
<source>Items processed:</source>
<target>Przetworzone elementy:</target>
@@ -348,6 +351,9 @@
<source>Total time:</source>
<target>Całkowity czas:</target>
+<source>Warning</source>
+<target>Ostrzeżenie</target>
+
<source>Stopped</source>
<target>Zatrzymana</target>
@@ -357,6 +363,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Błąd podczas parsowania pliku %x, rząd %y, kolumna %z.</target>
+<source>Services</source>
+<target>Usługi</target>
+
+<source>Show All</source>
+<target>Pokaż Wszystko</target>
+
+<source>Hide Others</source>
+<target>Ukryj Inne</target>
+
+<source>Hide %x</source>
+<target>Ukryj %x</target>
+
+<source>Quit %x</source>
+<target>Zamknij %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Nie można zablokować katalogów dla poniższych folderów:</target>
@@ -373,11 +394,11 @@
<source>Cannot read directory %x.</source>
<target>Nie można odczytać katalogu %x.</target>
-<source>/sec</source>
-<target>/sekundę</target>
+<source>%x/sec</source>
+<target>%x/sek</target>
-<source>%x items/sec</source>
-<target>%x elementów/sek</target>
+<source>%x items</source>
+<target>%x elementów</target>
<source>Show in Explorer</source>
<target>Wyświetl w Eksploratorze</target>
@@ -526,11 +547,11 @@
<source>Generating database...</source>
<target>Generowanie bazy danych...</target>
-<source>Searching for excess file versions:</source>
-<target>Wyszukiwanie nadmiarowych wersji plików:</target>
+<source>Searching for old file versions:</source>
+<target>Wyszukiwanie starych wersji plików:</target>
-<source>Removing excess file versions:</source>
-<target>Usuwanie nadmiarowych wersji plików:</target>
+<source>Removing old file versions:</source>
+<target>Usuwanie starych wersji plików:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Nie można utworzyć znacznika czasu dla wersjonowania:</target>
@@ -588,6 +609,24 @@ Przesłany: %y bajtów
<source>Unable to move %x to the recycle bin.</source>
<target>Nie można przenieść %x do kosza.</target>
+<source>Unable to access %x.</source>
+<target>Brak dostępu do %x.</target>
+
+<source>Authentication completed.</source>
+<target>Uwierzytelnianie zakończone.</target>
+
+<source>Authentication failed.</source>
+<target>Uwierzytelnianie nie powiodło się.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Możesz teraz zamknąć tę stronę i kontynuować FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Serwer zwrócił błąd:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Nie można określić wolnego miejsca na %x.</target>
+
<source>Cannot find %x.</source>
<target>Nie można odnaleźć %x.</target>
@@ -600,18 +639,12 @@ Przesłany: %y bajtów
<source>Cannot delete symbolic link %x.</source>
<target>Nie można usunąć dowiązania symbolicznego %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Nie można określić wolnego miejsca na %x.</target>
-
<source>Incorrect command line:</source>
<target>Niepoprawne polecenie:</target>
<source>The server does not support authentication via %x.</source>
<target>Serwer nie wspiera uwierzytelniania przez %x.</target>
-<source>Unable to access %x.</source>
-<target>Brak dostępu do %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -638,25 +671,6 @@ Przesłany: %y bajtów
<source>Failed to open SFTP channel number %x.</source>
<target>Nie można otworzyć kanału SFTP numer %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 bajt</pluralform>
-<pluralform>%x bajty</pluralform>
-<pluralform>%x bajtów</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Drag && Drop</target>
@@ -770,6 +784,25 @@ Komenda jest wykonywana gdy:
<source>&Retry</source>
<target>&Powtórz</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 bajt</pluralform>
+<pluralform>%x bajty</pluralform>
+<pluralform>%x bajtów</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Wczytywanie...</target>
@@ -916,7 +949,7 @@ Komenda jest wykonywana gdy:
<target>Zapisz w trybie &wsadowym...</target>
<source>Show &log</source>
-<target></target>
+<target>Pokaż &log</target>
<source>Start &comparison</source>
<target>Rozpo&cznij porównywanie</target>
@@ -978,9 +1011,6 @@ Komenda jest wykonywana gdy:
<source>Access online storage</source>
<target>Dostęp do zasobu zdalnego</target>
-<source>Swap sides</source>
-<target>Zamień stronami</target>
-
<source>Close search bar</source>
<target>Zamknij pasek wyszukiwania</target>
@@ -1005,6 +1035,9 @@ Komenda jest wykonywana gdy:
<source>View type:</source>
<target>Typ widoku:</target>
+<source>Save as default</source>
+<target>Zapisz jako domyślne</target>
+
<source>Select view:</source>
<target>Widok:</target>
@@ -1062,6 +1095,15 @@ Komenda jest wykonywana gdy:
<source>Handle daylight saving time</source>
<target>Uwzględniaj przesunięcie czasu</target>
+<source>Ignore errors</source>
+<target>Ignoruj błędy</target>
+
+<source>Retry count:</source>
+<target>Liczba prób:</target>
+
+<source>Delay (in seconds):</source>
+<target>Opóźnienie (w sekundach):</target>
+
<source>Performance improvements:</source>
<target>Polepszenie wydajności:</target>
@@ -1136,17 +1178,11 @@ Komenda jest wykonywana gdy:
<source>Last x days:</source>
<target>Ostatnie x dni:</target>
-<source>Ignore errors</source>
-<target>Ignoruj błędy</target>
-
-<source>Retry count:</source>
-<target>Liczba prób:</target>
-
-<source>Delay (in seconds):</source>
-<target>Opóźnienie (w sekundach):</target>
+<source>&Override default log path:</source>
+<target>&Zastąp domyślną ścieżkę logu:</target>
-<source>Run a command after synchronization:</source>
-<target>Uruchom komendę po zakończonej synchronizacji:</target>
+<source>Run a command:</source>
+<target>Uruchom polecenie:</target>
<source>OK</source>
<target>OK</target>
@@ -1196,6 +1232,9 @@ Komenda jest wykonywana gdy:
<source>Directory on server:</source>
<target>Katalog na serwerze:</target>
+<source>Access timeout (in seconds):</source>
+<target>Czas oczekiwania na dostęp (w sekundach):</target>
+
<source>SFTP channels per connection:</source>
<target>Liczba kanałów dla połączenia SFTP:</target>
@@ -1274,12 +1313,6 @@ Komenda jest wykonywana gdy:
<source>Stop synchronization at first error</source>
<target>Przerwij synchronizację przy pierwszym błędzie</target>
-<source>Save log:</source>
-<target>Zapisz logi:</target>
-
-<source>Limit number of log files:</source>
-<target>Ogranicz liczbę plików logów:</target>
-
<source>How can I schedule a batch job?</source>
<target>Jak zaplanować zadanie w trybie wsadowym?</target>
@@ -1316,8 +1349,11 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Przywróć wszystkie, stale ukryte dialogi i powiadomienia</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Domyślna ścieżka logu:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Skasuj logi po x dniach:</target>
<source>Customize context menu:</source>
<target>Dostosuj menu kontekstowe:</target>
@@ -1328,11 +1364,11 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>&Default</source>
<target>&Domyślne</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Wszelkie opinie i sugestie mile widziane</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Opinie i sugestie mile widziane:</target>
<source>Home page</source>
-<target>Strona domowa:</target>
+<target>Strona domowa</target>
<source>FreeFileSync Forum</source>
<target>Forum FreeFileSync</target>
@@ -1355,8 +1391,8 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Source code written in C++ using:</source>
<target>Kod stworzony w C++ z wykorzystaniem:</target>
-<source>Published under the GNU General Public License</source>
-<target>Udostępnione na zasadach licencji GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Opublikowano na licencji GNU General Public Licence:</target>
<source>Many thanks for localization:</source>
<target>Podziękowania za tłumaczenia:</target>
@@ -1413,7 +1449,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Brak wpisów w dzienniku</target>
<source>Select all</source>
<target>Zaznacz wszystko</target>
@@ -1439,6 +1475,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Overview</source>
<target>Przegląd</target>
+<source>Swap sides</source>
+<target>Zamień stronami</target>
+
<source>Show "%x"</source>
<target>Pokaż "%x"</target>
@@ -1617,9 +1656,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Show filtered or temporarily excluded files</source>
<target>Pokaż pliki wyfiltrowane lub wykluczone tymczasowo</target>
-<source>Save as default</source>
-<target>Zapisz jako domyślne</target>
-
<source>Filter</source>
<target>Filtr</target>
@@ -1970,12 +2006,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp
<source>Checking recycle bin failed for folder %x.</source>
<target>Sprawdzanie kosza systemowego dla katalogu %x zakończone niepowodzeniem.</target>
-<source>The following XML elements could not be read:</source>
-<target>Poniższy element XML nie może zostać odczytany:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Plik konfiguracyjny %x jest niekompletny. Zostaną ustawione domyślne wartości dla brakujących elementów.</target>
-
<source>Prepare installation</source>
<target>Przygotuj instalację</target>
diff --git a/FreeFileSync/Build/Languages/portuguese.lng b/FreeFileSync/Build/Languages/portuguese.lng
index 83c0ad21..b713f6f1 100755
--- a/FreeFileSync/Build/Languages/portuguese.lng
+++ b/FreeFileSync/Build/Languages/portuguese.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Itens diferem apenas nos atributos</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>O nome %x é usado por mais de um item na pasta.</target>
+
<source>Resolving symbolic link %x</source>
<target>Resolver link simbólico %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Tolerância de tempo de ficheiro</target>
-<source>Folder access timeout</source>
-<target>Tempo limite de acesso à pasta</target>
-
<source>Run with background priority</source>
<target>Correr com prioridade de fundo</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Actualizar atributos à direita</target>
-<source>Warning</source>
-<target>Atenção</target>
+<source>Errors:</source>
+<target>Erros:</target>
+
+<source>Warnings:</source>
+<target>Avisos:</target>
<source>Items processed:</source>
<target>Elementos processados:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Tempo total:</target>
+<source>Warning</source>
+<target>Atenção</target>
+
<source>Stopped</source>
<target>Parado</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Erro ao analisar ficheiro %x, linha %y, coluna %z.</target>
+<source>Services</source>
+<target>Serviços</target>
+
+<source>Show All</source>
+<target>Mostrar Todos</target>
+
+<source>Hide Others</source>
+<target>Ocultar Outros</target>
+
+<source>Hide %x</source>
+<target>Ocultar %x</target>
+
+<source>Quit %x</source>
+<target>Cerrar o %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Incapaz de definir bloqueios de directórios para as seguintes pastas:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Não é possível ler o directório %x.</target>
-<source>/sec</source>
-<target>/seg</target>
+<source>%x/sec</source>
+<target>%x/seg.</target>
-<source>%x items/sec</source>
-<target>%x itens/seg</target>
+<source>%x items</source>
+<target>%x itens</target>
<source>Show in Explorer</source>
<target>Mostrar no Explorer</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>A gerar base de dados...</target>
-<source>Searching for excess file versions:</source>
-<target>A pesquisar por versões em excesso de ficheiro:</target>
+<source>Searching for old file versions:</source>
+<target>A pesquisar por versões antigas do ficheiro:</target>
-<source>Removing excess file versions:</source>
-<target>A remover versões em excesso de ficheiro:</target>
+<source>Removing old file versions:</source>
+<target>A remover versões antigas do ficheiro:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Não é possível criar data/hora para controlo de versões:</target>
@@ -585,6 +606,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Não é possível mover %x para a reciclagem.</target>
+<source>Unable to access %x.</source>
+<target>Incapaz de aceder %x.</target>
+
+<source>Authentication completed.</source>
+<target>Autenticação completada.</target>
+
+<source>Authentication failed.</source>
+<target>Falha na autenticação.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Você pode cerrar esta página agora e continuar com o FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>O servidor retornou um erro:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Não é possível determinar o espaço livre no disco %x.</target>
+
<source>Cannot find %x.</source>
<target>Não é possível encontrar %x.</target>
@@ -597,18 +636,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Não é possível remover o link simbólico %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Não é possível determinar o espaço livre no disco %x.</target>
-
<source>Incorrect command line:</source>
<target>Linha de comandos incorrecta:</target>
<source>The server does not support authentication via %x.</source>
<target>O servidor não suporta autenticação via %x.</target>
-<source>Unable to access %x.</source>
-<target>Incapaz de aceder %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Falha ao abrir canal SFTP número %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Arrastar && Largar</target>
@@ -764,6 +779,24 @@ O comando é executado se:
<source>&Retry</source>
<target>&Tentar de Novo</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>A carregar...</target>
@@ -909,7 +942,7 @@ O comando é executado se:
<target>Guardar como &batch...</target>
<source>Show &log</source>
-<target></target>
+<target>Mostrar &registo</target>
<source>Start &comparison</source>
<target>Iniciar &comparação</target>
@@ -971,9 +1004,6 @@ O comando é executado se:
<source>Access online storage</source>
<target>Aceder armazenagem local</target>
-<source>Swap sides</source>
-<target>Trocar lados</target>
-
<source>Close search bar</source>
<target>Fechar barra de pesquisa</target>
@@ -998,6 +1028,9 @@ O comando é executado se:
<source>View type:</source>
<target>Tipo de vista:</target>
+<source>Save as default</source>
+<target>Guardar como padrão</target>
+
<source>Select view:</source>
<target>Seleccionar vista:</target>
@@ -1055,6 +1088,15 @@ O comando é executado se:
<source>Handle daylight saving time</source>
<target>Lidar com horário de verão</target>
+<source>Ignore errors</source>
+<target>Ignorar erros</target>
+
+<source>Retry count:</source>
+<target>Nº de tentativas:</target>
+
+<source>Delay (in seconds):</source>
+<target>Atraso (em segundos):</target>
+
<source>Performance improvements:</source>
<target>Melhorias de desempenho:</target>
@@ -1129,17 +1171,11 @@ O comando é executado se:
<source>Last x days:</source>
<target>Últimos x dias:</target>
-<source>Ignore errors</source>
-<target>Ignorar erros</target>
-
-<source>Retry count:</source>
-<target>Nº de tentativas:</target>
-
-<source>Delay (in seconds):</source>
-<target>Atraso (em segundos):</target>
+<source>&Override default log path:</source>
+<target>&Substituir caminho padrão do registo:</target>
-<source>Run a command after synchronization:</source>
-<target>Executar um comando após a sincronização:</target>
+<source>Run a command:</source>
+<target>Executar um comando:</target>
<source>OK</source>
<target>OK</target>
@@ -1189,6 +1225,9 @@ O comando é executado se:
<source>Directory on server:</source>
<target>Directório no servidor:</target>
+<source>Access timeout (in seconds):</source>
+<target>Tempo limite de acesso (em segundos):</target>
+
<source>SFTP channels per connection:</source>
<target>Canais SFTP por conexão:</target>
@@ -1267,12 +1306,6 @@ O comando é executado se:
<source>Stop synchronization at first error</source>
<target>Para sincronização ao primeiro erro</target>
-<source>Save log:</source>
-<target>Guardar registo:</target>
-
-<source>Limit number of log files:</source>
-<target>Limitar número de ficheiros de registo:</target>
-
<source>How can I schedule a batch job?</source>
<target>Como posso agendar um trabalho batch?</target>
@@ -1309,8 +1342,11 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Mostrar todos os diálogos escondidos permanentemente e mensagens de aviso novamente</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Caminho padrão do registo:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Eliminar registos após X dias:</target>
<source>Customize context menu:</source>
<target>Personalizar menu de contexto:</target>
@@ -1321,8 +1357,8 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>&Default</source>
<target>&Config. Iniciais</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Comentários e sugestões são apreciados</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Comentários e sugestões são bem-vindos:</target>
<source>Home page</source>
<target>Sítio da web</target>
@@ -1348,8 +1384,8 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Source code written in C++ using:</source>
<target>Código fonte escrito em C++ utilizando:</target>
-<source>Published under the GNU General Public License</source>
-<target>Publicado sobre GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Publicado sob a Licença Pública Geral GNU:</target>
<source>Many thanks for localization:</source>
<target>Muito obrigado pela localização:</target>
@@ -1406,7 +1442,7 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Nenhuma entrada no registo</target>
<source>Select all</source>
<target>Seleccionar tudo</target>
@@ -1432,6 +1468,9 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Overview</source>
<target>Vista</target>
+<source>Swap sides</source>
+<target>Trocar lados</target>
+
<source>Show "%x"</source>
<target>Mostrar "%x"</target>
@@ -1606,9 +1645,6 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Show filtered or temporarily excluded files</source>
<target>Mostrar ficheiros filtrados ou temporariamente excluídos</target>
-<source>Save as default</source>
-<target>Guardar como padrão</target>
-
<source>Filter</source>
<target>Filtro</target>
@@ -1954,12 +1990,6 @@ Isto garante um estado consistente mesmo em caso de falha grave.
<source>Checking recycle bin failed for folder %x.</source>
<target>Verificar a reciclagem falhou no directório %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Os seguintes elementos XML não puderam ser lidos:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>O ficheiro de instalação %x está incompleto. Os elementos em falta serão definidos para seus valores iniciais.</target>
-
<source>Prepare installation</source>
<target>A preparar a instalação</target>
diff --git a/FreeFileSync/Build/Languages/portuguese_br.lng b/FreeFileSync/Build/Languages/portuguese_br.lng
index 9b336566..43c0904c 100755
--- a/FreeFileSync/Build/Languages/portuguese_br.lng
+++ b/FreeFileSync/Build/Languages/portuguese_br.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Os itens diferem apenas nos atributos</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>O nome %x é usado por mais de um item na pasta.</target>
+
<source>Resolving symbolic link %x</source>
<target>Resolvendo link simbólico %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Tolerância de tempo do arquivo</target>
-<source>Folder access timeout</source>
-<target>Tempo limite de acesso da pasta</target>
-
<source>Run with background priority</source>
<target>Executar com prioridade de segundo plano</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Atualizar atributos à direita</target>
-<source>Warning</source>
-<target>Aviso</target>
+<source>Errors:</source>
+<target>Erros:</target>
+
+<source>Warnings:</source>
+<target>Avisos:</target>
<source>Items processed:</source>
<target>Elementos processados:</target>
@@ -346,8 +349,11 @@
<source>Total time:</source>
<target>Tempo total:</target>
+<source>Warning</source>
+<target>Aviso</target>
+
<source>Stopped</source>
-<target>Interrompido</target>
+<target>Parado</target>
<source>Cleaning up log files:</source>
<target>Limpando arquivos de log:</target>
@@ -355,8 +361,23 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Erro analisando o arquivo %x, linha %y, coluna %z.</target>
+<source>Services</source>
+<target>Serviços</target>
+
+<source>Show All</source>
+<target>Mostrar Todos</target>
+
+<source>Hide Others</source>
+<target>Ocultar Outros</target>
+
+<source>Hide %x</source>
+<target>Ocultar %x</target>
+
+<source>Quit %x</source>
+<target>Sair %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
-<target>Não é possível estabelecer bloqueio de diretório para as seguintes pastas:</target>
+<target>Não é possível definir bloqueios de diretório para as seguintes pastas:</target>
<source>
<pluralform>1 thread</pluralform>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Não é possível ler o diretório %x.</target>
-<source>/sec</source>
-<target>/s</target>
+<source>%x/sec</source>
+<target>%x/s</target>
-<source>%x items/sec</source>
-<target>%x itens/s</target>
+<source>%x items</source>
+<target>%x itens</target>
<source>Show in Explorer</source>
<target>Mostrar no Explorer</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Gerando banco de dados...</target>
-<source>Searching for excess file versions:</source>
-<target>Procurando por versões de arquivos em excesso:</target>
+<source>Searching for old file versions:</source>
+<target>Procurando por versões antigas de arquivos:</target>
-<source>Removing excess file versions:</source>
-<target>Removendo versões de arquivos em excesso:</target>
+<source>Removing old file versions:</source>
+<target>Removendo versões antigas de arquivos:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Não é possível criar a estampa de tempo para o controle de versões:</target>
@@ -585,6 +606,24 @@ Atual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Não é possível mover %x para a Lixeira.</target>
+<source>Unable to access %x.</source>
+<target>Não foi possível acessar %x.</target>
+
+<source>Authentication completed.</source>
+<target>Autenticação concluída.</target>
+
+<source>Authentication failed.</source>
+<target>Autenticação falhou.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Você pode fechar esta página agora e continuar com o FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>O servidor retornou um erro:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Não é possível determinar o espaço livre em disco para %x.</target>
+
<source>Cannot find %x.</source>
<target>Não é possível encontrar %x.</target>
@@ -597,18 +636,12 @@ Atual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Não é possível excluir o link simbólico %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Não é possível determinar o espaço livre em disco para %x.</target>
-
<source>Incorrect command line:</source>
<target>Linha de comando incorreta:</target>
<source>The server does not support authentication via %x.</source>
<target>O servidor não suporta autenticação via %x.</target>
-<source>Unable to access %x.</source>
-<target>Não foi possível acessar %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Atual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Falha ao abrir o canal SFTP número %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x kB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Arrastar && Soltar</target>
@@ -764,6 +779,24 @@ O comando é disparado se:
<source>&Retry</source>
<target>&Tentar Novamente</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x kB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Carregando...</target>
@@ -909,7 +942,7 @@ O comando é disparado se:
<target>Salvar como &tarefa em lotes...</target>
<source>Show &log</source>
-<target></target>
+<target>Mostrar &log</target>
<source>Start &comparison</source>
<target>Iniciar &Comparação</target>
@@ -971,9 +1004,6 @@ O comando é disparado se:
<source>Access online storage</source>
<target>Acessar armazenamento online</target>
-<source>Swap sides</source>
-<target>Trocar lados</target>
-
<source>Close search bar</source>
<target>Fechar barra de localização</target>
@@ -998,6 +1028,9 @@ O comando é disparado se:
<source>View type:</source>
<target>Tipo de visualização:</target>
+<source>Save as default</source>
+<target>Salvar como padrão</target>
+
<source>Select view:</source>
<target>Selecionar visualização:</target>
@@ -1055,6 +1088,15 @@ O comando é disparado se:
<source>Handle daylight saving time</source>
<target>Como lidar com horário de verão</target>
+<source>Ignore errors</source>
+<target>Ignorar erros</target>
+
+<source>Retry count:</source>
+<target>Número de tentativas:</target>
+
+<source>Delay (in seconds):</source>
+<target>Atraso (em segundos):</target>
+
<source>Performance improvements:</source>
<target>Melhorias de desempenho:</target>
@@ -1129,17 +1171,11 @@ O comando é disparado se:
<source>Last x days:</source>
<target>Últimos x dias:</target>
-<source>Ignore errors</source>
-<target>Ignorar erros</target>
-
-<source>Retry count:</source>
-<target>Número de tentativas:</target>
-
-<source>Delay (in seconds):</source>
-<target>Atraso (em segundos):</target>
+<source>&Override default log path:</source>
+<target>&Substituir caminho de log padrão:</target>
-<source>Run a command after synchronization:</source>
-<target>Executar um comando após a sincronização:</target>
+<source>Run a command:</source>
+<target>Executar um comando:</target>
<source>OK</source>
<target>OK</target>
@@ -1189,6 +1225,9 @@ O comando é disparado se:
<source>Directory on server:</source>
<target>Diretório no servidor:</target>
+<source>Access timeout (in seconds):</source>
+<target>Tempo limite de acesso (em segundos):</target>
+
<source>SFTP channels per connection:</source>
<target>Canais SFTP por conexão:</target>
@@ -1211,7 +1250,7 @@ O comando é disparado se:
<target>&Não mostrar esta caixa de diálogo novamente</target>
<source>Items found:</source>
-<target>Elementos encontrados:</target>
+<target>Itens encontrados:</target>
<source>Time remaining:</source>
<target>Tempo restante:</target>
@@ -1267,12 +1306,6 @@ O comando é disparado se:
<source>Stop synchronization at first error</source>
<target>Interromper a sincronização ao primeiro erro</target>
-<source>Save log:</source>
-<target>Salvar arquivo de log:</target>
-
-<source>Limit number of log files:</source>
-<target>Limitar número de arquivos de log:</target>
-
<source>How can I schedule a batch job?</source>
<target>Como posso agendar uma tarefa em lotes?</target>
@@ -1309,8 +1342,11 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Mostrar todas as caixas de diálogo e as mensagens de aviso permanentemente ocultadas</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Caminho de log padrão:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Excluir logs após x days:</target>
<source>Customize context menu:</source>
<target>Personalizar menu de contexto:</target>
@@ -1321,8 +1357,8 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>&Default</source>
<target>&Config. Padrão</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Críticas e sugestões são bem-vindas</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Comentários e sugestões são bem-vindos:</target>
<source>Home page</source>
<target>Página web</target>
@@ -1348,8 +1384,8 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Source code written in C++ using:</source>
<target>Código-fonte escrito em C++ utilizando:</target>
-<source>Published under the GNU General Public License</source>
-<target>Publicado sob a GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Publicado sob a Licença Pública Geral GNU:</target>
<source>Many thanks for localization:</source>
<target>Pela tradução, um agradecimento a:</target>
@@ -1406,7 +1442,7 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<target>Informações</target>
<source>No log entries</source>
-<target></target>
+<target>Nenhuma entrada de log</target>
<source>Select all</source>
<target>Selecionar todos</target>
@@ -1432,6 +1468,9 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Overview</source>
<target>Parâmetros</target>
+<source>Swap sides</source>
+<target>Trocar lados</target>
+
<source>Show "%x"</source>
<target>Mostrar "%x"</target>
@@ -1606,9 +1645,6 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Show filtered or temporarily excluded files</source>
<target>Mostrar arquivos que foram filtrados ou excluídos temporariamente</target>
-<source>Save as default</source>
-<target>Salvar como padrão</target>
-
<source>Filter</source>
<target>Filtro</target>
@@ -1954,12 +1990,6 @@ Isto garante um estado consistente mesmo em caso de erro grave.
<source>Checking recycle bin failed for folder %x.</source>
<target>Verificando falha na Lixeira para pasta %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Os seguintes elementos XML não puderam ser lidos:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Arquivo de configuração %x incompleto. Os elementos faltantes serão configurados com seus valores padrões.</target>
-
<source>Prepare installation</source>
<target>Preparando a instalação</target>
diff --git a/FreeFileSync/Build/Languages/romanian.lng b/FreeFileSync/Build/Languages/romanian.lng
index 7a970005..52a59d90 100755
--- a/FreeFileSync/Build/Languages/romanian.lng
+++ b/FreeFileSync/Build/Languages/romanian.lng
@@ -161,6 +161,9 @@
<source>Items differ in attributes only</source>
<target>Elementele diferă doar prin atributele lor</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Numele %x e folosit de mai multe elemente din dosar.</target>
+
<source>Resolving symbolic link %x</source>
<target>Rezolv legătura simbolică %x</target>
@@ -188,9 +191,6 @@
<source>File time tolerance</source>
<target>Toleranța timpului filei</target>
-<source>Folder access timeout</source>
-<target>Expirarea accesului la dosar</target>
-
<source>Run with background priority</source>
<target>Rulează cu prioritate de fundal [background]</target>
@@ -336,8 +336,11 @@
<source>Update attributes on right</source>
<target>Actualizează atributele în partea dreaptă</target>
-<source>Warning</source>
-<target>Atenție</target>
+<source>Errors:</source>
+<target>Erori:</target>
+
+<source>Warnings:</source>
+<target>Avertizări:</target>
<source>Items processed:</source>
<target>Elemente Procesate:</target>
@@ -348,6 +351,9 @@
<source>Total time:</source>
<target>Timp Total:</target>
+<source>Warning</source>
+<target>Atenție</target>
+
<source>Stopped</source>
<target>Oprită</target>
@@ -357,6 +363,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Eroare la parsarea filei %x, rîndul %y, coloana %z.</target>
+<source>Services</source>
+<target>Servicii</target>
+
+<source>Show All</source>
+<target>Arată Tot</target>
+
+<source>Hide Others</source>
+<target>Ascunde Restul</target>
+
+<source>Hide %x</source>
+<target>Ascunde %x</target>
+
+<source>Quit %x</source>
+<target>Închide %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Nu pot seta zăvorîrea [lock] pentru dosarele următoare:</target>
@@ -373,11 +394,11 @@
<source>Cannot read directory %x.</source>
<target>Nu pot citi dosarul %x.</target>
-<source>/sec</source>
-<target>/sec</target>
+<source>%x/sec</source>
+<target>%x/sec</target>
-<source>%x items/sec</source>
-<target>%x elemente/sec</target>
+<source>%x items</source>
+<target>%x elemente</target>
<source>Show in Explorer</source>
<target>Arată în Exploratorul de File</target>
@@ -526,11 +547,11 @@
<source>Generating database...</source>
<target>Generez baza de date...</target>
-<source>Searching for excess file versions:</source>
-<target>Caut versiunile în exces ale filelor:</target>
+<source>Searching for old file versions:</source>
+<target>Caut versiuni mai vechi ale filelor:</target>
-<source>Removing excess file versions:</source>
-<target>Înlătur versiunile în exces ale filelor:</target>
+<source>Removing old file versions:</source>
+<target>Înlătur versiunile vechi ale filelor:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Nu pot crea marcajul de timp pentru versionare:</target>
@@ -588,6 +609,24 @@ Actuală: %y baiți
<source>Unable to move %x to the recycle bin.</source>
<target>Nu pot muta %x în Reciclator.</target>
+<source>Unable to access %x.</source>
+<target>Nu pot accesa %x.</target>
+
+<source>Authentication completed.</source>
+<target>Autentificare realizată.</target>
+
+<source>Authentication failed.</source>
+<target>Autentificare eșuată.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Poți închide pagina asta acum și continua cu FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Serverul a returnat o eroare:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Nu pot determina spațiul liber de stocare pentru %x.</target>
+
<source>Cannot find %x.</source>
<target>Nu pot găsi %x.</target>
@@ -600,18 +639,12 @@ Actuală: %y baiți
<source>Cannot delete symbolic link %x.</source>
<target>Nu pot șterge legătura simbolică %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Nu pot determina spațiul liber de stocare pentru %x.</target>
-
<source>Incorrect command line:</source>
<target>Linie de comandă incorectă:</target>
<source>The server does not support authentication via %x.</source>
<target>Serverul nu suportă autentificarea prin %x.</target>
-<source>Unable to access %x.</source>
-<target>Nu pot accesa %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -638,25 +671,6 @@ Actuală: %y baiți
<source>Failed to open SFTP channel number %x.</source>
<target>N-am putut deschide canalul SFTP cu numărul %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 bait</pluralform>
-<pluralform>%x baiți</pluralform>
-<pluralform>%x de baiți</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Trage și pune un dosar mai jos sau explorează către el</target>
@@ -770,6 +784,25 @@ Comanda este declanșată dacă:
<source>&Retry</source>
<target>&Reîncearcă</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 bait</pluralform>
+<pluralform>%x baiți</pluralform>
+<pluralform>%x de baiți</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Deschid...</target>
@@ -916,7 +949,7 @@ Comanda este declanșată dacă:
<target>Salvea&ză ca Sarcină Set...</target>
<source>Show &log</source>
-<target></target>
+<target>Arată &Jurnalul</target>
<source>Start &comparison</source>
<target>Pornește &Compararea</target>
@@ -978,9 +1011,6 @@ Comanda este declanșată dacă:
<source>Access online storage</source>
<target>Accesează stocarea de pe internet [online]</target>
-<source>Swap sides</source>
-<target>Schimbă compartimentele stîng și drept între ele</target>
-
<source>Close search bar</source>
<target>Închide bara de căutare</target>
@@ -1005,6 +1035,9 @@ Comanda este declanșată dacă:
<source>View type:</source>
<target>Tipul Vederii:</target>
+<source>Save as default</source>
+<target>Salvează ca implicit</target>
+
<source>Select view:</source>
<target>Selectează Vederea:</target>
@@ -1062,6 +1095,15 @@ Comanda este declanșată dacă:
<source>Handle daylight saving time</source>
<target>Gestionarea timpului de vară (DST)</target>
+<source>Ignore errors</source>
+<target>Ignoră erorile</target>
+
+<source>Retry count:</source>
+<target>Numărul reîncercărilor:</target>
+
+<source>Delay (in seconds):</source>
+<target>Întîrziere (în secunde):</target>
+
<source>Performance improvements:</source>
<target>Îmbunătățiri de Performanță:</target>
@@ -1136,17 +1178,11 @@ Comanda este declanșată dacă:
<source>Last x days:</source>
<target>Ultimele x zile:</target>
-<source>Ignore errors</source>
-<target>Ignoră erorile</target>
-
-<source>Retry count:</source>
-<target>Numărul reîncercărilor:</target>
-
-<source>Delay (in seconds):</source>
-<target>Întîrziere (în secunde):</target>
+<source>&Override default log path:</source>
+<target>Ign&oră calea implicită a jurnalului:</target>
-<source>Run a command after synchronization:</source>
-<target>Rulează o comandă după sincronizare:</target>
+<source>Run a command:</source>
+<target>Rulează o comandă:</target>
<source>OK</source>
<target>OK</target>
@@ -1196,6 +1232,9 @@ Comanda este declanșată dacă:
<source>Directory on server:</source>
<target>Dosarul de pe server:</target>
+<source>Access timeout (in seconds):</source>
+<target>Timp de expirare a accesului (în sec.):</target>
+
<source>SFTP channels per connection:</source>
<target>Canale SFTP per conexiune:</target>
@@ -1274,12 +1313,6 @@ Comanda este declanșată dacă:
<source>Stop synchronization at first error</source>
<target>Oprește sincronizarea la prima eroare întîlnită</target>
-<source>Save log:</source>
-<target>Salvează jurnalul:</target>
-
-<source>Limit number of log files:</source>
-<target>Limitează numărul filelor de jurnalizare:</target>
-
<source>How can I schedule a batch job?</source>
<target>Cum pot planifica o sarcină set?</target>
@@ -1316,8 +1349,11 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Sînt arătate din nou dialogurile și mesajele de eroare care au fost ascunse permanent</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Calea implicită a jurnalului:</target>
+
+<source>&Delete logs after x days:</source>
+<target>Șter&ge jurnalul după x zile:</target>
<source>Customize context menu:</source>
<target>Personalizează meniul contextual:</target>
@@ -1328,8 +1364,8 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>&Default</source>
<target>Coloanele &Implicite</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Opiniile și sugestiile despre program sînt binevenite.</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Părerile și sugestiile sînt binevenite:</target>
<source>Home page</source>
<target>Pagina Sitului</target>
@@ -1355,8 +1391,8 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Source code written in C++ using:</source>
<target>Cod sursă scris în C++ folosind:</target>
-<source>Published under the GNU General Public License</source>
-<target>Publicat sub licența GNU GPL</target>
+<source>Published under the GNU General Public License:</source>
+<target>Publicat sub Licența Publică Generală GNU:</target>
<source>Many thanks for localization:</source>
<target>Multe mulțumiri pentru localizare:</target>
@@ -1413,7 +1449,7 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<target>Informații</target>
<source>No log entries</source>
-<target></target>
+<target>Nu există intrări în jurnal</target>
<source>Select all</source>
<target>Selectează Tot</target>
@@ -1439,6 +1475,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Overview</source>
<target>Panoramă</target>
+<source>Swap sides</source>
+<target>Schimbă compartimentele stîng și drept între ele</target>
+
<source>Show "%x"</source>
<target>Arată "%x"</target>
@@ -1617,9 +1656,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Show filtered or temporarily excluded files</source>
<target>Arată filele filtrate sau excluse temporar</target>
-<source>Save as default</source>
-<target>Salvează ca implicit</target>
-
<source>Filter</source>
<target>Filtrare</target>
@@ -1970,12 +2006,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției
<source>Checking recycle bin failed for folder %x.</source>
<target>Verificarea disponibilității Reciclatorului a eșuat pentru dosarul %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Elementele XML următoare nu pot fi citite:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Fila configurație %x este incompletă. Elementele lipsă vor fi setate la valorile implicite.</target>
-
<source>Prepare installation</source>
<target>Pregătirea Instalării</target>
diff --git a/FreeFileSync/Build/Languages/russian.lng b/FreeFileSync/Build/Languages/russian.lng
index 17566e71..d6777279 100755
--- a/FreeFileSync/Build/Languages/russian.lng
+++ b/FreeFileSync/Build/Languages/russian.lng
@@ -161,6 +161,9 @@
<source>Items differ in attributes only</source>
<target>Элементы различаются только атрибутами</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Имя %x используется более чем одним элементом в папке.</target>
+
<source>Resolving symbolic link %x</source>
<target>Разрешение символьной ссылки %x</target>
@@ -188,9 +191,6 @@
<source>File time tolerance</source>
<target>Допускаемая разница во времени изменения файла</target>
-<source>Folder access timeout</source>
-<target>Тайм-аут доступа к папкам</target>
-
<source>Run with background priority</source>
<target>Запустить с фоновым приоритетом</target>
@@ -336,8 +336,11 @@
<source>Update attributes on right</source>
<target>Обновление атрибутов справа</target>
-<source>Warning</source>
-<target>Внимание</target>
+<source>Errors:</source>
+<target>Ошибки:</target>
+
+<source>Warnings:</source>
+<target>Предупеждения:</target>
<source>Items processed:</source>
<target>Элементов обработано:</target>
@@ -348,15 +351,33 @@
<source>Total time:</source>
<target>Общее время:</target>
+<source>Warning</source>
+<target>Внимание</target>
+
<source>Stopped</source>
<target>Остановлено</target>
<source>Cleaning up log files:</source>
-<target>Очистка лог-файлов (журналов):</target>
+<target>Очистка файлов журнала:</target>
<source>Error parsing file %x, row %y, column %z.</source>
<target>Ошибка при разборе файла %x, строка %y, колонка %z.</target>
+<source>Services</source>
+<target>Службы</target>
+
+<source>Show All</source>
+<target>Показать все</target>
+
+<source>Hide Others</source>
+<target>Скрыть остальные</target>
+
+<source>Hide %x</source>
+<target>Скрыть %x</target>
+
+<source>Quit %x</source>
+<target>Выйти из %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Невозможно установить блокировки для следующих папок:</target>
@@ -373,11 +394,11 @@
<source>Cannot read directory %x.</source>
<target>Невозможно прочитать папку %x.</target>
-<source>/sec</source>
-<target>/с</target>
+<source>%x/sec</source>
+<target>%x/с</target>
-<source>%x items/sec</source>
-<target>%x элементов/с</target>
+<source>%x items</source>
+<target>%x элемент(ов)</target>
<source>Show in Explorer</source>
<target>Показать в Проводнике</target>
@@ -526,11 +547,11 @@
<source>Generating database...</source>
<target>Создание базы данных...</target>
-<source>Searching for excess file versions:</source>
-<target>Поиск лишних версий файлов:</target>
+<source>Searching for old file versions:</source>
+<target>Поиск старых версий файлов:</target>
-<source>Removing excess file versions:</source>
-<target>Удаление лишних версий файлов:</target>
+<source>Removing old file versions:</source>
+<target>Удаление старых версий файлов:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Невозможно создать метку времени для архивирования файлов:</target>
@@ -588,6 +609,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Невозможно переместить %x в "Корзину".</target>
+<source>Unable to access %x.</source>
+<target>Невозможно получить доступ к %x.</target>
+
+<source>Authentication completed.</source>
+<target>Аутентификация завершена.</target>
+
+<source>Authentication failed.</source>
+<target>Ошибка аутентификации.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Вы можете закрыть эту страницу сейчас и продолжить с FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Сервер возвратил ошибку:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Невозможно определить свободное место на диске для %x.</target>
+
<source>Cannot find %x.</source>
<target>Невозможно найти %x.</target>
@@ -600,18 +639,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Невозможно удалить символьную ссылку %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Невозможно определить свободное место на диске для %x.</target>
-
<source>Incorrect command line:</source>
<target>Неверная командная строка:</target>
<source>The server does not support authentication via %x.</source>
<target>Сервер не поддерживает аутентификацию с помощью %x.</target>
-<source>Unable to access %x.</source>
-<target>Невозможно получить доступ к %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -638,25 +671,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Не удалось открыть SFTP канал номер %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x байт</pluralform>
-<pluralform>%x байта</pluralform>
-<pluralform>%x байт</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x МБ</target>
-
-<source>%x KB</source>
-<target>%x КБ</target>
-
-<source>%x GB</source>
-<target>%x ГБ</target>
-
<source>Drag && drop</source>
<target>Drag && drop</target>
@@ -770,6 +784,25 @@ The command is triggered if:
<source>&Retry</source>
<target>&Повторить</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x байт</pluralform>
+<pluralform>%x байта</pluralform>
+<pluralform>%x байт</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x МБ</target>
+
+<source>%x KB</source>
+<target>%x КБ</target>
+
+<source>%x GB</source>
+<target>%x ГБ</target>
+
<source>Loading...</source>
<target>Загрузка...</target>
@@ -838,7 +871,7 @@ The command is triggered if:
<target>Последняя синхронизация</target>
<source>Log</source>
-<target>Лог (журнал)</target>
+<target>Журнал</target>
<source>Folder</source>
<target>Папка</target>
@@ -916,7 +949,7 @@ The command is triggered if:
<target>Сохранить как пакетное &задание...</target>
<source>Show &log</source>
-<target></target>
+<target>Показать &журнал</target>
<source>Start &comparison</source>
<target>Начать с&равнение</target>
@@ -978,9 +1011,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>Доступ к онлайн-хранилищу</target>
-<source>Swap sides</source>
-<target>Поменять направление</target>
-
<source>Close search bar</source>
<target>Закрыть строку поиска</target>
@@ -1005,6 +1035,9 @@ The command is triggered if:
<source>View type:</source>
<target>Тип просмотра:</target>
+<source>Save as default</source>
+<target>Сохранить по умолчанию</target>
+
<source>Select view:</source>
<target>Выберите отображение:</target>
@@ -1062,6 +1095,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Ручной переход на летнее время</target>
+<source>Ignore errors</source>
+<target>Игнорировать ошибки</target>
+
+<source>Retry count:</source>
+<target>Число повторений:</target>
+
+<source>Delay (in seconds):</source>
+<target>Задержка (в секундах):</target>
+
<source>Performance improvements:</source>
<target>Повышение производительности:</target>
@@ -1136,17 +1178,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>Последние x дней:</target>
-<source>Ignore errors</source>
-<target>Игнорировать ошибки</target>
-
-<source>Retry count:</source>
-<target>Число повторений:</target>
-
-<source>Delay (in seconds):</source>
-<target>Задержка (в секундах):</target>
+<source>&Override default log path:</source>
+<target>&Изменить путь журнала по умолчанию:</target>
-<source>Run a command after synchronization:</source>
-<target>Запустить команду после синхронизации:</target>
+<source>Run a command:</source>
+<target>Выполнить команду:</target>
<source>OK</source>
<target>OK</target>
@@ -1196,6 +1232,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Папка на сервере:</target>
+<source>Access timeout (in seconds):</source>
+<target>Тайм-аут доступа (в секундах):</target>
+
<source>SFTP channels per connection:</source>
<target>Количество SFTP каналов на одно соединение:</target>
@@ -1274,12 +1313,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>Остановить синхронизацию при первой ошибке</target>
-<source>Save log:</source>
-<target>Сохранять лог-файлы (журналы):</target>
-
-<source>Limit number of log files:</source>
-<target>Ограничить количество лог-файлов (журналов):</target>
-
<source>How can I schedule a batch job?</source>
<target>Как запланировать пакетное задание?</target>
@@ -1316,8 +1349,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Показать все скрытые окна и сообщения с предупреждениями снова</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Путь журнала по умолчанию:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Удаление журналов через x дней:</target>
<source>Customize context menu:</source>
<target>Кастомизация контекстного меню:</target>
@@ -1328,8 +1364,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&По умолчанию</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Замечания и предложения приветствуются</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Обратная связь и предложения приветствуются:</target>
<source>Home page</source>
<target>Домашняя страница</target>
@@ -1355,8 +1391,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>Исходный код написан на C++ с использованием:</target>
-<source>Published under the GNU General Public License</source>
-<target>Издается под лицензией GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Опубликовано в соответствии с GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Большое спасибо за перевод:</target>
@@ -1413,7 +1449,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Информация</target>
<source>No log entries</source>
-<target></target>
+<target>Нет записей в журнале</target>
<source>Select all</source>
<target>Выделить все</target>
@@ -1439,6 +1475,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>Главная</target>
+<source>Swap sides</source>
+<target>Поменять направление</target>
+
<source>Show "%x"</source>
<target>Показать "%x"</target>
@@ -1617,9 +1656,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>Показать отфильтрованные или временно исключенные файлы</target>
-<source>Save as default</source>
-<target>Сохранить по умолчанию</target>
-
<source>Filter</source>
<target>Фильтр</target>
@@ -1970,12 +2006,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>Проверка "Корзины" не удалась для папки %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Следующие XML-элементы не могут быть прочитаны:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Файл конфигурации %x является неполным. Недостающие элементы будут установлены в значения по умолчанию.</target>
-
<source>Prepare installation</source>
<target>Подготовка установки</target>
diff --git a/FreeFileSync/Build/Languages/slovak.lng b/FreeFileSync/Build/Languages/slovak.lng
index 4d296d17..4e0894dd 100755
--- a/FreeFileSync/Build/Languages/slovak.lng
+++ b/FreeFileSync/Build/Languages/slovak.lng
@@ -7,6 +7,81 @@
<plural_definition>n==1 ? 0 : n>=2 && n<=4 ? 1 : 2</plural_definition>
</header>
+<source>No log entries</source>
+<target></target>
+
+<source>Published under the GNU General Public License:</source>
+<target></target>
+
+<source>Feedback and suggestions are welcome:</source>
+<target></target>
+
+<source>&Delete logs after x days:</source>
+<target></target>
+
+<source>Default log path:</source>
+<target></target>
+
+<source>Access timeout (in seconds):</source>
+<target></target>
+
+<source>Run a command:</source>
+<target></target>
+
+<source>&Override default log path:</source>
+<target></target>
+
+<source>Show &log</source>
+<target></target>
+
+<source>The server returned an error:</source>
+<target></target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target></target>
+
+<source>Authentication failed.</source>
+<target></target>
+
+<source>Authentication completed.</source>
+<target></target>
+
+<source>Removing old file versions:</source>
+<target></target>
+
+<source>Searching for old file versions:</source>
+<target></target>
+
+<source>%x items</source>
+<target></target>
+
+<source>%x/sec</source>
+<target></target>
+
+<source>Quit %x</source>
+<target></target>
+
+<source>Hide %x</source>
+<target></target>
+
+<source>Hide Others</source>
+<target></target>
+
+<source>Show All</source>
+<target></target>
+
+<source>Services</source>
+<target></target>
+
+<source>Warnings:</source>
+<target></target>
+
+<source>Errors:</source>
+<target></target>
+
+<source>The name %x is used by more than one item in the folder.</source>
+<target></target>
+
<source>Both sides have changed since last synchronization.</source>
<target>Od poslednej synchronizácie došlo ku zmene obidvoch strán.</target>
@@ -188,9 +263,6 @@
<source>File time tolerance</source>
<target>Časová tolerancia súboru</target>
-<source>Folder access timeout</source>
-<target>Časový limit prístupu k priečinku</target>
-
<source>Run with background priority</source>
<target>Vykonať s prioritou na pozadí</target>
@@ -336,9 +408,6 @@
<source>Update attributes on right</source>
<target>Aktualizovať atribúty napravo</target>
-<source>Warning</source>
-<target>Varovanie</target>
-
<source>Items processed:</source>
<target>Spracovaných položiek:</target>
@@ -348,6 +417,9 @@
<source>Total time:</source>
<target>Celkový čas:</target>
+<source>Warning</source>
+<target>Varovanie</target>
+
<source>Stopped</source>
<target>Zastavené</target>
@@ -373,12 +445,6 @@
<source>Cannot read directory %x.</source>
<target>Nie je možné načítať adresár %x.</target>
-<source>/sec</source>
-<target>/s</target>
-
-<source>%x items/sec</source>
-<target>%x položiek/s</target>
-
<source>Show in Explorer</source>
<target>Zobraziť v Prieskumníkovi</target>
@@ -526,12 +592,6 @@
<source>Generating database...</source>
<target>Vytváranie databázy...</target>
-<source>Searching for excess file versions:</source>
-<target>Hľadanie prebytočných verzií súborov:</target>
-
-<source>Removing excess file versions:</source>
-<target>Odstránenie prebytočných verzií súborov:</target>
-
<source>Unable to create time stamp for versioning:</source>
<target>Nie je možné vytvoriť časovú značku verzovania:</target>
@@ -588,6 +648,12 @@ Aktuálne: %y b
<source>Unable to move %x to the recycle bin.</source>
<target>Nie je možné presunúť %x do Koša.</target>
+<source>Unable to access %x.</source>
+<target>Nie je možný prístup k %x.</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Nie je možné zistiť voľné miesto na disku %x.</target>
+
<source>Cannot find %x.</source>
<target>Nie je možné nájsť %x.</target>
@@ -600,18 +666,12 @@ Aktuálne: %y b
<source>Cannot delete symbolic link %x.</source>
<target>Nie je možné zmazať symbolický odkaz %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Nie je možné zistiť voľné miesto na disku %x.</target>
-
<source>Incorrect command line:</source>
<target>Neplatný príkaz:</target>
<source>The server does not support authentication via %x.</source>
<target>Server nepodporuje overenie pomocou %x.</target>
-<source>Unable to access %x.</source>
-<target>Nie je možný prístup k %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -638,25 +698,6 @@ Aktuálne: %y b
<source>Failed to open SFTP channel number %x.</source>
<target>Nie je možné otvoriť kanál číslo %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x B</pluralform>
-<pluralform>%x B</pluralform>
-<pluralform>%x B</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Pretiahni sem && pusť</target>
@@ -770,6 +811,25 @@ Príkaz bude spustení ak:
<source>&Retry</source>
<target>&Opakovať</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x B</pluralform>
+<pluralform>%x B</pluralform>
+<pluralform>%x B</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Načítanie...</target>
@@ -915,9 +975,6 @@ Príkaz bude spustení ak:
<source>Save as &batch job...</source>
<target>Uložiť ako &dávku...</target>
-<source>Show &log</source>
-<target></target>
-
<source>Start &comparison</source>
<target>Spustiť &porovnanie</target>
@@ -978,9 +1035,6 @@ Príkaz bude spustení ak:
<source>Access online storage</source>
<target>Prístup k online úložisku</target>
-<source>Swap sides</source>
-<target>Zámena strán</target>
-
<source>Close search bar</source>
<target>Zavrieť hľadanie</target>
@@ -1005,6 +1059,9 @@ Príkaz bude spustení ak:
<source>View type:</source>
<target>Typ zobrazenia:</target>
+<source>Save as default</source>
+<target>Uložiť ako predvolené</target>
+
<source>Select view:</source>
<target>Výber zobrazenia:</target>
@@ -1062,6 +1119,15 @@ Príkaz bude spustení ak:
<source>Handle daylight saving time</source>
<target>Používať letný čas</target>
+<source>Ignore errors</source>
+<target>Ignorovať chyby</target>
+
+<source>Retry count:</source>
+<target>Počet opakovaní:</target>
+
+<source>Delay (in seconds):</source>
+<target>Oneskorenie (v sekundách):</target>
+
<source>Performance improvements:</source>
<target>Zlepšenie výkonu:</target>
@@ -1136,18 +1202,6 @@ Príkaz bude spustení ak:
<source>Last x days:</source>
<target>Posledných x dní:</target>
-<source>Ignore errors</source>
-<target>Ignorovať chyby</target>
-
-<source>Retry count:</source>
-<target>Počet opakovaní:</target>
-
-<source>Delay (in seconds):</source>
-<target>Oneskorenie (v sekundách):</target>
-
-<source>Run a command after synchronization:</source>
-<target>Spustiť príkaz po synchronizácií:</target>
-
<source>OK</source>
<target>OK</target>
@@ -1274,12 +1328,6 @@ Príkaz bude spustení ak:
<source>Stop synchronization at first error</source>
<target>Ukončiť synchronizáciu pri prvej chybe</target>
-<source>Save log:</source>
-<target>Uložiť log:</target>
-
-<source>Limit number of log files:</source>
-<target>Limit počtu log súborov:</target>
-
<source>How can I schedule a batch job?</source>
<target>Ako nastaviť spustenie dávky v Plánovači?</target>
@@ -1313,9 +1361,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Zobraziť znovu všetky trvale skryté dialógy a varovné hlásenia</target>
-<source>Remove old log files after x days:</source>
-<target></target>
-
<source>Customize context menu:</source>
<target>Prispôsobiť kontextovú ponuku:</target>
@@ -1325,9 +1370,6 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&Predvolené</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Komentáre a námety sú vždy vítané</target>
-
<source>Home page</source>
<target>Domovská stránka</target>
@@ -1352,9 +1394,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>Zdrojový kód bol napísaný kompletne v C++ pomocou:</target>
-<source>Published under the GNU General Public License</source>
-<target>Vydané pod GNU General Public License (GPL)</target>
-
<source>Many thanks for localization:</source>
<target>Poďakovanie za preklad FreeFileSync:</target>
@@ -1409,9 +1448,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Info</source>
<target>Info</target>
-<source>No log entries</source>
-<target></target>
-
<source>Select all</source>
<target>Vybrať všetko</target>
@@ -1436,6 +1472,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>Prehľad</target>
+<source>Swap sides</source>
+<target>Zámena strán</target>
+
<source>Show "%x"</source>
<target>Zobraziť "%x"</target>
@@ -1614,9 +1653,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>Zobraziť filtrované alebo dočasne vynechané súbory</target>
-<source>Save as default</source>
-<target>Uložiť ako predvolené</target>
-
<source>Filter</source>
<target>Filter</target>
@@ -1967,12 +2003,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>Zlyhala kontrola Koša pre priečinok %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Nie je možné načítať následujúce XML elementy:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Konfiguračný súbor %x je nekompletný. Chybajúce položky budú nahradené predvolenými hodnotami.</target>
-
<source>Prepare installation</source>
<target>Príprava inštalácie</target>
@@ -2043,7 +2073,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Upraviť vo FreeFileSync</target>
<source>Instead of an ad, here's an animal.</source>
-<target>Namiesto reklami je tu obrázok zvieraťa.</target>
+<target>Namiesto reklamy je tu obrázok zvieraťa.</target>
<source>The FreeFileSync portable version cannot install into a subfolder of %x.</source>
<target>Prenosnú verziu FreeFileSync nie je možné inštalovať do podpriečinka %x.</target>
diff --git a/FreeFileSync/Build/Languages/slovenian.lng b/FreeFileSync/Build/Languages/slovenian.lng
index 8dc2eea4..f013812b 100755
--- a/FreeFileSync/Build/Languages/slovenian.lng
+++ b/FreeFileSync/Build/Languages/slovenian.lng
@@ -162,6 +162,9 @@
<source>Items differ in attributes only</source>
<target>Postavke se razlikujejo samo po atributih</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Ime %x uporablja več kot ena postavka v mapi.</target>
+
<source>Resolving symbolic link %x</source>
<target>Razrešujem simbolično povezavo %x</target>
@@ -189,9 +192,6 @@
<source>File time tolerance</source>
<target>Časovna toleranca datoteke</target>
-<source>Folder access timeout</source>
-<target>Časovna omejitev dostopa do mape</target>
-
<source>Run with background priority</source>
<target>Zaženi s prioriteto v ozadju</target>
@@ -338,8 +338,11 @@
<source>Update attributes on right</source>
<target>Posodobi atribute na desni strani</target>
-<source>Warning</source>
-<target>Opozorilo</target>
+<source>Errors:</source>
+<target>Napake:</target>
+
+<source>Warnings:</source>
+<target>Opozorila:</target>
<source>Items processed:</source>
<target>Obdelanih postavk:</target>
@@ -350,6 +353,9 @@
<source>Total time:</source>
<target>Celoten čas:</target>
+<source>Warning</source>
+<target>Opozorilo</target>
+
<source>Stopped</source>
<target>Ustavljeno</target>
@@ -359,6 +365,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Napaka pri razčlenjevanju datoteke %x, vrstica %y, stolpec %z.</target>
+<source>Services</source>
+<target>Storitve</target>
+
+<source>Show All</source>
+<target>Pokaži vse</target>
+
+<source>Hide Others</source>
+<target>Skrij druge</target>
+
+<source>Hide %x</source>
+<target>Skrij %x</target>
+
+<source>Quit %x</source>
+<target>Nehaj %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Ne morem nastaviti zaklepanje imenika za naslednje mape:</target>
@@ -376,11 +397,11 @@
<source>Cannot read directory %x.</source>
<target>Ne morem prebrati direktorija %x.</target>
-<source>/sec</source>
-<target>/sek</target>
+<source>%x/sec</source>
+<target>%x/sek</target>
-<source>%x items/sec</source>
-<target>%x postavk/sek</target>
+<source>%x items</source>
+<target>%x postavk</target>
<source>Show in Explorer</source>
<target>Prikaži v Raziskovalcu</target>
@@ -529,11 +550,11 @@
<source>Generating database...</source>
<target>Ustvarjam podatkovno bazo...</target>
-<source>Searching for excess file versions:</source>
-<target>Iskanje presežnih različic datotek:</target>
+<source>Searching for old file versions:</source>
+<target>Iskanje starih različic datotek:</target>
-<source>Removing excess file versions:</source>
-<target>Odstranjevanje presežnih različic datotek:</target>
+<source>Removing old file versions:</source>
+<target>Odstranjevanje starejših različic datotek:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Časovnega žiga za oznčitev ni bilo mogoče ustvariti:</target>
@@ -591,6 +612,24 @@ Dejansko: %y bajtov
<source>Unable to move %x to the recycle bin.</source>
<target>Ne morem premakniti %x v koš.</target>
+<source>Unable to access %x.</source>
+<target>Ne morem dostopati do %x.</target>
+
+<source>Authentication completed.</source>
+<target>Preverjanje pristnosti končano.</target>
+
+<source>Authentication failed.</source>
+<target>Preverjanje pristnosti ni uspelo.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>To stran lahko zdaj zaprete in nadaljujete z FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Strežnik je vrnil napako:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Ne morem določiti prostega prostora na disku za %x.</target>
+
<source>Cannot find %x.</source>
<target>Ne najdem %x.</target>
@@ -603,18 +642,12 @@ Dejansko: %y bajtov
<source>Cannot delete symbolic link %x.</source>
<target>Ne morem izbrisati simbolične povezave %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Ne morem določiti prostega prostora na disku za %x.</target>
-
<source>Incorrect command line:</source>
<target>Napačna ukazna vrstica:</target>
<source>The server does not support authentication via %x.</source>
<target>Strežnik ne podpira preverjanja pristnosti preko %x.</target>
-<source>Unable to access %x.</source>
-<target>Ne morem dostopati do %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -643,26 +676,6 @@ Dejansko: %y bajtov
<source>Failed to open SFTP channel number %x.</source>
<target>Ne morem odpreti SFTP kanala številka %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x bajt</pluralform>
-<pluralform>%x bajta</pluralform>
-<pluralform>%x bajti</pluralform>
-<pluralform>%x bajtov</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Povleci && spusti</target>
@@ -776,6 +789,26 @@ Ukaz se sproži če:
<source>&Retry</source>
<target>&Ponovi</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x bajt</pluralform>
+<pluralform>%x bajta</pluralform>
+<pluralform>%x bajti</pluralform>
+<pluralform>%x bajtov</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Nalagam...</target>
@@ -923,7 +956,7 @@ Ukaz se sproži če:
<target>Shrani kot paketno op&ravilo...</target>
<source>Show &log</source>
-<target></target>
+<target>Pokaži &dnevnik</target>
<source>Start &comparison</source>
<target>Začni &primerjavo</target>
@@ -985,9 +1018,6 @@ Ukaz se sproži če:
<source>Access online storage</source>
<target>Dostop do spletnega prostora za shranjevanje</target>
-<source>Swap sides</source>
-<target>Zamenjaj strani</target>
-
<source>Close search bar</source>
<target>Zapri iskalno vrstico</target>
@@ -1012,6 +1042,9 @@ Ukaz se sproži če:
<source>View type:</source>
<target>Vrsta prikaza:</target>
+<source>Save as default</source>
+<target>Shrani kot privzeto</target>
+
<source>Select view:</source>
<target>Izberi prikaz:</target>
@@ -1069,6 +1102,15 @@ Ukaz se sproži če:
<source>Handle daylight saving time</source>
<target>Upoštevaj poletni in zimski čas</target>
+<source>Ignore errors</source>
+<target>Prezri napake</target>
+
+<source>Retry count:</source>
+<target>Število poskusov:</target>
+
+<source>Delay (in seconds):</source>
+<target>Zakasnitev (v sekundah):</target>
+
<source>Performance improvements:</source>
<target>Izboljšave zmogljivosti:</target>
@@ -1143,17 +1185,11 @@ Ukaz se sproži če:
<source>Last x days:</source>
<target>Zadnji x dnevi:</target>
-<source>Ignore errors</source>
-<target>Prezri napake</target>
-
-<source>Retry count:</source>
-<target>Število poskusov:</target>
-
-<source>Delay (in seconds):</source>
-<target>Zakasnitev (v sekundah):</target>
+<source>&Override default log path:</source>
+<target>&Preglasi privzeto pot dnevnika:</target>
-<source>Run a command after synchronization:</source>
-<target>Zaženi ukaz po sinhronizaciji:</target>
+<source>Run a command:</source>
+<target>Zaženi ukaz:</target>
<source>OK</source>
<target>V redu</target>
@@ -1203,6 +1239,9 @@ Ukaz se sproži če:
<source>Directory on server:</source>
<target>Imenik na strežniku:</target>
+<source>Access timeout (in seconds):</source>
+<target>Časovna omejitev dostopa (v sekundah):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP kanali za povezavo:</target>
@@ -1281,12 +1320,6 @@ Ukaz se sproži če:
<source>Stop synchronization at first error</source>
<target>Ustavi sinhronizacijo ob prvi napaki</target>
-<source>Save log:</source>
-<target>Shrani dnevnik:</target>
-
-<source>Limit number of log files:</source>
-<target>Omejitve števila datotek dnevnika:</target>
-
<source>How can I schedule a batch job?</source>
<target>Kako lahko načrtujem opravilo v paketu?</target>
@@ -1323,8 +1356,11 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Prikaži vsa trajno skrita pogovorna okna in opozorilna sporočila</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Privzeta pot dnevnika:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Izbriši dnevnike po x dneh:</target>
<source>Customize context menu:</source>
<target>Prilagodi kontekstni meni:</target>
@@ -1335,8 +1371,8 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>&Default</source>
<target>&Privzeto</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Povratne informacije in predlogi so dobrodošli</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Povratne informacije in predlogi so dobrodošli:</target>
<source>Home page</source>
<target>Domača stran</target>
@@ -1362,8 +1398,8 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Source code written in C++ using:</source>
<target>Izvorna koda napisana v C++ z uporabo:</target>
-<source>Published under the GNU General Public License</source>
-<target>Objavljeno pod licenco GNU General Public</target>
+<source>Published under the GNU General Public License:</source>
+<target>Objavljeno pod licenco GNU General Public:</target>
<source>Many thanks for localization:</source>
<target>Zahvala prevajalcem za lokalizacijo:</target>
@@ -1420,7 +1456,7 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Ni zapisov v dnevniku</target>
<source>Select all</source>
<target>Izberi vse</target>
@@ -1446,6 +1482,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Overview</source>
<target>Predogled</target>
+<source>Swap sides</source>
+<target>Zamenjaj strani</target>
+
<source>Show "%x"</source>
<target>Prikaži "%x"</target>
@@ -1628,9 +1667,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Show filtered or temporarily excluded files</source>
<target>Pokaži filtrirane ali začasno izključene datoteke</target>
-<source>Save as default</source>
-<target>Shrani kot privzeto</target>
-
<source>Filter</source>
<target>Filter</target>
@@ -1986,12 +2022,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake.
<source>Checking recycle bin failed for folder %x.</source>
<target>Preverjanje koša za mapo %x ni uspelo.</target>
-<source>The following XML elements could not be read:</source>
-<target>Neaslednje XML postavke niso berljive:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Nastavitvena datoteka %x je nepopolna. Manjkajoči elementi bodo nastavljeni na privzete vrednosti.</target>
-
<source>Prepare installation</source>
<target>Pripravljam namestitev</target>
diff --git a/FreeFileSync/Build/Languages/spanish.lng b/FreeFileSync/Build/Languages/spanish.lng
index d539e749..0bb0b241 100755
--- a/FreeFileSync/Build/Languages/spanish.lng
+++ b/FreeFileSync/Build/Languages/spanish.lng
@@ -101,7 +101,7 @@
<target>archivo de config. global:</target>
<source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source>
-<target>Cualquier número de archivos de configuración de FreeFileSync ("ffs_gui" ó "ffs_batch").</target>
+<target>Cualquier número de archivos de configuración "ffs_gui" y/o "ffs_batch" de FreeFileSync.</target>
<source>Any number of alternative directory pairs for at most one config file.</source>
<target>Cualquier número de pares de directorios alternativos para un archivo de configuración como máximo.</target>
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Los elementos sólo se diferencian en los atributos</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>El nombre %x es utilizado por más de un elemento en la carpeta.</target>
+
<source>Resolving symbolic link %x</source>
<target>Resolviendo vínculo simbólico %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Tolerancia en hora de archivo</target>
-<source>Folder access timeout</source>
-<target>Tiempo de espera de acceso a carpetas</target>
-
<source>Run with background priority</source>
<target>Ejecutar con prioridad en segundo plano</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Actualizar atributos en la derecha</target>
-<source>Warning</source>
-<target>Atención</target>
+<source>Errors:</source>
+<target>Errores:</target>
+
+<source>Warnings:</source>
+<target>Advertencias:</target>
<source>Items processed:</source>
<target>Elementos procesados:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Tiempo total:</target>
+<source>Warning</source>
+<target>Atención</target>
+
<source>Stopped</source>
<target>Detenido</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Error analizando archivo %x, fila %y, columna %z.</target>
+<source>Services</source>
+<target>Servicios</target>
+
+<source>Show All</source>
+<target>Mostrar todo</target>
+
+<source>Hide Others</source>
+<target>Ocultar otros</target>
+
+<source>Hide %x</source>
+<target>Ocultar %x</target>
+
+<source>Quit %x</source>
+<target>Salir de %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>No se pudieron bloquear directorios para las carpetas siguientes:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>No se puede leer el directorio %x.</target>
-<source>/sec</source>
-<target>/seg</target>
+<source>%x/sec</source>
+<target>%x/seg</target>
-<source>%x items/sec</source>
-<target>%x elementos/seg</target>
+<source>%x items</source>
+<target>%x elementos</target>
<source>Show in Explorer</source>
<target>Mostrar en el Explorador</target>
@@ -523,10 +544,10 @@
<source>Generating database...</source>
<target>Generando base de datos...</target>
-<source>Searching for excess file versions:</source>
+<source>Searching for old file versions:</source>
<target>Buscando versiones antiguas de archivos:</target>
-<source>Removing excess file versions:</source>
+<source>Removing old file versions:</source>
<target>Eliminando versiones antiguas de archivos:</target>
<source>Unable to create time stamp for versioning:</source>
@@ -585,6 +606,24 @@ Reales: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>No es posible mover %x a la papelera de reciclaje.</target>
+<source>Unable to access %x.</source>
+<target>No se puede acceder a %x.</target>
+
+<source>Authentication completed.</source>
+<target>Autenticación completada.</target>
+
+<source>Authentication failed.</source>
+<target>Fallo de autenticación.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Puede cerrar esta página y continuar con FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>El servidor devolvió un error:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>No se puede determinar el espacio libre en disco para %x.</target>
+
<source>Cannot find %x.</source>
<target>No se encuentra %x.</target>
@@ -597,18 +636,12 @@ Reales: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>No se puede eliminar el vínculo simbólico %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>No se puede determinar el espacio libre en disco para %x.</target>
-
<source>Incorrect command line:</source>
<target>Línea de comandos incorrecta:</target>
<source>The server does not support authentication via %x.</source>
<target>El servidor no admite autenticación via %x.</target>
-<source>Unable to access %x.</source>
-<target>No se puede acceder a %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Reales: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Fallo de apertura del canal SFTP número %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x Gb</target>
-
<source>Drag && drop</source>
<target>Arrastrar y soltar</target>
@@ -764,6 +779,24 @@ El comando es disparado si:
<source>&Retry</source>
<target>&Reintentar</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x Gb</target>
+
<source>Loading...</source>
<target>Cargando...</target>
@@ -909,7 +942,7 @@ El comando es disparado si:
<target>Guardar como tarea por &lotes...</target>
<source>Show &log</source>
-<target></target>
+<target>Mostrar re&gistro</target>
<source>Start &comparison</source>
<target>Iniciar la &comparación</target>
@@ -971,9 +1004,6 @@ El comando es disparado si:
<source>Access online storage</source>
<target>Acceder a almacenamiento en línea</target>
-<source>Swap sides</source>
-<target>Intercambiar lados</target>
-
<source>Close search bar</source>
<target>Cerrar la barra de búsqueda</target>
@@ -998,6 +1028,9 @@ El comando es disparado si:
<source>View type:</source>
<target>Ver tipo:</target>
+<source>Save as default</source>
+<target>Guardar como predeterminado</target>
+
<source>Select view:</source>
<target>Seleccionar vista:</target>
@@ -1055,6 +1088,15 @@ El comando es disparado si:
<source>Handle daylight saving time</source>
<target>Respetar el horario de verano</target>
+<source>Ignore errors</source>
+<target>Ignorar errores</target>
+
+<source>Retry count:</source>
+<target>Cuenta de reintentos:</target>
+
+<source>Delay (in seconds):</source>
+<target>Retardo (en segundos):</target>
+
<source>Performance improvements:</source>
<target>Mejoras en el rendimiento:</target>
@@ -1129,17 +1171,11 @@ El comando es disparado si:
<source>Last x days:</source>
<target>Últimos x días:</target>
-<source>Ignore errors</source>
-<target>Ignorar errores</target>
-
-<source>Retry count:</source>
-<target>Cuenta de reintentos:</target>
-
-<source>Delay (in seconds):</source>
-<target>Retardo (en segundos):</target>
+<source>&Override default log path:</source>
+<target>&Reemplazar la ruta predeterminada del registro:</target>
-<source>Run a command after synchronization:</source>
-<target>Ejecutar un comando tras la sincronización:</target>
+<source>Run a command:</source>
+<target>Ejecutar un comando:</target>
<source>OK</source>
<target>OK</target>
@@ -1189,6 +1225,9 @@ El comando es disparado si:
<source>Directory on server:</source>
<target>Directorio del servidor:</target>
+<source>Access timeout (in seconds):</source>
+<target>Tiempo de espera en acceso (segundos):</target>
+
<source>SFTP channels per connection:</source>
<target>Canales SFTP por conexión:</target>
@@ -1250,7 +1289,7 @@ El comando es disparado si:
<target>Crear tarea por lotes para sincronización desatendida. Para iniciar, haga doble clic en este archivo o prográmelo en el planificador de tareas: %x</target>
<source>Progress dialog:</source>
-<target>Diálogo de progreson:</target>
+<target>Diálogo de progreso:</target>
<source>Run minimized</source>
<target>Ejecutar minimizado</target>
@@ -1267,12 +1306,6 @@ El comando es disparado si:
<source>Stop synchronization at first error</source>
<target>Detener la sincronización con el primer error</target>
-<source>Save log:</source>
-<target>Guardar registro:</target>
-
-<source>Limit number of log files:</source>
-<target>Limitar el número de archivos de registro:</target>
-
<source>How can I schedule a batch job?</source>
<target>¿Cómo puedo programar una tarea por lotes?</target>
@@ -1309,8 +1342,11 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Volver a mostrar todos los diálogos y mensajes de advertencia que fueron permanentemente ocultados</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Ruta predeterminada del registro:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Eliminar los registros tras x días:</target>
<source>Customize context menu:</source>
<target>Personalizar menú contextual:</target>
@@ -1321,8 +1357,8 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>&Default</source>
<target>&Configuración predeterminada</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Tus comentarios y sugerencias son bienvenidos:</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Comentarios y sugerencias son bienvenidos:</target>
<source>Home page</source>
<target>Página de inicio</target>
@@ -1348,8 +1384,8 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Source code written in C++ using:</source>
<target>Código fuente C++ con soporte de:</target>
-<source>Published under the GNU General Public License</source>
-<target>Publicado bajo régimen de derechos GNU General Public License:</target>
+<source>Published under the GNU General Public License:</source>
+<target>Publicado con licencia pública general GNU:</target>
<source>Many thanks for localization:</source>
<target>Agradecimientos a los traductores:</target>
@@ -1406,7 +1442,7 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>No hay entradas de registro</target>
<source>Select all</source>
<target>Seleccionar todo</target>
@@ -1432,6 +1468,9 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Overview</source>
<target>Vista general</target>
+<source>Swap sides</source>
+<target>Intercambiar lados</target>
+
<source>Show "%x"</source>
<target>Mostrar "%x"</target>
@@ -1606,9 +1645,6 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Show filtered or temporarily excluded files</source>
<target>Mostrar archivos excluidos temporalmente o filtrados</target>
-<source>Save as default</source>
-<target>Guardar como predeterminado</target>
-
<source>Filter</source>
<target>Filtro</target>
@@ -1954,12 +1990,6 @@ Esto garantiza un estado coherente incluso en caso de error grave.
<source>Checking recycle bin failed for folder %x.</source>
<target>Fallo al comprobar la papelera de reciclaje para la carpeta %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>No se pueden leer los siguientes elementos XML:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>El archivo de configuración %x está incompleto. Los elementos ausentes se definirán con valores predeterminados.</target>
-
<source>Prepare installation</source>
<target>Preparar la instalación</target>
diff --git a/FreeFileSync/Build/Languages/swedish.lng b/FreeFileSync/Build/Languages/swedish.lng
index ee21b7ee..91965af7 100755
--- a/FreeFileSync/Build/Languages/swedish.lng
+++ b/FreeFileSync/Build/Languages/swedish.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Endast attribut skiljer objekten åt</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Namnet %x, används av mer än ett objekt, i mappen.</target>
+
<source>Resolving symbolic link %x</source>
<target>Översätter den symboliska länken %x</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Tidsstämpeltolerans</target>
-<source>Folder access timeout</source>
-<target>Tidsgräns för mappåtkomst</target>
-
<source>Run with background priority</source>
<target>Kör med bakgrundsprioritet</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Uppdatera attribut på höger sida</target>
-<source>Warning</source>
-<target>Varning</target>
+<source>Errors:</source>
+<target>Fel:</target>
+
+<source>Warnings:</source>
+<target>Varningar:</target>
<source>Items processed:</source>
<target>Processade poster:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Total tid:</target>
+<source>Warning</source>
+<target>Varning</target>
+
<source>Stopped</source>
<target>Stoppad</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Tolkningsfel på filen %x, rad %y, kolumn %z.</target>
+<source>Services</source>
+<target>Tjänster</target>
+
+<source>Show All</source>
+<target>Visa all</target>
+
+<source>Hide Others</source>
+<target>Dölj andra</target>
+
+<source>Hide %x</source>
+<target>Dölj %x</target>
+
+<source>Quit %x</source>
+<target>Avsluta %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Kan inte ange mapplås för följanda mappar:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>Kan inte läsa mappen %x.</target>
-<source>/sec</source>
-<target>/s</target>
+<source>%x/sec</source>
+<target>%x/sek</target>
-<source>%x items/sec</source>
-<target>%x objekt/sek.</target>
+<source>%x items</source>
+<target>%x objekt</target>
<source>Show in Explorer</source>
<target>Visa i Utforskaren</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Skapar databas...</target>
-<source>Searching for excess file versions:</source>
-<target>Söker efter överflödiga filversioner:</target>
+<source>Searching for old file versions:</source>
+<target>Söker efter gamla filversioner:</target>
-<source>Removing excess file versions:</source>
-<target>Tar bort överflödiga filversioner:</target>
+<source>Removing old file versions:</source>
+<target>Tar bort gamla filversioner:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Kunde inte skapa tidsstämpel för versionshantering:</target>
@@ -585,6 +606,24 @@ Aktuell: %y byte
<source>Unable to move %x to the recycle bin.</source>
<target>Kan inte att flytta %x till papperskorgen.</target>
+<source>Unable to access %x.</source>
+<target>Kan inte komma åt %x.</target>
+
+<source>Authentication completed.</source>
+<target>Autentisering slutförd.</target>
+
+<source>Authentication failed.</source>
+<target>Autentisering misslyckades.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Du kan nu stänga denna sida och fortsätta med FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Servern returnerade ett fel:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Kan inte avgöra ledigt utrymme på %x.</target>
+
<source>Cannot find %x.</source>
<target>Kan inte hitta %x.</target>
@@ -597,18 +636,12 @@ Aktuell: %y byte
<source>Cannot delete symbolic link %x.</source>
<target>Kan inte ta bort den symboliska länken %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Kan inte avgöra ledigt utrymme på %x.</target>
-
<source>Incorrect command line:</source>
<target>Felaktig kommandorad:</target>
<source>The server does not support authentication via %x.</source>
<target>Servern stödjer inte autentisering via %x.</target>
-<source>Unable to access %x.</source>
-<target>Kan inte komma åt %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Aktuell: %y byte
<source>Failed to open SFTP channel number %x.</source>
<target>Kunde inte öppna SFTP-kanal nummer %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 byte</pluralform>
-<pluralform>%x byte</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Dra && släpp</target>
@@ -764,6 +779,24 @@ Kommandot triggas om:
<source>&Retry</source>
<target>&Försök igen</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 byte</pluralform>
+<pluralform>%x byte</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Läser in...</target>
@@ -909,7 +942,7 @@ Kommandot triggas om:
<target>Spara som &batch-fil...</target>
<source>Show &log</source>
-<target></target>
+<target>Visa &logg</target>
<source>Start &comparison</source>
<target>&Jämför</target>
@@ -971,9 +1004,6 @@ Kommandot triggas om:
<source>Access online storage</source>
<target>Anslut fjärrlagring</target>
-<source>Swap sides</source>
-<target>Byt sida</target>
-
<source>Close search bar</source>
<target>Stäng sökfältet</target>
@@ -998,6 +1028,9 @@ Kommandot triggas om:
<source>View type:</source>
<target>Visningstyp:</target>
+<source>Save as default</source>
+<target>Spara som standard</target>
+
<source>Select view:</source>
<target>Välj vy:</target>
@@ -1055,6 +1088,15 @@ Kommandot triggas om:
<source>Handle daylight saving time</source>
<target>Hantera sommartid</target>
+<source>Ignore errors</source>
+<target>Ignorera fel</target>
+
+<source>Retry count:</source>
+<target>Antal försök:</target>
+
+<source>Delay (in seconds):</source>
+<target>Fördröjning (i sekunder):</target>
+
<source>Performance improvements:</source>
<target>Prestandaförbättringar:</target>
@@ -1129,17 +1171,11 @@ Kommandot triggas om:
<source>Last x days:</source>
<target>Senaste x dagarna:</target>
-<source>Ignore errors</source>
-<target>Ignorera fel</target>
-
-<source>Retry count:</source>
-<target>Antal försök:</target>
-
-<source>Delay (in seconds):</source>
-<target>Fördröjning (i sekunder):</target>
+<source>&Override default log path:</source>
+<target>&Åsidosätt standard loggsökväg:</target>
-<source>Run a command after synchronization:</source>
-<target>Kör ett kommando efter synkronisering:</target>
+<source>Run a command:</source>
+<target>Kör ett kommando:</target>
<source>OK</source>
<target>OK</target>
@@ -1189,6 +1225,9 @@ Kommandot triggas om:
<source>Directory on server:</source>
<target>Målmapp på servern:</target>
+<source>Access timeout (in seconds):</source>
+<target>Åtkomsttidsgräns (i sekunder):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP-kanaler per anslutning:</target>
@@ -1267,12 +1306,6 @@ Kommandot triggas om:
<source>Stop synchronization at first error</source>
<target>Stoppa synkroniseringen vid första fel som uppstår</target>
-<source>Save log:</source>
-<target>Spara logg:</target>
-
-<source>Limit number of log files:</source>
-<target>Begränsa antalet loggfiler:</target>
-
<source>How can I schedule a batch job?</source>
<target>Hur schemalägger jag en batch-fil?</target>
@@ -1309,8 +1342,11 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Visa alla permanent dolda dialoger och varningsmeddelanden igen</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Standard loggsökväg:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Ta bort loggar efter x dagar:</target>
<source>Customize context menu:</source>
<target>Anpassad kontextmeny:</target>
@@ -1321,8 +1357,8 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>&Default</source>
<target>&Standard</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Återkoppling och förslag är välkommna</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Återkoppling och förslag välkomnas:</target>
<source>Home page</source>
<target>Hemsida</target>
@@ -1348,8 +1384,8 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Source code written in C++ using:</source>
<target>Källkod skriven i C++ med hjälp av:</target>
-<source>Published under the GNU General Public License</source>
-<target>Publiserad under GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Publicerat under GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Tack för översättning:</target>
@@ -1406,7 +1442,7 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<target>Info</target>
<source>No log entries</source>
-<target></target>
+<target>Inga loggposter</target>
<source>Select all</source>
<target>Markera alla</target>
@@ -1432,6 +1468,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Overview</source>
<target>Översikt</target>
+<source>Swap sides</source>
+<target>Byt sida</target>
+
<source>Show "%x"</source>
<target>Visa "%x"</target>
@@ -1606,9 +1645,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Show filtered or temporarily excluded files</source>
<target>Dölj filtrerade eller temporärt undantagna filer</target>
-<source>Save as default</source>
-<target>Spara som standard</target>
-
<source>Filter</source>
<target>Filter</target>
@@ -1954,12 +1990,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel.
<source>Checking recycle bin failed for folder %x.</source>
<target>Kontroll av papperskorgen misslyckades för %x.</target>
-<source>The following XML elements could not be read:</source>
-<target>Följande XML-element kunde inte läsas:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Konfigurationsfilen %x är inte komplett. De saknade elementen kommer att anges till standardvärdet.</target>
-
<source>Prepare installation</source>
<target>Förbered installation</target>
diff --git a/FreeFileSync/Build/Languages/turkish.lng b/FreeFileSync/Build/Languages/turkish.lng
index e4c3a9fd..d06e670f 100755
--- a/FreeFileSync/Build/Languages/turkish.lng
+++ b/FreeFileSync/Build/Languages/turkish.lng
@@ -160,6 +160,9 @@
<source>Items differ in attributes only</source>
<target>Yalnız öznitelikleri farklı olan ögeler</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target></target>
+
<source>Resolving symbolic link %x</source>
<target>%x sembolik bağlantısı çözümleniyor</target>
@@ -187,9 +190,6 @@
<source>File time tolerance</source>
<target>Yok sayılacak dosya zamanı farkı</target>
-<source>Folder access timeout</source>
-<target>Klasör erişimi zaman aşımı</target>
-
<source>Run with background priority</source>
<target>Artalan önceliği ile çalışsın</target>
@@ -334,8 +334,11 @@
<source>Update attributes on right</source>
<target>Sağdaki öznitelikler güncellensin</target>
-<source>Warning</source>
-<target>Uyarı</target>
+<source>Errors:</source>
+<target></target>
+
+<source>Warnings:</source>
+<target></target>
<source>Items processed:</source>
<target>İşlenen öge:</target>
@@ -346,6 +349,9 @@
<source>Total time:</source>
<target>Toplam süre:</target>
+<source>Warning</source>
+<target>Uyarı</target>
+
<source>Stopped</source>
<target>Durduruldu</target>
@@ -355,6 +361,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>%x dosyası işlenirken sorun çıktı, satır %y, sütun %z.</target>
+<source>Services</source>
+<target></target>
+
+<source>Show All</source>
+<target></target>
+
+<source>Hide Others</source>
+<target></target>
+
+<source>Hide %x</source>
+<target></target>
+
+<source>Quit %x</source>
+<target></target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Şu klasörler kilitlenemedi:</target>
@@ -370,11 +391,11 @@
<source>Cannot read directory %x.</source>
<target>%x klasörü okunamadı.</target>
-<source>/sec</source>
-<target>/saniye</target>
+<source>%x/sec</source>
+<target></target>
-<source>%x items/sec</source>
-<target>%x öge/saniye</target>
+<source>%x items</source>
+<target></target>
<source>Show in Explorer</source>
<target>Tarayıcıda Görüntüle</target>
@@ -523,11 +544,11 @@
<source>Generating database...</source>
<target>Veritabanı oluşturuluyor...</target>
-<source>Searching for excess file versions:</source>
-<target>Fazla dosya sürümleri aranıyor:</target>
+<source>Searching for old file versions:</source>
+<target></target>
-<source>Removing excess file versions:</source>
-<target>Fazla dosya sürümleri siliniyor:</target>
+<source>Removing old file versions:</source>
+<target></target>
<source>Unable to create time stamp for versioning:</source>
<target>Sürümlendirme için zaman damgası oluşturulamadı:</target>
@@ -585,6 +606,24 @@ Gerçekleşen: %y bayt
<source>Unable to move %x to the recycle bin.</source>
<target>%x geri dönüşüm kutusuna atılamadı.</target>
+<source>Unable to access %x.</source>
+<target>%x üzerine erişilemedi.</target>
+
+<source>Authentication completed.</source>
+<target></target>
+
+<source>Authentication failed.</source>
+<target></target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target></target>
+
+<source>The server returned an error:</source>
+<target></target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>%x için boş disk alanı belirlenemedi.</target>
+
<source>Cannot find %x.</source>
<target>%x bulunamadı.</target>
@@ -597,18 +636,12 @@ Gerçekleşen: %y bayt
<source>Cannot delete symbolic link %x.</source>
<target>%x sembolik bağlantısı silinemedi.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>%x için boş disk alanı belirlenemedi.</target>
-
<source>Incorrect command line:</source>
<target>Satırdaki komut geçersiz:</target>
<source>The server does not support authentication via %x.</source>
<target>Sunucu %x üzerinden kimlik doğrulamasını desteklemiyor.</target>
-<source>Unable to access %x.</source>
-<target>%x üzerine erişilemedi.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -633,24 +666,6 @@ Gerçekleşen: %y bayt
<source>Failed to open SFTP channel number %x.</source>
<target>%x SFTP kanal numarası açılamadı.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>1 bayt</pluralform>
-<pluralform>%x bayt</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x MB</target>
-
-<source>%x KB</source>
-<target>%x KB</target>
-
-<source>%x GB</source>
-<target>%x GB</target>
-
<source>Drag && drop</source>
<target>Dosyaları sürükleyip buraya bırakın</target>
@@ -764,6 +779,24 @@ Komut şu durumlarda yürütülür:
<source>&Retry</source>
<target>&Yeniden Dene</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>1 bayt</pluralform>
+<pluralform>%x bayt</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x MB</target>
+
+<source>%x KB</source>
+<target>%x KB</target>
+
+<source>%x GB</source>
+<target>%x GB</target>
+
<source>Loading...</source>
<target>Yükleniyor...</target>
@@ -971,9 +1004,6 @@ Komut şu durumlarda yürütülür:
<source>Access online storage</source>
<target>Çevrimiçi Depolama Erişimi</target>
-<source>Swap sides</source>
-<target>Sağ ve Sol Tarafları Değiştir</target>
-
<source>Close search bar</source>
<target>Arama Çubuğunu Kapat</target>
@@ -998,6 +1028,9 @@ Komut şu durumlarda yürütülür:
<source>View type:</source>
<target>Görünüm Kipi:</target>
+<source>Save as default</source>
+<target>Varsayılan Olarak Kaydet</target>
+
<source>Select view:</source>
<target>Eşit Dosya Görünümü:</target>
@@ -1055,6 +1088,15 @@ Komut şu durumlarda yürütülür:
<source>Handle daylight saving time</source>
<target>Yaz saati hakkında bilgiler</target>
+<source>Ignore errors</source>
+<target>Sorunlar yok sayılsın</target>
+
+<source>Retry count:</source>
+<target>Deneme Sayısı:</target>
+
+<source>Delay (in seconds):</source>
+<target>Bekleme (saniye):</target>
+
<source>Performance improvements:</source>
<target>Başarım İyileştirmeleri:</target>
@@ -1129,17 +1171,11 @@ Komut şu durumlarda yürütülür:
<source>Last x days:</source>
<target>Son x Gün:</target>
-<source>Ignore errors</source>
-<target>Sorunlar yok sayılsın</target>
-
-<source>Retry count:</source>
-<target>Deneme Sayısı:</target>
-
-<source>Delay (in seconds):</source>
-<target>Bekleme (saniye):</target>
+<source>&Override default log path:</source>
+<target></target>
-<source>Run a command after synchronization:</source>
-<target>Eşitleme sonrası yürütülecek komut:</target>
+<source>Run a command:</source>
+<target></target>
<source>OK</source>
<target>Tamam</target>
@@ -1189,6 +1225,9 @@ Komut şu durumlarda yürütülür:
<source>Directory on server:</source>
<target>Sunucudaki Klasör:</target>
+<source>Access timeout (in seconds):</source>
+<target></target>
+
<source>SFTP channels per connection:</source>
<target>Bir Bağlantı için SFTP Kanalı Sayısı:</target>
@@ -1267,12 +1306,6 @@ Komut şu durumlarda yürütülür:
<source>Stop synchronization at first error</source>
<target>Çıkan ilk sorunda eşitleme durdurulsun</target>
-<source>Save log:</source>
-<target>İşlem Günlüğü Kaydedilsin:</target>
-
-<source>Limit number of log files:</source>
-<target>Günlük dosyaları sayısı sınırlaması:</target>
-
<source>How can I schedule a batch job?</source>
<target>Toplu İşlem Zamanlaması Hakkında Bilgiler</target>
@@ -1309,7 +1342,10 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Kalıcı olarak gizlenmiş tüm ileti ve uyarılar yeniden görüntülenir</target>
-<source>Remove old log files after x days:</source>
+<source>Default log path:</source>
+<target></target>
+
+<source>&Delete logs after x days:</source>
<target></target>
<source>Customize context menu:</source>
@@ -1321,8 +1357,8 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>&Default</source>
<target>&Varsayılan</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Öneri ve geri bildirimlerinizi bekleriz</target>
+<source>Feedback and suggestions are welcome:</source>
+<target></target>
<source>Home page</source>
<target>Ana Sayfa</target>
@@ -1348,8 +1384,8 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Source code written in C++ using:</source>
<target>Kaynak kodu C++ kullanılarak yazılmıştır:</target>
-<source>Published under the GNU General Public License</source>
-<target>GNU Genel Kamu Lisansı koşulları altında yayınlanmıştır</target>
+<source>Published under the GNU General Public License:</source>
+<target></target>
<source>Many thanks for localization:</source>
<target>Çeviriler için çok teşekkürler:</target>
@@ -1432,6 +1468,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Overview</source>
<target>Genel</target>
+<source>Swap sides</source>
+<target>Sağ ve Sol Tarafları Değiştir</target>
+
<source>Show "%x"</source>
<target>"%x" paneli görüntülensin</target>
@@ -1606,9 +1645,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Show filtered or temporarily excluded files</source>
<target>Süzülmüş ya da geçici olarak katılmayan dosyaları görüntüler ya da gizler</target>
-<source>Save as default</source>
-<target>Varsayılan Olarak Kaydet</target>
-
<source>Filter</source>
<target>Süzme</target>
@@ -1954,12 +1990,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y
<source>Checking recycle bin failed for folder %x.</source>
<target>%x klasörü için Geri Dönüşüm Kutusu denetlenemedi.</target>
-<source>The following XML elements could not be read:</source>
-<target>Şu XML bileşenleri okunamadı:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>%x yapılandırma dosyası tam değil. Eksik bileşenler için varsayılan değerler kullanılacak.</target>
-
<source>Prepare installation</source>
<target>Kuruluma hazırlanıyor</target>
diff --git a/FreeFileSync/Build/Languages/ukrainian.lng b/FreeFileSync/Build/Languages/ukrainian.lng
index a095fe89..b3d31637 100755
--- a/FreeFileSync/Build/Languages/ukrainian.lng
+++ b/FreeFileSync/Build/Languages/ukrainian.lng
@@ -161,6 +161,9 @@
<source>Items differ in attributes only</source>
<target>Елементи відрізняються тільки атрибутами</target>
+<source>The name %x is used by more than one item in the folder.</source>
+<target>Ім'я %x використовується більше ніж одним елементом у папці.</target>
+
<source>Resolving symbolic link %x</source>
<target>Вирішення символьного посилання %x</target>
@@ -188,9 +191,6 @@
<source>File time tolerance</source>
<target>Толеранс часу файлу</target>
-<source>Folder access timeout</source>
-<target>Таймаут доступу до папки</target>
-
<source>Run with background priority</source>
<target>Запустити з фоновим пріоритетом</target>
@@ -336,8 +336,11 @@
<source>Update attributes on right</source>
<target>Оновити атрибути праворуч</target>
-<source>Warning</source>
-<target>Увага</target>
+<source>Errors:</source>
+<target>Помилки:</target>
+
+<source>Warnings:</source>
+<target>Попередження:</target>
<source>Items processed:</source>
<target>Елементів оброблено:</target>
@@ -348,6 +351,9 @@
<source>Total time:</source>
<target>Загальний час:</target>
+<source>Warning</source>
+<target>Увага</target>
+
<source>Stopped</source>
<target>Зупинено</target>
@@ -357,6 +363,21 @@
<source>Error parsing file %x, row %y, column %z.</source>
<target>Помилка розбору файлу %x, рядок %y, колонка %z.</target>
+<source>Services</source>
+<target>Служби</target>
+
+<source>Show All</source>
+<target>Показати Усі</target>
+
+<source>Hide Others</source>
+<target>Сховати Інші</target>
+
+<source>Hide %x</source>
+<target>Сховати %x</target>
+
+<source>Quit %x</source>
+<target>Вихід %x</target>
+
<source>Cannot set directory locks for the following folders:</source>
<target>Неможливо встановити блокування каталогів для таких папок:</target>
@@ -373,11 +394,11 @@
<source>Cannot read directory %x.</source>
<target>Не вдається прочитати папку %x.</target>
-<source>/sec</source>
-<target>/сек</target>
+<source>%x/sec</source>
+<target>%x/сек</target>
-<source>%x items/sec</source>
-<target>%x елемента/сек</target>
+<source>%x items</source>
+<target>%x елементів</target>
<source>Show in Explorer</source>
<target>Показати у Провіднику</target>
@@ -526,11 +547,11 @@
<source>Generating database...</source>
<target>Створення бази даних...</target>
-<source>Searching for excess file versions:</source>
-<target>Пошук надлишкових версій файлів:</target>
+<source>Searching for old file versions:</source>
+<target>Пошук старих версій файлів:</target>
-<source>Removing excess file versions:</source>
-<target>Видалення надлишкових версій файлів:</target>
+<source>Removing old file versions:</source>
+<target>Видалення старих версій файлів:</target>
<source>Unable to create time stamp for versioning:</source>
<target>Не вдається створити часової мітки для версій:</target>
@@ -588,6 +609,24 @@ Actual: %y bytes
<source>Unable to move %x to the recycle bin.</source>
<target>Не вдається перемістити %x до корзини.</target>
+<source>Unable to access %x.</source>
+<target>Не вдалося отримати доступ до %x.</target>
+
+<source>Authentication completed.</source>
+<target>Автентифікація виконана.</target>
+
+<source>Authentication failed.</source>
+<target>Автентифікація не виконана.</target>
+
+<source>You may close this page now and continue with FreeFileSync.</source>
+<target>Ви можете закрити цю сторінку зараз і продовжити з FreeFileSync.</target>
+
+<source>The server returned an error:</source>
+<target>Сервер вернув помилку:</target>
+
+<source>Cannot determine free disk space for %x.</source>
+<target>Не вдається визначити об'єм вільного місця для %x.</target>
+
<source>Cannot find %x.</source>
<target>Не вдається знайти %x.</target>
@@ -600,18 +639,12 @@ Actual: %y bytes
<source>Cannot delete symbolic link %x.</source>
<target>Не вдалося вилучити символьне посилання %x.</target>
-<source>Cannot determine free disk space for %x.</source>
-<target>Не вдається визначити об'єм вільного місця для %x.</target>
-
<source>Incorrect command line:</source>
<target>Неправильний командний рядок:</target>
<source>The server does not support authentication via %x.</source>
<target>Сервер не підтримує аутентифікацію за допомогою %x.</target>
-<source>Unable to access %x.</source>
-<target>Не вдалося отримати доступ до %x.</target>
-
<source>
<pluralform>Operation timed out after 1 second.</pluralform>
<pluralform>Operation timed out after %x seconds.</pluralform>
@@ -638,25 +671,6 @@ Actual: %y bytes
<source>Failed to open SFTP channel number %x.</source>
<target>Не вдалося відкрити SFTP канал номер %x.</target>
-<source>
-<pluralform>1 byte</pluralform>
-<pluralform>%x bytes</pluralform>
-</source>
-<target>
-<pluralform>%x байт</pluralform>
-<pluralform>%x байти</pluralform>
-<pluralform>%x байтів</pluralform>
-</target>
-
-<source>%x MB</source>
-<target>%x МБ</target>
-
-<source>%x KB</source>
-<target>%x КБ</target>
-
-<source>%x GB</source>
-<target>%x ГБ</target>
-
<source>Drag && drop</source>
<target>Drag && drop</target>
@@ -770,6 +784,25 @@ The command is triggered if:
<source>&Retry</source>
<target>&Повторити</target>
+<source>
+<pluralform>1 byte</pluralform>
+<pluralform>%x bytes</pluralform>
+</source>
+<target>
+<pluralform>%x байт</pluralform>
+<pluralform>%x байти</pluralform>
+<pluralform>%x байтів</pluralform>
+</target>
+
+<source>%x MB</source>
+<target>%x МБ</target>
+
+<source>%x KB</source>
+<target>%x КБ</target>
+
+<source>%x GB</source>
+<target>%x ГБ</target>
+
<source>Loading...</source>
<target>Завантаження...</target>
@@ -916,7 +949,7 @@ The command is triggered if:
<target>Зберегти як &пакетне завдання...</target>
<source>Show &log</source>
-<target></target>
+<target>Показати &журнал</target>
<source>Start &comparison</source>
<target>Запуск по&рівняння</target>
@@ -978,9 +1011,6 @@ The command is triggered if:
<source>Access online storage</source>
<target>Доступ до online сховища</target>
-<source>Swap sides</source>
-<target>Поміняти місцями</target>
-
<source>Close search bar</source>
<target>Закрити панель пошуку</target>
@@ -1005,6 +1035,9 @@ The command is triggered if:
<source>View type:</source>
<target>Тип перегляду:</target>
+<source>Save as default</source>
+<target>Зберегти як замовчування</target>
+
<source>Select view:</source>
<target>Вибрати перегляд:</target>
@@ -1062,6 +1095,15 @@ The command is triggered if:
<source>Handle daylight saving time</source>
<target>Перехід на літній час вручну</target>
+<source>Ignore errors</source>
+<target>Ігнорувати помилки</target>
+
+<source>Retry count:</source>
+<target>Кількість спроб:</target>
+
+<source>Delay (in seconds):</source>
+<target>Затримка (секунд):</target>
+
<source>Performance improvements:</source>
<target>Підвищення продуктивності:</target>
@@ -1136,17 +1178,11 @@ The command is triggered if:
<source>Last x days:</source>
<target>Останні x днів:</target>
-<source>Ignore errors</source>
-<target>Ігнорувати помилки</target>
-
-<source>Retry count:</source>
-<target>Кількість спроб:</target>
-
-<source>Delay (in seconds):</source>
-<target>Затримка (секунд):</target>
+<source>&Override default log path:</source>
+<target>&Перевизначити шлях журналу за замовчуванням:</target>
-<source>Run a command after synchronization:</source>
-<target>Виконати команду після синхронізації:</target>
+<source>Run a command:</source>
+<target>Запустити команду:</target>
<source>OK</source>
<target>OK</target>
@@ -1196,6 +1232,9 @@ The command is triggered if:
<source>Directory on server:</source>
<target>Папка на сервері:</target>
+<source>Access timeout (in seconds):</source>
+<target>Тайм-аут доступу (у секундах):</target>
+
<source>SFTP channels per connection:</source>
<target>SFTP канали на з'єднання:</target>
@@ -1274,12 +1313,6 @@ The command is triggered if:
<source>Stop synchronization at first error</source>
<target>Зупинити синхронізацію при першій помилці</target>
-<source>Save log:</source>
-<target>Зберегти журнал:</target>
-
-<source>Limit number of log files:</source>
-<target>Обмеження кількості файлів журналу:</target>
-
<source>How can I schedule a batch job?</source>
<target>Як можна запланувати пакетне завдання?</target>
@@ -1316,8 +1349,11 @@ This guarantees a consistent state even in case of a serious error.
<source>Show all permanently hidden dialogs and warning messages again</source>
<target>Показати всі сховані діалоги і повідомлення з попередженнями знову</target>
-<source>Remove old log files after x days:</source>
-<target></target>
+<source>Default log path:</source>
+<target>Шлях до журналу за замовчуванням:</target>
+
+<source>&Delete logs after x days:</source>
+<target>&Видалити журнали після x днів:</target>
<source>Customize context menu:</source>
<target>Налаштування контекстного меню:</target>
@@ -1328,8 +1364,8 @@ This guarantees a consistent state even in case of a serious error.
<source>&Default</source>
<target>&За замовчуванням</target>
-<source>Feedback and suggestions are welcome</source>
-<target>Відгуки та пропозиції вітаються</target>
+<source>Feedback and suggestions are welcome:</source>
+<target>Зворотній зв'язок і пропозиції вітаються:</target>
<source>Home page</source>
<target>Домашня сторінка</target>
@@ -1355,8 +1391,8 @@ This guarantees a consistent state even in case of a serious error.
<source>Source code written in C++ using:</source>
<target>Код програми написаний на C++ з використанням:</target>
-<source>Published under the GNU General Public License</source>
-<target>Видано за ліцензією GNU General Public License</target>
+<source>Published under the GNU General Public License:</source>
+<target>Опубліковано за GNU General Public License:</target>
<source>Many thanks for localization:</source>
<target>Подяка за локалізацію:</target>
@@ -1413,7 +1449,7 @@ This guarantees a consistent state even in case of a serious error.
<target>Інформація</target>
<source>No log entries</source>
-<target></target>
+<target>Немає записів журналу</target>
<source>Select all</source>
<target>Виділити все</target>
@@ -1439,6 +1475,9 @@ This guarantees a consistent state even in case of a serious error.
<source>Overview</source>
<target>Огляд</target>
+<source>Swap sides</source>
+<target>Поміняти місцями</target>
+
<source>Show "%x"</source>
<target>Показати "%x"</target>
@@ -1617,9 +1656,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Show filtered or temporarily excluded files</source>
<target>Показати відфільтровані чи тимчасово виключені елементи</target>
-<source>Save as default</source>
-<target>Зберегти як замовчування</target>
-
<source>Filter</source>
<target>Фільтр</target>
@@ -1970,12 +2006,6 @@ This guarantees a consistent state even in case of a serious error.
<source>Checking recycle bin failed for folder %x.</source>
<target>Перевірка корзини для папки %x не вдалася.</target>
-<source>The following XML elements could not be read:</source>
-<target>Наступні XML елементи не вдалося прочитати:</target>
-
-<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source>
-<target>Файл конфігурації %x неповний. Елементи, яких не вистачає, будуть заповнені значеннями за замовчуванням.</target>
-
<source>Prepare installation</source>
<target>Підготовка встановлення</target>
diff --git a/FreeFileSync/Build/styles.gtk_rc b/FreeFileSync/Build/styles.gtk_rc
index 36eecb1b..b13985a0 100755
--- a/FreeFileSync/Build/styles.gtk_rc
+++ b/FreeFileSync/Build/styles.gtk_rc
@@ -1,7 +1,7 @@
style "no-inner-border"
{
- GtkButton::focus-padding = 0
- GtkButton::inner-border = {0, 0, 0, 0}
+ GtkButton::inner-border = {0, 0, 0, 0} /*remove excessive borders on Gnome*/
+ /*GtkButton::focus-padding = 0 => keep default: minor difference + looks better on KDE */
}
class "GtkButton" style "no-inner-border"
diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile
index bd274138..8c666383 100755
--- a/FreeFileSync/Source/Makefile
+++ b/FreeFileSync/Source/Makefile
@@ -39,11 +39,11 @@ CPP_FILES+=base/dir_lock.cpp
CPP_FILES+=base/ffs_paths.cpp
CPP_FILES+=base/file_hierarchy.cpp
CPP_FILES+=base/generate_logfile.cpp
-CPP_FILES+=base/hard_filter.cpp
CPP_FILES+=base/icon_buffer.cpp
CPP_FILES+=base/icon_loader.cpp
CPP_FILES+=base/localization.cpp
CPP_FILES+=base/parallel_scan.cpp
+CPP_FILES+=base/path_filter.cpp
CPP_FILES+=base/process_xml.cpp
CPP_FILES+=base/perf_check.cpp
CPP_FILES+=base/resolve_path.cpp
@@ -96,24 +96,24 @@ CPP_FILES+=../../wx+/popup_dlg_generated.cpp
CPP_FILES+=../../wx+/zlib_wrap.cpp
CPP_FILES+=../../xBRZ/src/xbrz.cpp
-OBJECT_LIST = $(CPP_FILES:%.cpp=../Obj/FFS_GCC_Make_Release/ffs/src/%.o)
+OBJ_FILES = $(CPP_FILES:%=../Obj/FFS_GCC_Make_Release/ffs/src/%.o)
-all: ../Build/$(APPNAME)
+all: ../Build/Bin/$(APPNAME)
-../Build/$(APPNAME): $(OBJECT_LIST)
+../Build/Bin/$(APPNAME): $(OBJ_FILES)
g++ -o $@ $^ $(LINKFLAGS)
-../Obj/FFS_GCC_Make_Release/ffs/src/%.o : %.cpp
+../Obj/FFS_GCC_Make_Release/ffs/src/%.o : %
mkdir -p $(dir $@)
g++ $(CXXFLAGS) -c $< -o $@
clean:
rm -rf ../Obj/FFS_GCC_Make_Release
- rm -f ../Build/$(APPNAME)
+ rm -f ../Build/Bin/$(APPNAME)
install:
mkdir -p $(BINDIR)
- cp ../Build/$(APPNAME) $(BINDIR)
+ cp ../Build/Bin/$(APPNAME) $(BINDIR)
mkdir -p $(APPSHAREDIR)
cp -R ../Build/Languages/ \
@@ -121,9 +121,9 @@ install:
../Build/gong.wav \
../Build/harp.wav \
../Build/Resources.zip \
- "../Build/User Manual.pdf" \
$(APPSHAREDIR)
mkdir -p $(DOCSHAREDIR)
cp ../../Changelog.txt $(DOCSHAREDIR)/CHANGELOG
+# cp "../Build/User Manual.pdf" $(DOCSHAREDIR)
gzip $(DOCSHAREDIR)/CHANGELOG
diff --git a/FreeFileSync/Source/RealTimeSync/Makefile b/FreeFileSync/Source/RealTimeSync/Makefile
index 900d2d22..d04aeb6f 100755
--- a/FreeFileSync/Source/RealTimeSync/Makefile
+++ b/FreeFileSync/Source/RealTimeSync/Makefile
@@ -37,21 +37,21 @@ CPP_FILES+=../../../wx+/popup_dlg.cpp
CPP_FILES+=../../../wx+/popup_dlg_generated.cpp
CPP_FILES+=../../../xBRZ/src/xbrz.cpp
-OBJECT_LIST=$(CPP_FILES:%.cpp=../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o)
+OBJ_FILES=$(CPP_FILES:%=../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o)
-all: ../../Build/$(APPNAME)
+all: ../../Build/Bin/$(APPNAME)
-../../Build/$(APPNAME): $(OBJECT_LIST)
+../../Build/Bin/$(APPNAME): $(OBJ_FILES)
g++ -o $@ $^ $(LINKFLAGS)
-../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o : %.cpp
+../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o : %
mkdir -p $(dir $@)
g++ $(CXXFLAGS) -c $< -o $@
clean:
rm -rf ../../Obj/RTS_GCC_Make_Release
- rm -f ../../Build/$(APPNAME)
+ rm -f ../../Build/Bin/$(APPNAME)
install:
mkdir -p $(BINDIR)
- cp ../../Build/$(APPNAME) $(BINDIR)
+ cp ../../Build/Bin/$(APPNAME) $(BINDIR)
diff --git a/FreeFileSync/Source/RealTimeSync/application.cpp b/FreeFileSync/Source/RealTimeSync/application.cpp
index 6047468f..af556ace 100755
--- a/FreeFileSync/Source/RealTimeSync/application.cpp
+++ b/FreeFileSync/Source/RealTimeSync/application.cpp
@@ -32,7 +32,6 @@ IMPLEMENT_APP(Application);
namespace
{
-
const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType();
}
@@ -126,8 +125,8 @@ int Application::OnRun()
{
fff::logFatalError(e.what()); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works!
- const auto title = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred");
- wxSafeShowMessage(title, e.what());
+ const auto titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred");
+ std::cerr << utfTo<std::string>(titleFmt + SPACED_DASH) << e.what() << "\n";
return fff::FFS_RC_EXCEPTION;
}
//catch (...) -> let it crash and create mini dump!!!
diff --git a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp
index 298d55b6..200cdbcf 100755
--- a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp
+++ b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp
@@ -36,7 +36,8 @@ void setFolderPath(const Zstring& dirpath, wxTextCtrl* txtCtrl, wxWindow& toolti
tooltipWnd.SetToolTip(utfTo<wxString>(folderPathFmt));
if (staticText) //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway
- staticText->SetLabel(equalLocalPath(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ? wxString(_("Drag && drop")) : utfTo<wxString>(folderPathFmt));
+ staticText->SetLabel(equalNativePath(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ?
+ wxString(_("Drag && drop")) : utfTo<wxString>(folderPathFmt));
}
}
diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
index e4dfcb56..2689955c 100755
--- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
+++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp
@@ -98,10 +98,12 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName)
Zstring currentConfigFile = cfgFileName;
if (currentConfigFile.empty())
- {
- if (!itemNotExisting(lastRunConfigPath_)) //existing/access error? => user should be informed about access errors
- currentConfigFile = lastRunConfigPath_;
- }
+ try
+ {
+ if (itemStillExists(lastRunConfigPath_)) //throw FileError
+ currentConfigFile = lastRunConfigPath_;
+ }
+ catch (FileError&) { currentConfigFile = lastRunConfigPath_; } //access error? => user should be informed
bool loadCfgSuccess = false;
if (!currentConfigFile.empty())
@@ -212,7 +214,7 @@ void MainDialog::OnStart(wxCommandEvent& event)
Hide();
XmlRealConfig currentCfg = getConfiguration();
- const Zstring activeCfgFilePath = !equalLocalPath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring();
+ const Zstring activeCfgFilePath = !equalNativePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring();
switch (runFolderMonitor(currentCfg, ::extractJobName(activeCfgFilePath)))
{
@@ -231,7 +233,7 @@ void MainDialog::OnStart(wxCommandEvent& event)
void MainDialog::OnConfigSave(wxCommandEvent& event)
{
- const Zstring defaultFilePath = !activeConfigFile_.empty() && !equalLocalPath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstr("Realtime.ffs_real");
+ const Zstring defaultFilePath = !activeConfigFile_.empty() && !equalNativePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstr("Realtime.ffs_real");
auto defaultFolder = utfTo<wxString>(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE));
auto defaultFileName = utfTo<wxString>(afterLast (defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL));
@@ -290,7 +292,7 @@ void MainDialog::setLastUsedConfig(const Zstring& filepath)
{
activeConfigFile_ = filepath;
- const Zstring activeCfgFilePath = !equalLocalPath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring();
+ const Zstring activeCfgFilePath = !equalNativePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring();
if (!activeCfgFilePath.empty())
SetTitle(utfTo<wxString>(activeCfgFilePath));
@@ -308,7 +310,7 @@ void MainDialog::OnConfigNew(wxCommandEvent& event)
void MainDialog::OnConfigLoad(wxCommandEvent& event)
{
- const Zstring activeCfgFilePath = !equalLocalPath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring();
+ const Zstring activeCfgFilePath = !equalNativePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring();
wxFileDialog filePicker(this,
wxString(),
diff --git a/FreeFileSync/Source/RealTimeSync/monitor.cpp b/FreeFileSync/Source/RealTimeSync/monitor.cpp
index 8d12080b..647a1114 100755
--- a/FreeFileSync/Source/RealTimeSync/monitor.cpp
+++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp
@@ -22,8 +22,8 @@ const std::chrono::seconds FOLDER_EXISTENCE_CHECK_INTERVAL(1);
//wait until all directories become available (again) + logs in network share
-std::set<Zstring, LessLocalPath> waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw FileError
- const std::function<void(const Zstring& folderPath)>& requestUiRefresh, std::chrono::milliseconds cbInterval)
+std::set<Zstring, LessNativePath> waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw FileError
+ const std::function<void(const Zstring& folderPath)>& requestUiRefresh, std::chrono::milliseconds cbInterval)
{
//early failure! check for unsupported folder paths:
for (const Zstring& protoName : { Zstr("FTP"), Zstr("SFTP"), Zstr("MTP") })
@@ -39,7 +39,7 @@ std::set<Zstring, LessLocalPath> waitForMissingDirs(const std::vector<Zstring>&
Zstring folderPathPhrase;
std::future<bool> folderAvailable;
};
- std::map<Zstring, FolderInfo, LessLocalPath> folderInfos; //folderPath => FolderInfo
+ std::map<Zstring, FolderInfo, LessNativePath> folderInfos; //folderPath => FolderInfo
for (const Zstring& phrase : folderPathPhrases)
{
@@ -50,8 +50,8 @@ std::set<Zstring, LessLocalPath> waitForMissingDirs(const std::vector<Zstring>&
folderInfos[folderPath] = { phrase, runAsync([folderPath]{ return dirAvailable(folderPath); }) };
}
- std::set<Zstring, LessLocalPath> availablePaths;
- std::set<Zstring, LessLocalPath> missingPathPhrases;
+ std::set<Zstring, LessNativePath> availablePaths;
+ std::set<Zstring, LessNativePath> missingPathPhrases;
for (auto& [folderPath, folderInfo] : folderInfos)
{
std::future<bool>& folderAvailable = folderInfo.folderAvailable;
@@ -118,7 +118,7 @@ struct WaitResult
};
-WaitResult waitForChanges(const std::set<Zstring, LessLocalPath>& folderPaths, //throw FileError
+WaitResult waitForChanges(const std::set<Zstring, LessNativePath>& folderPaths, //throw FileError
const std::function<void(bool readyForSync)>& requestUiRefresh, std::chrono::milliseconds cbInterval)
{
assert(std::all_of(folderPaths.begin(), folderPaths.end(), [](const Zstring& folderPath) { return dirAvailable(folderPath); }));
@@ -163,7 +163,7 @@ WaitResult waitForChanges(const std::set<Zstring, LessLocalPath>& folderPaths, /
{
std::vector<DirWatcher::Entry> changedItems = watcher->getChanges([&] { requestUiRefresh(false /*readyForSync*/); /*throw X*/ },
cbInterval); //throw FileError
- erase_if(changedItems, [](const DirWatcher::Entry& e)
+ eraseIf(changedItems, [](const DirWatcher::Entry& e)
{
return
endsWith(e.itemPath, Zstr(".ffs_tmp")) || //sync.8ea2.ffs_tmp
@@ -222,7 +222,7 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, std:
for (;;)
try
{
- std::set<Zstring, LessLocalPath> folderPaths = waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { requestUiRefresh(&folderPath); }, cbInterval); //throw FileError
+ std::set<Zstring, LessNativePath> folderPaths = waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { requestUiRefresh(&folderPath); }, cbInterval); //throw FileError
//schedule initial execution (*after* all directories have arrived)
auto nextExecTime = std::chrono::steady_clock::now() + delay;
diff --git a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
index 667b480d..106c508b 100755
--- a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
+++ b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp
@@ -247,7 +247,7 @@ private:
rts::AbortReason rts::runFolderMonitor(const XmlRealConfig& config, const wxString& jobname)
{
std::vector<Zstring> dirNamesNonFmt = config.directories;
- erase_if(dirNamesNonFmt, [](const Zstring& str) { return trimCpy(str).empty(); }); //remove empty entries WITHOUT formatting paths yet!
+ eraseIf(dirNamesNonFmt, [](const Zstring& str) { return trimCpy(str).empty(); }); //remove empty entries WITHOUT formatting paths yet!
if (dirNamesNonFmt.empty())
{
diff --git a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
index 1e8ca476..4cd8636f 100755
--- a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
+++ b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp
@@ -124,7 +124,7 @@ void rts::readRealOrBatchConfig(const Zstring& filePath, XmlRealConfig& config,
XmlIn in(doc);
//read folder pairs
- std::set<Zstring, LessLocalPath> uniqueFolders;
+ std::set<Zstring, LessNativePath> uniqueFolders;
for (XmlIn inPair = in["FolderPairs"]["Pair"]; inPair; inPair.next())
{
@@ -142,7 +142,7 @@ void rts::readRealOrBatchConfig(const Zstring& filePath, XmlRealConfig& config,
//---------------------------------------------------------------------------------------
- erase_if(uniqueFolders, [](const Zstring& str) { return trimCpy(str).empty(); });
+ eraseIf(uniqueFolders, [](const Zstring& str) { return trimCpy(str).empty(); });
config.directories.assign(uniqueFolders.begin(), uniqueFolders.end());
config.commandline = Zstr('"') + fff::getFreeFileSyncLauncherPath() + Zstr("\" \"") + filePath + Zstr('"');
}
@@ -162,9 +162,9 @@ wxLanguage rts::getProgramLanguage() //throw FileError
}
catch (FileError&)
{
- if (!itemNotExisting(filePath)) //existing or access error
- throw;
- return fff::getSystemLanguage();
+ if (!itemStillExists(filePath)) //throw FileError
+ return fff::getSystemLanguage();
+ throw;
}
if (getXmlTypeNoThrow(doc) != RtsXmlType::GLOBAL) //noexcept
diff --git a/FreeFileSync/Source/base/algorithm.cpp b/FreeFileSync/Source/base/algorithm.cpp
index ff1bc787..e35f0bdf 100755
--- a/FreeFileSync/Source/base/algorithm.cpp
+++ b/FreeFileSync/Source/base/algorithm.cpp
@@ -192,21 +192,19 @@ bool fff::allElementsEqual(const FolderComparison& folderCmp)
namespace
{
template <SelectedSide side> inline
-bool matchesDbEntry(const FilePair& file, const InSyncFolder::FileList::value_type* dbFile, const std::vector<unsigned int>& ignoreTimeShiftMinutes)
+bool matchesDbEntry(const FilePair& file, const InSyncFile* dbFile, const std::vector<unsigned int>& ignoreTimeShiftMinutes)
{
if (file.isEmpty<side>())
return !dbFile;
else if (!dbFile)
return false;
- const Zstring& fileNameDb = dbFile->first;
- const InSyncDescrFile& descrDb = SelectParam<side>::ref(dbFile->second.left, dbFile->second.right);
+ const InSyncDescrFile& descrDb = SelectParam<side>::ref(dbFile->left, dbFile->right);
- return getUnicodeNormalForm(file.getItemName<side>()) == getUnicodeNormalForm(fileNameDb) && //detect changes in case (but ignore Unicode normal forms)
- //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes it's modification date by up to 2 seconds
- //we're not interested in "fileTimeTolerance" here!
- sameFileTime(file.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes) &&
- file.getFileSize<side>() == dbFile->second.fileSize;
+ //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes it's modification date by up to 2 seconds
+ return //we're not interested in "fileTimeTolerance" here!
+ sameFileTime(file.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes) &&
+ file.getFileSize<side>() == dbFile->fileSize;
//note: we do *not* consider FileId here, but are only interested in *visual* changes. Consider user moving data to some other medium, this is not a change!
}
@@ -239,19 +237,17 @@ bool stillInSync(const InSyncFile& dbFile, CompareVariant compareVar, int fileTi
//check whether database entry and current item match: *irrespective* of current comparison settings
template <SelectedSide side> inline
-bool matchesDbEntry(const SymlinkPair& symlink, const InSyncFolder::SymlinkList::value_type* dbSymlink, const std::vector<unsigned int>& ignoreTimeShiftMinutes)
+bool matchesDbEntry(const SymlinkPair& symlink, const InSyncSymlink* dbSymlink, const std::vector<unsigned int>& ignoreTimeShiftMinutes)
{
if (symlink.isEmpty<side>())
return !dbSymlink;
else if (!dbSymlink)
return false;
- const Zstring& linkNameDb = dbSymlink->first;
- const InSyncDescrLink& descrDb = SelectParam<side>::ref(dbSymlink->second.left, dbSymlink->second.right);
+ const InSyncDescrLink& descrDb = SelectParam<side>::ref(dbSymlink->left, dbSymlink->right);
- return getUnicodeNormalForm(symlink.getItemName<side>()) == getUnicodeNormalForm(linkNameDb) &&
- //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes its modification date by up to 2 seconds
- sameFileTime(symlink.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes);
+ //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes its modification date by up to 2 seconds
+ return sameFileTime(symlink.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes);
}
@@ -281,16 +277,10 @@ bool stillInSync(const InSyncSymlink& dbLink, CompareVariant compareVar, int fil
//check whether database entry and current item match: *irrespective* of current comparison settings
template <SelectedSide side> inline
-bool matchesDbEntry(const FolderPair& folder, const InSyncFolder::FolderList::value_type* dbFolder)
+bool matchesDbEntry(const FolderPair& folder, const InSyncFolder* dbFolder)
{
- if (!dbFolder || dbFolder->second.status == InSyncFolder::DIR_STATUS_STRAW_MAN)
- return folder.isEmpty<side>();
- else if (folder.isEmpty<side>())
- return false;
-
- const Zstring& folderNameDb = dbFolder->first;
-
- return getUnicodeNormalForm(folder.getItemName<side>()) == getUnicodeNormalForm(folderNameDb);
+ const bool haveDbEntry = dbFolder && dbFolder->status != InSyncFolder::DIR_STATUS_STRAW_MAN;
+ return haveDbEntry == !folder.isEmpty<side>();
}
@@ -315,22 +305,22 @@ private:
fileTimeTolerance_(baseFolder.getFileTimeTolerance()),
ignoreTimeShiftMinutes_(baseFolder.getIgnoredTimeShift())
{
- recurse(baseFolder, &dbFolder);
+ recurse(baseFolder, &dbFolder, &dbFolder);
if ((!exLeftOnlyById_ .empty() || !exLeftOnlyByPath_ .empty()) &&
(!exRightOnlyById_.empty() || !exRightOnlyByPath_.empty()))
detectMovePairs(dbFolder);
}
- void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolder)
+ void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR)
{
for (FilePair& file : hierObj.refSubFiles())
{
- auto getDbFileEntry = [&]() -> const InSyncFile* //evaluate lazily!
+ auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& fileName) -> const InSyncFile*
{
if (dbFolder)
{
- auto it = dbFolder->files.find(file.getItemNameAny());
+ auto it = dbFolder->files.find(fileName);
if (it != dbFolder->files.end())
return &it->second;
}
@@ -341,8 +331,8 @@ private:
if (cat == FILE_LEFT_SIDE_ONLY)
{
- if (const InSyncFile* dbFile = getDbFileEntry())
- exLeftOnlyByPath_.emplace(dbFile, &file);
+ if (const InSyncFile* dbEntry = getDbEntry(dbFolderL, file.getItemName<LEFT_SIDE>()))
+ exLeftOnlyByPath_.emplace(dbEntry, &file);
else if (!file.getFileId<LEFT_SIDE>().empty())
{
auto rv = exLeftOnlyById_.emplace(file.getFileId<LEFT_SIDE>(), &file);
@@ -352,8 +342,8 @@ private:
}
else if (cat == FILE_RIGHT_SIDE_ONLY)
{
- if (const InSyncFile* dbFile = getDbFileEntry())
- exRightOnlyByPath_.emplace(dbFile, &file);
+ if (const InSyncFile* dbEntry = getDbEntry(dbFolderR, file.getItemName<RIGHT_SIDE>()))
+ exRightOnlyByPath_.emplace(dbEntry, &file);
else if (!file.getFileId<RIGHT_SIDE>().empty())
{
auto rv = exRightOnlyById_.emplace(file.getFileId<RIGHT_SIDE>(), &file);
@@ -365,15 +355,22 @@ private:
for (FolderPair& folder : hierObj.refSubFolders())
{
- const InSyncFolder* dbSubFolder = nullptr; //try to find corresponding database entry
- if (dbFolder)
+ auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& folderName) -> const InSyncFolder*
{
- auto it = dbFolder->folders.find(folder.getItemNameAny());
- if (it != dbFolder->folders.end())
- dbSubFolder = &it->second;
- }
+ if (dbFolder)
+ {
+ auto it = dbFolder->folders.find(folderName);
+ if (it != dbFolder->folders.end())
+ return &it->second;
+ }
+ return nullptr;
+ };
+ const InSyncFolder* dbEntryL = getDbEntry(dbFolderL, folder.getItemName<LEFT_SIDE>());
+ const InSyncFolder* dbEntryR = dbEntryL;
+ if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()))
+ dbEntryR = getDbEntry(dbFolderR, folder.getItemName<RIGHT_SIDE>());
- recurse(folder, dbSubFolder);
+ recurse(folder, dbEntryL, dbEntryR);
}
}
@@ -400,10 +397,10 @@ private:
}
template <SelectedSide side>
- static FilePair* getAssocFilePair(const InSyncFile& dbFile,
- const std::unordered_map<AFS::FileId, FilePair*, StringHash>& exOneSideById,
- const std::unordered_map<const InSyncFile*, FilePair*>& exOneSideByPath)
+ FilePair* getAssocFilePair(const InSyncFile& dbFile) const
{
+ const std::unordered_map<AFS::FileId, FilePair*, StringHash>& exOneSideById = SelectParam<side>::ref(exLeftOnlyById_, exRightOnlyById_);
+ const std::unordered_map<const InSyncFile*, FilePair* >& exOneSideByPath = SelectParam<side>::ref(exLeftOnlyByPath_, exRightOnlyByPath_);
{
auto it = exOneSideByPath.find(&dbFile);
if (it != exOneSideByPath.end())
@@ -426,9 +423,9 @@ private:
void findAndSetMovePair(const InSyncFile& dbFile) const
{
if (stillInSync(dbFile, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))
- if (FilePair* fileLeftOnly = getAssocFilePair<LEFT_SIDE>(dbFile, exLeftOnlyById_, exLeftOnlyByPath_))
+ if (FilePair* fileLeftOnly = getAssocFilePair<LEFT_SIDE>(dbFile))
if (sameSizeAndDate<LEFT_SIDE>(*fileLeftOnly, dbFile))
- if (FilePair* fileRightOnly = getAssocFilePair<RIGHT_SIDE>(dbFile, exRightOnlyById_, exRightOnlyByPath_))
+ if (FilePair* fileRightOnly = getAssocFilePair<RIGHT_SIDE>(dbFile))
if (sameSizeAndDate<RIGHT_SIDE>(*fileRightOnly, dbFile))
if (fileLeftOnly ->getMoveRef() == nullptr && //don't let a row participate in two move pairs!
fileRightOnly->getMoveRef() == nullptr) //
@@ -489,22 +486,22 @@ private:
ignoreTimeShiftMinutes_(baseFolder.getIgnoredTimeShift())
{
//-> considering filter not relevant:
- //if narrowing filter: all ok; if widening filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine)
-
- recurse(baseFolder, &dbFolder);
+ // if stricter filter than last time: all ok;
+ // if less strict filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine)
+ recurse(baseFolder, &dbFolder, &dbFolder);
}
- void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolder) const
+ void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) const
{
for (FilePair& file : hierObj.refSubFiles())
- processFile(file, dbFolder);
+ processFile(file, dbFolderL, dbFolderR);
for (SymlinkPair& link : hierObj.refSubLinks())
- processSymlink(link, dbFolder);
+ processSymlink(link, dbFolderL, dbFolderR);
for (FolderPair& folder : hierObj.refSubFolders())
- processDir(folder, dbFolder);
+ processDir(folder, dbFolderL, dbFolderR);
}
- void processFile(FilePair& file, const InSyncFolder* dbFolder) const
+ void processFile(FilePair& file, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) const
{
const CompareFilesResult cat = file.getCategory();
if (cat == FILE_EQUAL)
@@ -518,72 +515,88 @@ private:
//####################################################################################
//try to find corresponding database entry
- const InSyncFolder::FileList::value_type* dbEntry = nullptr;
- if (dbFolder)
+ auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& fileName) -> const InSyncFile*
{
- auto it = dbFolder->files.find(file.getItemNameAny());
- if (it != dbFolder->files.end())
- dbEntry = &*it;
- }
+ if (dbFolder)
+ {
+ auto it = dbFolder->files.find(fileName);
+ if (it != dbFolder->files.end())
+ return &it->second;
+ }
+ return nullptr;
+ };
+ const InSyncFile* dbEntryL = getDbEntry(dbFolderL, file.getItemName<LEFT_SIDE>());
+ const InSyncFile* dbEntryR = dbEntryL;
+ if (dbFolderL != dbFolderR || getUnicodeNormalForm(file.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(file.getItemName<RIGHT_SIDE>()))
+ dbEntryR = getDbEntry(dbFolderR, file.getItemName<RIGHT_SIDE>());
//evaluation
- const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes_);
- const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes_);
+ const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntryL, ignoreTimeShiftMinutes_);
+ const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(file, dbEntryR, ignoreTimeShiftMinutes_);
if (changeOnLeft != changeOnRight)
{
//if database entry not in sync according to current settings! -> set direction based on sync status only!
- if (dbEntry && !stillInSync(dbEntry->second, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))
- file.setSyncDirConflict(txtDbNotInSync);
+ if ((dbEntryL && !stillInSync(*dbEntryL, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)) ||
+ (dbEntryR && !stillInSync(*dbEntryR, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)))
+ file.setSyncDirConflict(txtDbNotInSync_);
else
file.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT);
}
else
{
if (changeOnLeft)
- file.setSyncDirConflict(txtBothSidesChanged);
+ file.setSyncDirConflict(txtBothSidesChanged_);
else
- file.setSyncDirConflict(txtNoSideChanged);
+ file.setSyncDirConflict(txtNoSideChanged_);
}
}
- void processSymlink(SymlinkPair& symlink, const InSyncFolder* dbFolder) const
+ void processSymlink(SymlinkPair& symlink, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) const
{
const CompareSymlinkResult cat = symlink.getLinkCategory();
if (cat == SYMLINK_EQUAL)
return;
//try to find corresponding database entry
- const InSyncFolder::SymlinkList::value_type* dbEntry = nullptr;
- if (dbFolder)
+ auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& linkName) -> const InSyncSymlink*
{
- auto it = dbFolder->symlinks.find(symlink.getItemNameAny());
- if (it != dbFolder->symlinks.end())
- dbEntry = &*it;
- }
+ if (dbFolder)
+ {
+ auto it = dbFolder->symlinks.find(linkName);
+ if (it != dbFolder->symlinks.end())
+ return &it->second;
+ }
+ return nullptr;
+ };
+ const InSyncSymlink* dbEntryL = getDbEntry(dbFolderL, symlink.getItemName<LEFT_SIDE>());
+ const InSyncSymlink* dbEntryR = dbEntryL;
+ if (dbFolderL != dbFolderR || getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()))
+ dbEntryR = getDbEntry(dbFolderR, symlink.getItemName<RIGHT_SIDE>());
//evaluation
- const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes_);
- const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes_);
+ const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntryL, ignoreTimeShiftMinutes_);
+ const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(symlink, dbEntryR, ignoreTimeShiftMinutes_);
if (changeOnLeft != changeOnRight)
{
//if database entry not in sync according to current settings! -> set direction based on sync status only!
- if (dbEntry && !stillInSync(dbEntry->second, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))
- symlink.setSyncDirConflict(txtDbNotInSync);
+ if ((dbEntryL && !stillInSync(*dbEntryL, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)) ||
+ (dbEntryR && !stillInSync(*dbEntryR, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)))
+ symlink.setSyncDirConflict(txtDbNotInSync_);
else
symlink.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT);
}
else
{
if (changeOnLeft)
- symlink.setSyncDirConflict(txtBothSidesChanged);
+ symlink.setSyncDirConflict(txtBothSidesChanged_);
else
- symlink.setSyncDirConflict(txtNoSideChanged);
+ symlink.setSyncDirConflict(txtNoSideChanged_);
}
}
- void processDir(FolderPair& folder, const InSyncFolder* dbFolder) const
+ void processDir(FolderPair& folder, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) const
{
const CompareDirResult cat = folder.getDirCategory();
@@ -595,43 +608,51 @@ private:
//#######################################################################################
//try to find corresponding database entry
- const InSyncFolder::FolderList::value_type* dbEntry = nullptr;
- if (dbFolder)
+ auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& folderName) -> const InSyncFolder*
{
- auto it = dbFolder->folders.find(folder.getItemNameAny());
- if (it != dbFolder->folders.end())
- dbEntry = &*it;
- }
+ if (dbFolder)
+ {
+ auto it = dbFolder->folders.find(folderName);
+ if (it != dbFolder->folders.end())
+ return &it->second;
+ }
+ return nullptr;
+ };
+ const InSyncFolder* dbEntryL = getDbEntry(dbFolderL, folder.getItemName<LEFT_SIDE>());
+ const InSyncFolder* dbEntryR = dbEntryL;
+ if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()))
+ dbEntryR = getDbEntry(dbFolderR, folder.getItemName<RIGHT_SIDE>());
if (cat != DIR_EQUAL)
{
//evaluation
- const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(folder, dbEntry);
- const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(folder, dbEntry);
+ const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(folder, dbEntryL);
+ const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(folder, dbEntryR);
if (changeOnLeft != changeOnRight)
{
//if database entry not in sync according to current settings! -> set direction based on sync status only!
- if (dbEntry && !stillInSync(dbEntry->second))
- folder.setSyncDirConflict(txtDbNotInSync);
+ if ((dbEntryL && !stillInSync(*dbEntryL)) ||
+ (dbEntryR && !stillInSync(*dbEntryR)))
+ folder.setSyncDirConflict(txtDbNotInSync_);
else
folder.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT);
}
else
{
if (changeOnLeft)
- folder.setSyncDirConflict(txtBothSidesChanged);
+ folder.setSyncDirConflict(txtBothSidesChanged_);
else
- folder.setSyncDirConflict(txtNoSideChanged);
+ folder.setSyncDirConflict(txtNoSideChanged_);
}
}
- recurse(folder, dbEntry ? &dbEntry->second : nullptr);
+ recurse(folder, dbEntryL, dbEntryR);
}
- const std::wstring txtBothSidesChanged = _("Both sides have changed since last synchronization.");
- const std::wstring txtNoSideChanged = _("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.");
- const std::wstring txtDbNotInSync = _("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.");
+ const std::wstring txtBothSidesChanged_ = _("Both sides have changed since last synchronization.");
+ const std::wstring txtNoSideChanged_ = _("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization.");
+ const std::wstring txtDbNotInSync_ = _("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings.");
const CompareVariant cmpVar_;
const int fileTimeTolerance_;
@@ -858,10 +879,10 @@ template <FilterStrategy strategy>
class ApplyHardFilter
{
public:
- static void execute(ContainerObject& hierObj, const HardFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); }
+ static void execute(ContainerObject& hierObj, const PathFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); }
private:
- ApplyHardFilter(ContainerObject& hierObj, const HardFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); }
+ ApplyHardFilter(ContainerObject& hierObj, const PathFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); }
void recurse(ContainerObject& hierObj) const
{
@@ -902,7 +923,7 @@ private:
recurse(folder);
}
- const HardFilter& filterProc;
+ const PathFilter& filterProc;
};
@@ -1033,7 +1054,7 @@ void fff::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& m
const NormalizedFilter normFilter = normalizeFilters(mainCfg.globalFilter, it->localFilter);
//"set" hard filter
- ApplyHardFilter<STRATEGY_SET>::execute(baseFolder, *normFilter.nameFilter);
+ ApplyHardFilter<STRATEGY_SET>::execute(baseFolder, normFilter.nameFilter.ref());
//"and" soft filter
addSoftFiltering(baseFolder, normFilter.timeSizeFilter);
@@ -1109,15 +1130,15 @@ void fff::applyTimeSpanFilter(FolderComparison& folderCmp, time_t timeFrom, time
}
-std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL,
- const AbstractPath& basePathR, const HardFilter& filterR)
+std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const PathFilter& filterL,
+ const AbstractPath& basePathR, const PathFilter& filterR)
{
if (!AFS::isNullPath(basePathL) && !AFS::isNullPath(basePathR))
{
- if (AFS::getRootPath(basePathL) == AFS::getRootPath(basePathR))
+ if (basePathL.afsDevice == basePathR.afsDevice)
{
- const std::vector<Zstring> relPathL = split(AFS::getRootRelativePath(basePathL), FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY);
- const std::vector<Zstring> relPathR = split(AFS::getRootRelativePath(basePathR), FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY);
+ const std::vector<Zstring> relPathL = split(basePathL.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY);
+ const std::vector<Zstring> relPathR = split(basePathR.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY);
const bool leftParent = relPathL.size() <= relPathR.size();
@@ -1129,12 +1150,12 @@ std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePat
Zstring relDirPath;
std::for_each(relPathC.begin() + relPathP.size(), relPathC.end(), [&](const Zstring& itemName)
{
- relDirPath = AFS::appendPaths(relDirPath, itemName, FILE_NAME_SEPARATOR);
+ relDirPath = nativeAppendPaths(relDirPath, itemName);
});
const AbstractPath& basePathP = leftParent ? basePathL : basePathR;
const AbstractPath& basePathC = leftParent ? basePathR : basePathL;
- const HardFilter& filterP = leftParent ? filterL : filterR;
+ const PathFilter& filterP = leftParent ? filterL : filterR;
//if there's a dependency, check if the sub directory is (fully) excluded via filter
//=> easy to check but still insufficient in general:
// - one folder may have a *.txt include-filter, the other a *.lng include filter => no dependencies, but "childItemMightMatch = true" below!
@@ -1150,10 +1171,10 @@ std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePat
//############################################################################################################
-std::pair<std::wstring, int> fff::getSelectedItemsAsString(const std::vector<const FileSystemObject*>& selectionLeft,
- const std::vector<const FileSystemObject*>& selectionRight)
+std::pair<std::wstring, int> fff::getSelectedItemsAsString(std::span<const FileSystemObject* const> selectionLeft,
+ std::span<const FileSystemObject* const> selectionRight)
{
- //don't use wxString! its rather dumb linear allocation strategy brings perf down to a crawl!
+ //don't use wxString! its dumb linear allocation strategy brings perf down to a crawl!
std::wstring fileList; //
int totalDelCount = 0;
@@ -1212,33 +1233,22 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
}
catch (FileError&)
{
- const AFS::PathStatus ps = AFS::getPathStatus(targetPath); //throw FileError
-
- if (ps.relPath.empty()) //already existing
+ try
{
+ AFS::getItemType(targetPath); //throw FileError
+ //already existing! =>
if (deletionError)
std::rethrow_exception(deletionError);
+ throw;
}
- else if (ps.relPath.size() > 1) //parent folder missing
- {
- //notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(*AFS::getParentFolderPath(targetPath))); -> useful?
+ catch (FileError&) {} //not yet existing or access error
- AbstractPath intermediatePath = ps.existingPath;
- std::for_each(ps.relPath.begin(), ps.relPath.end() - 1, [&](const Zstring& itemName)
- {
- AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
- statReporter.reportDelta(1, 0);
- callback.requestUiRefresh(); //throw X
- });
- //potential future issue when adding multithreading support: intermediate folders might already exist
- //potential future issue 2: folder created by parallel thread just after failure => ps->relPath.size() == 1, but need retry!
- //see abstract.cpp; AFS::createFolderIfMissingRecursion()
+ if (const std::optional<AbstractPath> targetParentPath = AFS::getParentPath(targetPath))
+ AFS::createFolderIfMissingRecursion(*targetParentPath); //throw FileError
- //retry:
- copyItemPlain(nullptr /*deleteTargetItem*/); //throw FileError
- return;
- }
- throw;
+ //retry:
+ copyItemPlain(nullptr /*deleteTargetItem*/); //throw FileError
+ return;
}
};
@@ -1253,34 +1263,10 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
{
ItemStatReporter<> statReporter(1, 0, callback);
notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(targetPath));
- try
- {
- //target existing: undefined behavior! (fail/overwrite)
- AFS::createFolderPlain(targetPath); //throw FileError
- statReporter.reportDelta(1, 0);
- }
- catch (FileError&)
- {
- const AFS::PathStatus ps = AFS::getPathStatus(targetPath); //throw FileError
- if (ps.existingType == AFS::ItemType::FILE)
- throw;
-
- if (ps.relPath.size() == 1) //don't repeat the very same createFolderPlain() call from above!
- throw;
- //folder might already exist: see creation of intermediate directories below
-
- AbstractPath intermediatePath = ps.existingPath;
- for (const Zstring& itemName : ps.relPath)
- {
- AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
- statReporter.reportDelta(1, 0);
- callback.requestUiRefresh(); //throw X
- }
- //potential future issue when adding multithreading support: intermediate folders might already exist
- //potential future issue 2: parent folder created by parallel thread just after failure => ps->relPath.size() == 1, but need retry!
- //see abstract.cpp; AFS::createFolderIfMissingRecursion()
- }
+ AFS::createFolderIfMissingRecursion(targetPath); //throw FileError
+ statReporter.reportDelta(1, 0);
+ //folder might already exist: see creation of intermediate directories below
},
[&](const FilePair& file)
@@ -1325,18 +1311,18 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT
}
-void fff::copyToAlternateFolder(const std::vector<const FileSystemObject*>& rowsToCopyOnLeft,
- const std::vector<const FileSystemObject*>& rowsToCopyOnRight,
+void fff::copyToAlternateFolder(std::span<const FileSystemObject* const> rowsToCopyOnLeft,
+ std::span<const FileSystemObject* const> rowsToCopyOnRight,
const Zstring& targetFolderPathPhrase,
bool keepRelPaths,
bool overwriteIfExists,
WarningDialogs& warnings,
ProcessCallback& callback)
{
- std::vector<const FileSystemObject*> itemSelectionLeft = rowsToCopyOnLeft;
- std::vector<const FileSystemObject*> itemSelectionRight = rowsToCopyOnRight;
- erase_if(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); });
- erase_if(itemSelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); });
+ std::vector<const FileSystemObject*> itemSelectionLeft (rowsToCopyOnLeft .begin(), rowsToCopyOnLeft .end());
+ std::vector<const FileSystemObject*> itemSelectionRight(rowsToCopyOnRight.begin(), rowsToCopyOnRight.end());
+ eraseIf(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed for correct stats!
+ eraseIf(itemSelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); //
const int itemTotal = static_cast<int>(itemSelectionLeft.size() + itemSelectionRight.size());
int64_t bytesTotal = 0;
@@ -1514,8 +1500,8 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete
std::vector<FileSystemObject*> deleteLeft = rowsToDeleteOnLeft;
std::vector<FileSystemObject*> deleteRight = rowsToDeleteOnRight;
- erase_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed?
- erase_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); //yes, for correct stats:
+ eraseIf(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed?
+ eraseIf(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); //yes, for correct stats:
const int itemCount = static_cast<int>(deleteLeft.size() + deleteRight.size());
callback.initNewPhase(itemCount, 0, ProcessCallback::PHASE_SYNCHRONIZING); //throw X
@@ -1601,13 +1587,16 @@ bool fff::operator<(const FileDescriptor& lhs, const FileDescriptor& rhs)
if (lhs.attr.fileSize != rhs.attr.fileSize)
return lhs.attr.fileSize < rhs.attr.fileSize;
- if (lhs.attr.fileId != rhs.attr.fileId)
- return lhs.attr.fileId < rhs.attr.fileId;
-
if (lhs.attr.isFollowedSymlink != rhs.attr.isFollowedSymlink)
return lhs.attr.isFollowedSymlink < rhs.attr.isFollowedSymlink;
- return lhs.path < rhs.path;
+ if (lhs.attr.fileId != rhs.attr.fileId)
+ return lhs.attr.fileId < rhs.attr.fileId;
+
+ if (!lhs.attr.fileId.empty())
+ return false; //when (non-empty) file IDs match we don't have to check the path => pre-mature optimization?
+ else
+ return lhs.path < rhs.path;
}
@@ -1678,7 +1667,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P
const Zstring fileName = AFS::getItemName(descr.path);
- auto it = find_last(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "."
+ auto it = findLast(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "."
const Zstring tempFileName = Zstring(fileName.begin(), it) + Zstr('-') + descrHash + Zstring(it, fileName.end());
const Zstring tempFilePath = appendSeparator(tempFolderPath_) + tempFileName;
diff --git a/FreeFileSync/Source/base/algorithm.h b/FreeFileSync/Source/base/algorithm.h
index a8facb9d..f7b2cc15 100755
--- a/FreeFileSync/Source/base/algorithm.h
+++ b/FreeFileSync/Source/base/algorithm.h
@@ -48,16 +48,16 @@ struct PathDependency
AbstractPath basePathChild;
Zstring relPath; //filled if child path is subfolder of parent path; empty if child path == parent path
};
-std::optional<PathDependency> getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL,
- const AbstractPath& basePathR, const HardFilter& filterR);
+std::optional<PathDependency> getPathDependency(const AbstractPath& basePathL, const PathFilter& filterL,
+ const AbstractPath& basePathR, const PathFilter& filterR);
std::pair<std::wstring, int> getSelectedItemsAsString( //returns string with item names and total count of selected(!) items, NOT total files/dirs!
- const std::vector<const FileSystemObject*>& selectionLeft, //all pointers need to be bound!
- const std::vector<const FileSystemObject*>& selectionRight); //
+ std::span<const FileSystemObject* const> selectionLeft, //all pointers need to be bound!
+ std::span<const FileSystemObject* const> selectionRight); //
//manual copy to alternate folder:
-void copyToAlternateFolder(const std::vector<const FileSystemObject*>& rowsToCopyOnLeft, //all pointers need to be bound!
- const std::vector<const FileSystemObject*>& rowsToCopyOnRight, //
+void copyToAlternateFolder(std::span<const FileSystemObject* const> rowsToCopyOnLeft, //all pointers need to be bound!
+ std::span<const FileSystemObject* const> rowsToCopyOnRight, //
const Zstring& targetFolderPathPhrase,
bool keepRelPaths,
bool overwriteIfExists,
diff --git a/FreeFileSync/Source/base/application.cpp b/FreeFileSync/Source/base/application.cpp
index 4707fd98..fc07e808 100755
--- a/FreeFileSync/Source/base/application.cpp
+++ b/FreeFileSync/Source/base/application.cpp
@@ -13,6 +13,7 @@
#include <wx+/app_main.h>
#include <wx+/popup_dlg.h>
#include <wx+/image_resources.h>
+#include <wx/msgdlg.h>
#include "comparison.h"
#include "algorithm.h"
#include "synchronization.h"
@@ -55,8 +56,8 @@ bool Application::OnInit()
//do not call wxApp::OnInit() to avoid using wxWidgets command line parser
::gtk_init(nullptr, nullptr);
- //::gtk_rc_parse((getResourceDirPf() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons
- //=> looks bad on Suse Linux!
+ ::gtk_rc_parse((getResourceDirPf() + "styles.gtk_rc").c_str()); //remove excessive inner border from bitmap buttons
+
//Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise:
wxToolTip::Enable(true); //yawn, a wxWidgets screw-up: wxToolTip::SetAutoPop is no-op if global tooltip window is not yet constructed: wxToolTip::Enable creates it
@@ -115,8 +116,8 @@ int Application::OnRun()
{
logFatalError(e.what()); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works!
- const auto title = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred");
- wxSafeShowMessage(title, e.what());
+ const auto titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred");
+ std::cerr << utfTo<std::string>(titleFmt + SPACED_DASH) << e.what() << "\n";
return FFS_RC_EXCEPTION;
}
//catch (...) -> let it crash and create mini dump!!!
@@ -151,10 +152,12 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
{
logFatalError(utfTo<std::string>(msg));
- //error handling strategy unknown and no sync log output available at this point! => show message box
+ //error handling strategy unknown and no sync log output available at this point!
auto titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + title;
- wxSafeShowMessage(titleFmt, msg);
-
+ std::cerr << utfTo<std::string>(titleFmt + SPACED_DASH + msg) << "\n";
+ //alternative0: std::wcerr: cannot display non-ASCII at all, so why does it exist???
+ //alternative1: wxSafeShowMessage => NO console output on Debian x86, WTF!
+ //alternative2: wxMessageBox() => works, but we probably shouldn't block during command line usage
raiseReturnCode(returnCode_, FFS_RC_ABORTED);
};
@@ -202,35 +205,23 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
else if (equalAsciiNoCase(*it, optionLeftDir))
{
if (++it == commandArgs.end() || isCommandLineOption(*it))
- {
- notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionLeftDir)), _("Syntax error"));
- return;
- }
+ return notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionLeftDir)), _("Syntax error"));
dirPathPhrasesLeft.push_back(*it);
}
else if (equalAsciiNoCase(*it, optionRightDir))
{
if (++it == commandArgs.end() || isCommandLineOption(*it))
- {
- notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionRightDir)), _("Syntax error"));
- return;
- }
+ return notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionRightDir)), _("Syntax error"));
dirPathPhrasesRight.push_back(*it);
}
else if (equalAsciiNoCase(*it, optionDirPair))
{
if (++it == commandArgs.end() || isCommandLineOption(*it))
- {
- notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error"));
- return;
- }
+ return notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error"));
dirPathPhrasePairs.emplace_back(*it, Zstring());
if (++it == commandArgs.end() || isCommandLineOption(*it))
- {
- notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error"));
- return;
- }
+ return notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error"));
dirPathPhrasePairs.back().second = *it;
}
else if (equalAsciiNoCase(*it, optionSendTo))
@@ -283,10 +274,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
else if (fileAvailable(filePath + Zstr(".xml")))
filePath += Zstr(".xml");
else
- {
- notifyFatalError(replaceCpy(_("Cannot find file %x."), L"%x", fmtPath(filePath)), _("Error"));
- return;
- }
+ return notifyFatalError(replaceCpy(_("Cannot find file %x."), L"%x", fmtPath(filePath)), _("Error"));
}
try
@@ -303,22 +291,17 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
globalConfigFile = filePath;
break;
case XML_TYPE_OTHER:
- notifyFatalError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath)), _("Error"));
- return;
+ return notifyFatalError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath)), _("Error"));
}
}
catch (const FileError& e)
{
- notifyFatalError(e.toString(), _("Error"));
- return;
+ return notifyFatalError(e.toString(), _("Error"));
}
}
if (dirPathPhrasesLeft.size() != dirPathPhrasesRight.size())
- {
- notifyFatalError(_("Unequal number of left and right directories specified."), _("Syntax error"));
- return;
- }
+ return notifyFatalError(_("Unequal number of left and right directories specified."), _("Syntax error"));
for (size_t i = 0; i < dirPathPhrasesLeft.size(); ++i)
dirPathPhrasePairs.emplace_back(dirPathPhrasesLeft[i], dirPathPhrasesRight[i]);
@@ -395,8 +378,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
}
catch (const FileError& e)
{
- notifyFatalError(e.toString(), _("Error"));
- return;
+ return notifyFatalError(e.toString(), _("Error"));
}
if (!replaceDirectories(batchCfg.mainCfg))
return;
@@ -417,8 +399,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
}
catch (const FileError& e)
{
- notifyFatalError(e.toString(), _("Error"));
- return;
+ return notifyFatalError(e.toString(), _("Error"));
}
if (!replaceDirectories(guiCfg.mainCfg))
return;
@@ -432,10 +413,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
else
{
if (!dirPathPhrasePairs.empty())
- {
- notifyFatalError(_("Directories cannot be set for more than one configuration file."), _("Syntax error"));
- return;
- }
+ return notifyFatalError(_("Directories cannot be set for more than one configuration file."), _("Syntax error"));
std::vector<Zstring> filePaths;
for (const auto& [filePath, xmlType] : configFiles)
@@ -453,8 +431,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs)
}
catch (const FileError& e)
{
- notifyFatalError(e.toString(), _("Error"));
- return;
+ return notifyFatalError(e.toString(), _("Error"));
}
runGuiMode(globalConfigFilePath, guiCfg, filePaths, !openForEdit /*startComparison*/);
}
@@ -518,13 +495,19 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
{
std::wstring warningMsg;
readConfig(globalConfigFilePath, globalCfg, warningMsg); //throw FileError
-
assert(warningMsg.empty()); //ignore parsing errors: should be migration problems only *cross-fingers*
}
- catch (const FileError& e)
+ catch (FileError&)
{
- if (!itemNotExisting(globalConfigFilePath)) //existing or access error
+ try
+ {
+ if (itemStillExists(globalConfigFilePath)) //throw FileError
+ throw;
+ }
+ catch (const FileError& e)
+ {
return notifyError(e.toString(), FFS_RC_ABORTED); //abort sync!
+ }
}
try
@@ -544,7 +527,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
// checkForUpdatePeriodically(globalCfg.lastUpdateCheck);
//WinInet not working when FFS is running as a service!!! https://support.microsoft.com/en-us/kb/238425
- const std::map<AbstractPath, size_t>& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps;
+ const std::map<AfsDevice, size_t>& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps;
std::set<AbstractPath> logFilePathsToKeep;
for (const ConfigFileItem& item : globalCfg.gui.mainDlg.cfgFileHistory)
@@ -605,7 +588,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat
//update last sync stats for the selected cfg file
for (ConfigFileItem& cfi : globalCfg.gui.mainDlg.cfgFileHistory)
- if (equalLocalPath(cfi.cfgFilePath, cfgFilePath))
+ if (equalNativePath(cfi.cfgFilePath, cfgFilePath))
{
if (r.finalStatus != SyncResult::ABORTED)
cfi.lastSyncTime = std::chrono::system_clock::to_time_t(syncStartTime);
diff --git a/FreeFileSync/Source/base/comparison.cpp b/FreeFileSync/Source/base/comparison.cpp
index ce601ac8..26c33dca 100755
--- a/FreeFileSync/Source/base/comparison.cpp
+++ b/FreeFileSync/Source/base/comparison.cpp
@@ -34,9 +34,9 @@ std::vector<FolderPairCfg> fff::extractCompareCfg(const MainConfiguration& mainC
const SyncConfig syncCfg = lpc.localSyncCfg ? *lpc.localSyncCfg : mainCfg.syncCfg;
NormalizedFilter filter = normalizeFilters(mainCfg.globalFilter, lpc.localFilter);
- //exclude the database file(s) sync.ffs_db, sync.x64.ffs_db, etc. and lock files
+ //exclude sync.ffs_db and lock files
//=> can't put inside fff::parallelDeviceTraversal() which is also used by versioning
- filter.nameFilter = filter.nameFilter->copyFilterAddingExclusion(Zstring(Zstr("*")) + SYNC_DB_FILE_ENDING + Zstr("\n*") + LOCK_FILE_ENDING);
+ filter.nameFilter = filter.nameFilter.ref().copyFilterAddingExclusion(Zstring(Zstr("*")) + SYNC_DB_FILE_ENDING + Zstr("\n*") + LOCK_FILE_ENDING);
output.push_back(
{
@@ -68,7 +68,7 @@ struct ResolvedBaseFolders
};
-ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCfgList, const std::map<AbstractPath, size_t>& deviceParallelOps,
+ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCfgList, const std::map<AfsDevice, size_t>& deviceParallelOps,
bool allowUserInteraction,
bool& warnFolderNotExisting,
ProcessCallback& callback /*throw X*/)
@@ -78,25 +78,31 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCf
tryReportingError([&]
{
- std::set<AbstractPath> uniqueBaseFolders;
-
//support "retry" for environment variable and variable driver letter resolution!
- output.resolvedPairs.clear();
+ std::set<AbstractPath> baseFolders;
+ std::vector<std::pair<AbstractPath, AbstractPath>> folderPairs;
+
for (const FolderPairCfg& fpCfg : fpCfgList)
{
- AbstractPath folderPathLeft = createAbstractPath(fpCfg.folderPathPhraseLeft_);
- AbstractPath folderPathRight = createAbstractPath(fpCfg.folderPathPhraseRight_);
+ folderPairs.emplace_back(createAbstractPath(fpCfg.folderPathPhraseLeft_),
+ createAbstractPath(fpCfg.folderPathPhraseRight_));
- uniqueBaseFolders.insert(folderPathLeft);
- uniqueBaseFolders.insert(folderPathRight);
-
- output.resolvedPairs.push_back({ folderPathLeft, folderPathRight });
+ baseFolders.insert(folderPairs.back().first);
+ baseFolders.insert(folderPairs.back().second);
}
- const FolderStatus status = getFolderStatusNonBlocking(uniqueBaseFolders, deviceParallelOps, //re-check *all* directories on each try!
+ const FolderStatus status = getFolderStatusNonBlocking(baseFolders, deviceParallelOps, //re-check *all* directories on each try!
allowUserInteraction, callback); //throw X
- output.existingBaseFolders = status.existing;
- notExisting = status.notExisting;
+ output.resolvedPairs.clear();
+ for (const auto& [folderPathL, folderPathR] : folderPairs)
+ output.resolvedPairs.push_back({ getNormalizedPath(status, folderPathL), getNormalizedPath(status, folderPathR)});
+
+ output.existingBaseFolders.clear();
+ for (const AbstractPath& folderPath : status.existing)
+ output.existingBaseFolders.insert(getNormalizedPath(status, folderPath));
+
+ notExisting = status.notExisting;
+
if (!status.failedChecks.empty())
{
std::wstring msg = _("Cannot find the following folders:") + L"\n";
@@ -134,7 +140,7 @@ class ComparisonBuffer
{
public:
ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
int fileTimeTolerance,
ProcessCallback& callback);
@@ -155,12 +161,12 @@ private:
std::map<DirectoryKey, DirectoryValue> directoryBuffer_; //contains only *existing* directories
const int fileTimeTolerance_;
ProcessCallback& cb_;
- const std::map<AbstractPath, size_t> deviceParallelOps_;
+ const std::map<AfsDevice, size_t> deviceParallelOps_;
};
ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
int fileTimeTolerance,
ProcessCallback& callback) :
fileTimeTolerance_(fileTimeTolerance), cb_(callback), deviceParallelOps_(deviceParallelOps)
@@ -248,6 +254,12 @@ Zstringw getDescrDiffMetaDate(const FileOrLinkPair& file)
arrowRight + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.template getLastWriteTime<RIGHT_SIDE>()));
}
+
+Zstringw getConflictAmbiguousItemName(const Zstring& itemName)
+{
+ return copyStringTo<Zstringw>(replaceCpy(_("The name %x is used by more than one item in the folder."), L"%x", fmtPath(itemName)));
+}
+
//-----------------------------------------------------------------------------
void categorizeSymlinkByTime(SymlinkPair& symlink)
@@ -261,7 +273,7 @@ void categorizeSymlinkByTime(SymlinkPair& symlink)
//1. SYMLINK_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp
//2. harmonize with "bool stillInSync()" in algorithm.cpp
- if (getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE >()) ==
+ if (getUnicodeNormalForm(symlink.getItemName< LEFT_SIDE>()) ==
getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()))
symlink.setCategory<FILE_EQUAL>();
else
@@ -311,7 +323,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const Resolv
//3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
if (file->getFileSize<LEFT_SIDE>() == file->getFileSize<RIGHT_SIDE>())
{
- if (getUnicodeNormalForm(file->getItemName<LEFT_SIDE >()) ==
+ if (getUnicodeNormalForm(file->getItemName< LEFT_SIDE>()) ==
getUnicodeNormalForm(file->getItemName<RIGHT_SIDE>()))
file->setCategory<FILE_EQUAL>();
else
@@ -369,7 +381,7 @@ void categorizeSymlinkByContent(SymlinkPair& symlink, ProcessCallback& callback)
//2. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h
//symlinks have same "content"
- if (getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE >()) !=
+ if (getUnicodeNormalForm(symlink.getItemName< LEFT_SIDE>()) !=
getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()))
symlink.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(symlink));
//else if (!sameFileTime(symlink.getLastWriteTime<LEFT_SIDE>(),
@@ -450,7 +462,7 @@ void categorizeFileByContent(FilePair& file, const std::wstring& txtComparingCon
interruptionPoint(); //throw ThreadInterruption
};
- haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath<LEFT_SIDE >(),
+ haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath< LEFT_SIDE>(),
file.getAbstractPath<RIGHT_SIDE>(), notifyUnbufferedIO, singleThread); //throw FileError
statReporter.reportDelta(1, 0);
}, acb); //throw ThreadInterruption
@@ -491,7 +503,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
size_t effectiveMax = 0; //a folder pair is allowed to use the maximum parallelOps that left/right devices support
// => consider max over all folder pairs, that a device is involved with!
};
- std::map<AbstractPath, ParallelOps> parallelOpsStatus;
+ std::map<AfsDevice, ParallelOps> parallelOpsStatus;
struct BinaryWorkload
{
@@ -503,15 +515,12 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
auto addToBinaryWorkload = [&](const AbstractPath& basePathL, const AbstractPath& basePathR, RingBuffer<FilePair*>&& filesToCompareBytewise)
{
- const AbstractPath rootPathL = AFS::getRootPath(basePathL);
- const AbstractPath rootPathR = AFS::getRootPath(basePathR);
-
//calculate effective max parallelOps that devices must support
- const size_t parallelOpsFp = std::max(getDeviceParallelOps(deviceParallelOps_, rootPathL),
- getDeviceParallelOps(deviceParallelOps_, rootPathR));
+ const size_t parallelOpsFp = std::max(getDeviceParallelOps(deviceParallelOps_, basePathL.afsDevice),
+ getDeviceParallelOps(deviceParallelOps_, basePathR.afsDevice));
- ParallelOps& posL = parallelOpsStatus[rootPathL];
- ParallelOps& posR = parallelOpsStatus[rootPathR];
+ ParallelOps& posL = parallelOpsStatus[basePathL.afsDevice];
+ ParallelOps& posR = parallelOpsStatus[basePathR.afsDevice];
posL.effectiveMax = std::max(posL.effectiveMax, parallelOpsFp);
posR.effectiveMax = std::max(posR.effectiveMax, parallelOpsFp);
@@ -548,7 +557,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
filesToCompareBytewise.push_back(file);
}
if (!filesToCompareBytewise.empty())
- addToBinaryWorkload(output.back()->getAbstractPath<LEFT_SIDE >(),
+ addToBinaryWorkload(output.back()->getAbstractPath< LEFT_SIDE>(),
output.back()->getAbstractPath<RIGHT_SIDE>(), std::move(filesToCompareBytewise));
//finish symlink categorization
@@ -591,9 +600,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co
ParallelOps& posL = bwl.parallelOpsL;
ParallelOps& posR = bwl.parallelOpsR;
- const size_t newTaskCount = numeric::min<size_t>(posL.effectiveMax - posL.current,
- posR.effectiveMax - posR.current,
- bwl.filesToCompareBytewise.size());
+ const size_t newTaskCount = std::min<size_t>({ posL.effectiveMax - posL.current, posR.effectiveMax - posR.current, bwl.filesToCompareBytewise.size() });
if (&posL != &posR) posL.current += newTaskCount; //
/**/ posR.current += newTaskCount; //consider aliasing!
@@ -721,7 +728,7 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn
{
struct FileRef
{
- Zstring normalName; //buffer expensive makeUpperCopy() calls!!
+ Zstring upperCaseName; //buffer expensive makeUpperCopy() calls!!
const typename MapType::value_type* ref;
bool leftSide;
};
@@ -731,20 +738,9 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn
for (const auto& item : mapLeft ) fileList.push_back({ makeUpperCopy(item.first), &item, true });
for (const auto& item : mapRight) fileList.push_back({ makeUpperCopy(item.first), &item, false });
- std::sort(fileList.begin(), fileList.end(), [&](const FileRef& lhs, const FileRef& rhs)
- {
- int rv = compareString(lhs.normalName, rhs.normalName);
- if (rv != 0)
- return rv < 0; //primary sort key: ignore unicode normal form and case
-
- //perf: sorting by secondary/tertiary key here costs about 7% additional runtime
- rv = compareString(getUnicodeNormalForm(lhs.ref->first), getUnicodeNormalForm(rhs.ref->first));
- if (rv != 0)
- return rv < 0; //secondary sort key: ignore unicode normal
-
- return lhs.ref->first < rhs.ref->first; //tertiary sort key: raw string value
- });
+ //primary sort: ignore unicode normal form and case
//bonus: natural default sequence on file guid UI
+ std::sort(fileList.begin(), fileList.end(), [](const FileRef& lhs, const FileRef& rhs) { return lhs.upperCaseName < rhs.upperCaseName; });
auto tryMatchRange = [&](auto it, auto itLast)
{
@@ -758,11 +754,11 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn
else
bo(*it[1].ref, *it[0].ref);
}
- else if (equalCountL == 0) //no match
- std::for_each(it, itLast, [&](const FileRef& fr) { ro(*fr.ref); });
- else if (equalCountR == 0) //no match
- std::for_each(it, itLast, [&](const FileRef& fr) { lo(*fr.ref); });
- else //ambiguous
+ else if (equalCountL == 1 && equalCountR == 0)
+ lo(*it->ref, nullptr);
+ else if (equalCountL == 0 && equalCountR == 1)
+ ro(*it->ref, nullptr);
+ else //ambiguous (yes, even if one side only, e.g. different Unicode normalization forms)
return false;
return true;
};
@@ -770,29 +766,30 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn
for (auto it = fileList.begin(); it != fileList.end();)
{
//find equal range: ignore case, ignore Unicode normalization
- auto itEndEq = std::find_if(it + 1, fileList.end(), [&](const FileRef& fr) { return fr.normalName != it->normalName; });
+ auto itEndEq = std::find_if(it + 1, fileList.end(), [&](const FileRef& fr) { return fr.upperCaseName != it->upperCaseName; });
if (!tryMatchRange(it, itEndEq))
+ {
+ //secondary sort: respect case, ignore unicode normal forms
+ std::sort(it, itEndEq, [](const FileRef& lhs, const FileRef& rhs) { return getUnicodeNormalForm(lhs.ref->first) < getUnicodeNormalForm(rhs.ref->first); });
+
for (auto itCase = it; itCase != itEndEq;)
{
//find equal range: respect case, ignore Unicode normalization
auto itEndCase = std::find_if(itCase + 1, itEndEq, [&](const FileRef& fr) { return getUnicodeNormalForm(fr.ref->first) != getUnicodeNormalForm(itCase->ref->first); });
if (!tryMatchRange(itCase, itEndCase))
- for (auto itRaw = itCase; itRaw != itEndCase;)
+ {
+ const Zstringw& conflictMsg = getConflictAmbiguousItemName(itCase->ref->first);
+ std::for_each(itCase, itEndCase, [&](const FileRef& fr)
{
- //find equal range: respect case, respect Unicode normalization
- auto itEndRaw = std::find_if(itRaw + 1, itEndCase, [&](const FileRef& fr) { return fr.ref->first != itRaw->ref->first; });
- if (!tryMatchRange(itRaw, itEndRaw))
- std::for_each(itRaw, itEndRaw, [&](const FileRef& fr)
- {
- if (fr.leftSide)
- lo(*fr.ref);
- else
- ro(*fr.ref);
- });
- itRaw = itEndRaw;
- }
+ if (fr.leftSide)
+ lo(*fr.ref, &conflictMsg);
+ else
+ ro(*fr.ref, &conflictMsg);
+ });
+ }
itCase = itEndCase;
}
+ }
it = itEndEq;
}
}
@@ -802,11 +799,17 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
{
using FileData = FolderContainer::FileList::value_type;
- matchFolders(lhs.files, rhs.files,
- [&](const FileData& fileLeft ) { FilePair& newItem = output.addSubFile< LEFT_SIDE>(fileLeft .first, fileLeft .second); checkFailedRead(newItem, errorMsg); }, //left only
- [&](const FileData& fileRight) { FilePair& newItem = output.addSubFile<RIGHT_SIDE>(fileRight.first, fileRight.second); checkFailedRead(newItem, errorMsg); }, //right only
-
- [&](const FileData& fileLeft, const FileData& fileRight) //both sides
+ matchFolders(lhs.files, rhs.files, [&](const FileData& fileLeft, const Zstringw* conflictMsg)
+ {
+ FilePair& newItem = output.addSubFile< LEFT_SIDE>(fileLeft .first, fileLeft .second);
+ checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg);
+ },
+ [&](const FileData& fileRight, const Zstringw* conflictMsg)
+ {
+ FilePair& newItem = output.addSubFile<RIGHT_SIDE>(fileRight.first, fileRight.second);
+ checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg);
+ },
+ [&](const FileData& fileLeft, const FileData& fileRight)
{
FilePair& newItem = output.addSubFile(fileLeft.first,
fileLeft.second,
@@ -821,10 +824,16 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
//-----------------------------------------------------------------------------------------------
using SymlinkData = FolderContainer::SymlinkList::value_type;
- matchFolders(lhs.symlinks, rhs.symlinks,
- [&](const SymlinkData& symlinkLeft ) { SymlinkPair& newItem = output.addSubLink< LEFT_SIDE>(symlinkLeft .first, symlinkLeft .second); checkFailedRead(newItem, errorMsg); }, //left only
- [&](const SymlinkData& symlinkRight) { SymlinkPair& newItem = output.addSubLink<RIGHT_SIDE>(symlinkRight.first, symlinkRight.second); checkFailedRead(newItem, errorMsg); }, //right only
-
+ matchFolders(lhs.symlinks, rhs.symlinks, [&](const SymlinkData& symlinkLeft, const Zstringw* conflictMsg)
+ {
+ SymlinkPair& newItem = output.addSubLink< LEFT_SIDE>(symlinkLeft .first, symlinkLeft .second);
+ checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg);
+ },
+ [&](const SymlinkData& symlinkRight, const Zstringw* conflictMsg)
+ {
+ SymlinkPair& newItem = output.addSubLink<RIGHT_SIDE>(symlinkRight.first, symlinkRight.second);
+ checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg);
+ },
[&](const SymlinkData& symlinkLeft, const SymlinkData& symlinkRight) //both sides
{
SymlinkPair& newItem = output.addSubLink(symlinkLeft.first,
@@ -839,21 +848,19 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
//-----------------------------------------------------------------------------------------------
using FolderData = FolderContainer::FolderList::value_type;
- matchFolders(lhs.folders, rhs.folders,
- [&](const FolderData& dirLeft) //left only
+ matchFolders(lhs.folders, rhs.folders, [&](const FolderData& dirLeft, const Zstringw* conflictMsg)
{
FolderPair& newFolder = output.addSubFolder<LEFT_SIDE>(dirLeft.first, dirLeft.second.first);
- const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg);
+ const Zstringw* errorMsgNew = checkFailedRead(newFolder, conflictMsg ? conflictMsg : errorMsg);
this->fillOneSide<LEFT_SIDE>(dirLeft.second.second, errorMsgNew, newFolder); //recurse
},
- [&](const FolderData& dirRight) //right only
+ [&](const FolderData& dirRight, const Zstringw* conflictMsg)
{
FolderPair& newFolder = output.addSubFolder<RIGHT_SIDE>(dirRight.first, dirRight.second.first);
- const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg);
+ const Zstringw* errorMsgNew = checkFailedRead(newFolder, conflictMsg ? conflictMsg : errorMsg);
this->fillOneSide<RIGHT_SIDE>(dirRight.second.second, errorMsgNew, newFolder); //recurse
},
-
- [&](const FolderData& dirLeft, const FolderData& dirRight) //both sides
+ [&](const FolderData& dirLeft, const FolderData& dirRight)
{
FolderPair& newFolder = output.addSubFolder(dirLeft.first, dirLeft.second.first, DIR_EQUAL, dirRight.first, dirRight.second.first);
const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg);
@@ -870,7 +877,7 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer
//-----------------------------------------------------------------------------------------------
//uncheck excluded directories (see parallelDeviceTraversal()) + remove superfluous excluded subdirectories
-void stripExcludedDirectories(ContainerObject& hierObj, const HardFilter& filterProc)
+void stripExcludedDirectories(ContainerObject& hierObj, const PathFilter& filterProc)
{
for (FolderPair& folder : hierObj.refSubFolders())
stripExcludedDirectories(folder, filterProc);
@@ -942,7 +949,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv
bufValueLeft != nullptr, //dir existence must be checked only once: available iff buffer entry exists!
fp.folderPathRight,
bufValueRight != nullptr,
- fpCfg.filter.nameFilter->copyFilterAddingExclusion(excludefilterFailedRead),
+ fpCfg.filter.nameFilter.ref().copyFilterAddingExclusion(excludefilterFailedRead),
fpCfg.compareVar,
fileTimeTolerance_,
fpCfg.ignoreTimeShiftMinutes);
@@ -957,8 +964,8 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv
//NOTE: we need to finish de-activating rows BEFORE binary comparison is run so that it can skip them!
//attention: some excluded directories are still in the comparison result! (see include filter handling!)
- if (!fpCfg.filter.nameFilter->isNull())
- stripExcludedDirectories(*output, *fpCfg.filter.nameFilter); //mark excluded directories (see parallelDeviceTraversal()) + remove superfluous excluded subdirectories
+ if (!fpCfg.filter.nameFilter.ref().isNull())
+ stripExcludedDirectories(*output, fpCfg.filter.nameFilter.ref()); //mark excluded directories (see parallelDeviceTraversal()) + remove superfluous excluded subdirectories
//apply soft filtering (hard filter already applied during traversal!)
addSoftFiltering(*output, fpCfg.filter.timeSizeFilter);
@@ -1007,7 +1014,7 @@ FolderComparison fff::compare(WarningDialogs& warnings,
bool createDirLocks,
std::unique_ptr<LockHolder>& dirLocks,
const std::vector<FolderPairCfg>& fpCfgList,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
ProcessCallback& callback)
{
//PERF_START;
@@ -1078,8 +1085,8 @@ FolderComparison fff::compare(WarningDialogs& warnings,
std::wstring msg;
for (const auto& [folderPair, fpCfg] : workLoad)
- if (std::optional<PathDependency> pd = getPathDependency(folderPair.folderPathLeft, *fpCfg.filter.nameFilter,
- folderPair.folderPathRight, *fpCfg.filter.nameFilter))
+ if (std::optional<PathDependency> pd = getPathDependency(folderPair.folderPathLeft, fpCfg.filter.nameFilter.ref(),
+ folderPair.folderPathRight, fpCfg.filter.nameFilter.ref()))
{
msg += L"\n\n" +
AFS::getDisplayPath(folderPair.folderPathLeft) + L"\n" +
@@ -1098,12 +1105,12 @@ FolderComparison fff::compare(WarningDialogs& warnings,
//lock (existing) directories before comparison
if (createDirLocks)
{
- std::set<Zstring, LessFilePath> dirPathsExisting;
+ std::set<Zstring> folderPathsToLock;
for (const AbstractPath& folderPath : resInfo.existingBaseFolders)
if (std::optional<Zstring> nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further
- dirPathsExisting.insert(*nativePath);
+ folderPathsToLock.insert(*nativePath);
- dirLocks = std::make_unique<LockHolder>(dirPathsExisting, warnings.warnDirectoryLockFailed, callback);
+ dirLocks = std::make_unique<LockHolder>(folderPathsToLock, warnings.warnDirectoryLockFailed, callback);
}
try
diff --git a/FreeFileSync/Source/base/comparison.h b/FreeFileSync/Source/base/comparison.h
index b956019b..82e73637 100755
--- a/FreeFileSync/Source/base/comparison.h
+++ b/FreeFileSync/Source/base/comparison.h
@@ -58,7 +58,7 @@ FolderComparison compare(WarningDialogs& warnings,
bool createDirLocks,
std::unique_ptr<LockHolder>& dirLocks, //out
const std::vector<FolderPairCfg>& fpCfgList,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
ProcessCallback& callback);
}
diff --git a/FreeFileSync/Source/base/db_file.cpp b/FreeFileSync/Source/base/db_file.cpp
index 2dc88402..3f06a197 100755
--- a/FreeFileSync/Source/base/db_file.cpp
+++ b/FreeFileSync/Source/base/db_file.cpp
@@ -141,7 +141,7 @@ DbStreams loadStreams(const AbstractPath& dbPath, const IOCallback& notifyUnbuff
catch (FileError&)
{
bool dbNotYetExisting = false;
- try { dbNotYetExisting = !AFS::getItemTypeIfExists(dbPath); /*throw FileError*/ }
+ try { dbNotYetExisting = !AFS::itemStillExists(dbPath); /*throw FileError*/ }
catch (FileError&) {} //previous exception is more relevant
if (dbNotYetExisting) //throw FileError
@@ -546,7 +546,7 @@ public:
}
private:
- LastSynchronousStateUpdater(CompareVariant activeCmpVar, const HardFilter& filter) :
+ LastSynchronousStateUpdater(CompareVariant activeCmpVar, const PathFilter& filter) :
filter_(filter),
activeCmpVar_(activeCmpVar) {}
@@ -562,15 +562,15 @@ private:
{
//C++17's map::try_emplace() is faster than map::emplace() if key is already existing
auto rv = map.try_emplace(key, std::forward<V>(value)); //and does NOT MOVE r-value arguments unlike map::emplace()!
- if (rv.second)
- return rv.first->second;
+ if (!rv.second)
+ rv.first->second = std::forward<V>(value);
- return rv.first->second = std::forward<V>(value);
+ return rv.first->second;
}
void process(const ContainerObject::FileList& currentFiles, const Zstring& parentRelPath, InSyncFolder::FileList& dbFiles)
{
- std::unordered_set<const InSyncFile*> toPreserve; //referencing fixed-in-memory std::map elements
+ std::set<Zstring, LessUnicodeNormal> toPreserve;
for (const FilePair& file : currentFiles)
if (!file.isPairEmpty())
@@ -584,30 +584,29 @@ private:
assert(file.getFileSize<LEFT_SIDE>() == file.getFileSize<RIGHT_SIDE>());
//create or update new "in-sync" state
- InSyncFile& dbFile = mapAddOrUpdate(dbFiles, file.getItemNameAny(),
- InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(),
- file.getFileId < LEFT_SIDE>()),
- InSyncDescrFile(file.getLastWriteTime<RIGHT_SIDE>(),
- file.getFileId <RIGHT_SIDE>()),
- activeCmpVar_,
- file.getFileSize<LEFT_SIDE>()));
- toPreserve.insert(&dbFile);
+ mapAddOrUpdate(dbFiles, file.getItemNameAny(),
+ InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(),
+ file.getFileId < LEFT_SIDE>()),
+ InSyncDescrFile(file.getLastWriteTime<RIGHT_SIDE>(),
+ file.getFileId <RIGHT_SIDE>()),
+ activeCmpVar_,
+ file.getFileSize<LEFT_SIDE>()));
+ toPreserve.insert(file.getItemNameAny());
}
else //not in sync: preserve last synchronous state
{
- auto it = dbFiles.find(file.getItemNameAny());
- if (it != dbFiles.end())
- toPreserve.insert(&it->second);
+ toPreserve.insert(file.getItemName< LEFT_SIDE>()); //left/right may differ in case!
+ toPreserve.insert(file.getItemName<RIGHT_SIDE>()); //
}
}
//delete removed items (= "in-sync") from database
- erase_if(dbFiles, [&](const InSyncFolder::FileList::value_type& v) -> bool
+ eraseIf(dbFiles, [&](const InSyncFolder::FileList::value_type& v) -> bool
{
- if (toPreserve.find(&v.second) != toPreserve.end())
+ if (toPreserve.find(v.first) != toPreserve.end())
return false;
//all items not existing in "currentFiles" have either been deleted meanwhile or been excluded via filter:
- const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR);
+ const Zstring& itemRelPath = nativeAppendPaths(parentRelPath, v.first);
return filter_.passFileFilter(itemRelPath);
//note: items subject to traveral errors are also excluded by this file filter here! see comparison.cpp, modified file filter for read errors
});
@@ -615,7 +614,7 @@ private:
void process(const ContainerObject::SymlinkList& currentSymlinks, const Zstring& parentRelPath, InSyncFolder::SymlinkList& dbSymlinks)
{
- std::unordered_set<const InSyncSymlink*> toPreserve;
+ std::set<Zstring, LessUnicodeNormal> toPreserve;
for (const SymlinkPair& symlink : currentSymlinks)
if (!symlink.isPairEmpty())
@@ -625,27 +624,26 @@ private:
assert(getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>()));
//create or update new "in-sync" state
- InSyncSymlink& dbSymlink = mapAddOrUpdate(dbSymlinks, symlink.getItemNameAny(),
- InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime<LEFT_SIDE>()),
- InSyncDescrLink(symlink.getLastWriteTime<RIGHT_SIDE>()),
- activeCmpVar_));
- toPreserve.insert(&dbSymlink);
+ mapAddOrUpdate(dbSymlinks, symlink.getItemNameAny(),
+ InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime< LEFT_SIDE>()),
+ InSyncDescrLink(symlink.getLastWriteTime<RIGHT_SIDE>()),
+ activeCmpVar_));
+ toPreserve.insert(symlink.getItemNameAny());
}
else //not in sync: preserve last synchronous state
{
- auto it = dbSymlinks.find(symlink.getItemNameAny());
- if (it != dbSymlinks.end())
- toPreserve.insert(&it->second);
+ toPreserve.insert(symlink.getItemName< LEFT_SIDE>()); //left/right may differ in case!
+ toPreserve.insert(symlink.getItemName<RIGHT_SIDE>()); //
}
}
//delete removed items (= "in-sync") from database
- erase_if(dbSymlinks, [&](const InSyncFolder::SymlinkList::value_type& v) -> bool
+ eraseIf(dbSymlinks, [&](const InSyncFolder::SymlinkList::value_type& v) -> bool
{
- if (toPreserve.find(&v.second) != toPreserve.end())
+ if (toPreserve.find(v.first) != toPreserve.end())
return false;
//all items not existing in "currentSymlinks" have either been deleted meanwhile or been excluded via filter:
- const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR);
+ const Zstring& itemRelPath = nativeAppendPaths(parentRelPath, v.first);
return filter_.passFileFilter(itemRelPath);
});
}
@@ -656,59 +654,45 @@ private:
for (const FolderPair& folder : currentFolders)
if (!folder.isPairEmpty())
- switch (folder.getDirCategory())
+ {
+ if (folder.getDirCategory() == DIR_EQUAL)
{
- case DIR_EQUAL:
- {
- assert(getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()));
-
- //update directory entry only (shallow), but do *not touch* exising child elements!!!
- const Zstring& key = folder.getItemNameAny();
- auto insertResult = dbFolders.emplace(key, InSyncFolder(InSyncFolder::DIR_STATUS_IN_SYNC)); //get or create
- auto it = insertResult.first;
-
- InSyncFolder& dbFolder = it->second;
- dbFolder.status = InSyncFolder::DIR_STATUS_IN_SYNC; //update immediate directory entry
- toPreserve.insert(&dbFolder);
- recurse(folder, dbFolder);
- }
- break;
-
- case DIR_CONFLICT:
- case DIR_DIFFERENT_METADATA:
- //if DIR_DIFFERENT_METADATA and no old database entry yet: we have to insert a placeholder database entry:
- //we cannot simply skip the whole directory, since sub-items might be in sync!
- //Example: directories on left and right differ in case while sub-files are equal
- {
- //reuse last "in-sync" if available or insert strawman entry (do not try to update and thereby remove child elements!!!)
- InSyncFolder& dbFolder = dbFolders.emplace(folder.getItemNameAny(), InSyncFolder(InSyncFolder::DIR_STATUS_STRAW_MAN)).first->second;
- toPreserve.insert(&dbFolder);
- recurse(folder, dbFolder); //unconditional recursion without filter check! => no problem since "childItemMightMatch" is optional!!!
- }
- break;
-
- //not in sync: reuse last synchronous state:
- case DIR_LEFT_SIDE_ONLY:
- case DIR_RIGHT_SIDE_ONLY:
+ assert(getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()));
+
+ //update directory entry only (shallow), but do *not touch* exising child elements!!!
+ InSyncFolder& dbFolder = dbFolders.emplace(folder.getItemNameAny(), InSyncFolder(InSyncFolder::DIR_STATUS_IN_SYNC)).first->second; //get or create
+ dbFolder.status = InSyncFolder::DIR_STATUS_IN_SYNC; //update immediate directory entry
+
+ toPreserve.insert(&dbFolder);
+ recurse(folder, dbFolder);
+ }
+ else //not in sync: preserve last synchronous state
+ {
+ auto preserveDbEntry = [&](const Zstring& folderName)
{
- auto it = dbFolders.find(folder.getItemNameAny());
+ auto it = dbFolders.find(folderName);
if (it != dbFolders.end())
{
toPreserve.insert(&it->second);
- recurse(folder, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!!
+ recurse(folder, it->second); //required: existing child-items may not be in sync, but items deleted on both sides *are* in-sync!!!
}
- }
- break;
+ };
+ preserveDbEntry(folder.getItemName<LEFT_SIDE>());
+
+ //folder match with names differing in case? => treat like any other folder rename => no *new* database entries even if child items are in sync
+ if (getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>()))
+ preserveDbEntry(folder.getItemName<RIGHT_SIDE>());
}
+ }
//delete removed items (= "in-sync") from database
- erase_if(dbFolders, [&](InSyncFolder::FolderList::value_type& v) -> bool
+ eraseIf(dbFolders, [&](InSyncFolder::FolderList::value_type& v) -> bool
{
if (toPreserve.find(&v.second) != toPreserve.end())
return false;
- const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR);
- //if directory is not included in "currentDirs", it is either not existing anymore, in which case it should be deleted from database
+ const Zstring& itemRelPath = nativeAppendPaths(parentRelPath, v.first);
+ //if folder is not included in "current folders", it is either not existing anymore, in which case it should be deleted from database
//or it was excluded via filter and the database entry should be preserved
bool childItemMightMatch = true;
@@ -722,10 +706,10 @@ private:
//delete all entries for removed folder (= "in-sync") from database
void dbSetEmptyState(InSyncFolder& dbFolder, const Zstring& parentRelPathPf)
{
- erase_if(dbFolder.files, [&](const InSyncFolder::FileList ::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); });
- erase_if(dbFolder.symlinks, [&](const InSyncFolder::SymlinkList::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); });
+ eraseIf(dbFolder.files, [&](const InSyncFolder::FileList ::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); });
+ eraseIf(dbFolder.symlinks, [&](const InSyncFolder::SymlinkList::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); });
- erase_if(dbFolder.folders, [&](InSyncFolder::FolderList::value_type& v)
+ eraseIf(dbFolder.folders, [&](InSyncFolder::FolderList::value_type& v)
{
const Zstring& itemRelPath = parentRelPathPf + v.first;
@@ -737,7 +721,7 @@ private:
});
}
- const HardFilter& filter_; //filter used while scanning directory: generates view on actual files!
+ const PathFilter& filter_; //filter used while scanning directory: generates view on actual files!
const CompareVariant activeCmpVar_;
};
diff --git a/FreeFileSync/Source/base/db_file.h b/FreeFileSync/Source/base/db_file.h
index ebbb0c01..c0d1b390 100755
--- a/FreeFileSync/Source/base/db_file.h
+++ b/FreeFileSync/Source/base/db_file.h
@@ -58,16 +58,16 @@ struct InSyncFolder
enum InSyncStatus
{
DIR_STATUS_IN_SYNC,
- DIR_STATUS_STRAW_MAN //there is no last synchronous state, but used as container only
+ DIR_STATUS_STRAW_MAN //no last synchronous state, but used as container only
};
InSyncFolder(InSyncStatus statusIn) : status(statusIn) {}
InSyncStatus status = DIR_STATUS_STRAW_MAN;
//------------------------------------------------------------------
- using FolderList = std::map<Zstring, InSyncFolder, LessFilePath>; //
- using FileList = std::map<Zstring, InSyncFile, LessFilePath>; // key: file name
- using SymlinkList = std::map<Zstring, InSyncSymlink, LessFilePath>; //
+ using FolderList = std::map<Zstring, InSyncFolder, LessUnicodeNormal>; //
+ using FileList = std::map<Zstring, InSyncFile, LessUnicodeNormal>; // key: file name (ignoring Unicode normal forms)
+ using SymlinkList = std::map<Zstring, InSyncSymlink, LessUnicodeNormal>; //
//------------------------------------------------------------------
FolderList folders;
diff --git a/FreeFileSync/Source/base/dir_exist_async.h b/FreeFileSync/Source/base/dir_exist_async.h
index c445a665..749e79ce 100755
--- a/FreeFileSync/Source/base/dir_exist_async.h
+++ b/FreeFileSync/Source/base/dir_exist_async.h
@@ -29,46 +29,78 @@ struct FolderStatus
std::set<AbstractPath> existing;
std::set<AbstractPath> notExisting;
std::map<AbstractPath, zen::FileError> failedChecks;
+
+ std::map<AbstractPath, AbstractPath> normalizedPathsEx; //get rid of folder aliases (e.g. path differing in case)
};
-FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPaths, const std::map<AbstractPath, size_t>& deviceParallelOps,
+AbstractPath getNormalizedPath(const FolderStatus& status, const AbstractPath& folderPath)
+{
+ auto it = status.normalizedPathsEx.find(folderPath);
+ return it == status.normalizedPathsEx.end() ? folderPath : it->second;
+}
+
+
+FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPaths, const std::map<AfsDevice, size_t>& deviceParallelOps,
bool allowUserInteraction, ProcessCallback& procCallback /*throw X*/)
{
using namespace zen;
//aggregate folder paths that are on the same root device: see parallel_scan.h
- std::map<AbstractPath, std::set<AbstractPath>> perDevicePaths;
+ std::map<AfsDevice, std::set<AbstractPath>> perDevicePaths;
for (const AbstractPath& folderPath : folderPaths)
if (!AFS::isNullPath(folderPath)) //skip empty folders
- perDevicePaths[AFS::getRootPath(folderPath)].insert(folderPath);
+ perDevicePaths[folderPath.afsDevice].insert(folderPath);
- std::vector<std::pair<AbstractPath, std::future<bool>>> futureInfo;
+ std::vector<std::pair<AbstractPath, std::future<std::optional<AFS::FileId>>>> futureDetails;
- std::vector<ThreadGroup<std::packaged_task<bool()>>> perDeviceThreads;
- for (const auto& [rootPath, deviceFolderPaths] : perDevicePaths)
+ std::vector<ThreadGroup<std::packaged_task<std::optional<AFS::FileId>()>>> perDeviceThreads;
+ for (const auto& [afsDevice, deviceFolderPaths] : perDevicePaths)
{
- const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, rootPath);
+ const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, afsDevice);
- perDeviceThreads.emplace_back(parallelOps, "DirExist: " + utfTo<std::string>(AFS::getDisplayPath(rootPath)));
+ perDeviceThreads.emplace_back(parallelOps, "DirExist: " + utfTo<std::string>(AFS::getDisplayPath(AbstractPath(afsDevice, AfsPath()))));
auto& threadGroup = perDeviceThreads.back();
threadGroup.detach(); //don't wait on threads hanging longer than "folderAccessTimeout"
for (const AbstractPath& folderPath : deviceFolderPaths)
{
- std::packaged_task<bool()> pt([folderPath, allowUserInteraction] //AbstractPath is thread-safe like an int! :)
+ std::packaged_task<std::optional<AFS::FileId>()> pt([folderPath, allowUserInteraction]() -> std::optional<AFS::FileId>
{
//1. login to network share, open FTP connection, etc.
AFS::connectNetworkFolder(folderPath, allowUserInteraction); //throw FileError
- //2. check dir existence
- return static_cast<bool>(AFS::getItemTypeIfExists(folderPath)); //throw FileError
- //TODO: consider ItemType::FILE a failure instead? In any case: return "false" IFF nothing (of any type) exists
+ //2. check dir existence (...by doing something useful and getting the file ID)
+ std::exception_ptr fidError;
+ try
+ {
+ const AFS::FileId fileId = AFS::getFileId(folderPath); //throw FileError
+ if (!fileId.empty()) //=> folder exists
+ return fileId;
+ }
+ catch (FileError&) { fidError = std::current_exception(); }
+ //else: error or fileId not available, e.g. FTP, SFTP
+
+ /* CAVEAT: the case-sensitive semantics of AFS::itemStillExists() do not fit here!
+ BUT: its implementation happens to be okay for our use:
+ Assume we have a case-insensitive path match:
+ => AFS::itemStillExists() first checks AFS::getItemType()
+ => either succeeds (fine) or fails because of 1. not existing or 2. access error
+ => the subsequent folder search reports "no folder": only a problem in case 2
+ => FFS tries to create the folder during sync and fails with I. access error (fine) or II. already existing (obscures the previous "access error") */
+ if (!AFS::itemStillExists(folderPath)) //throw FileError
+ return {};
+
+ if (fidError)
+ std::rethrow_exception(fidError);
+ else
+ return AFS::FileId();
+ //consider ItemType::FILE a failure instead? Meanwhile: return "false" IFF nothing (of any type) exists
});
auto fut = pt.get_future();
threadGroup.run(std::move(pt));
- futureInfo.emplace_back(folderPath, std::move(fut));
+ futureDetails.emplace_back(folderPath, std::move(fut));
}
}
@@ -76,8 +108,9 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath
const auto startTime = std::chrono::steady_clock::now();
FolderStatus output;
+ std::map<AFS::FileId, AbstractPath> exFoldersById;
- for (auto& [folderPath, future] : futureInfo)
+ for (auto& [folderPath, future] : futureDetails)
{
const std::wstring& displayPathFmt = fmtPath(AFS::getDisplayPath(folderPath));
@@ -96,8 +129,17 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath
try
{
//call future::get() only *once*! otherwise: undefined behavior!
- if (future.get()) //throw FileError
- output.existing.insert(folderPath);
+ if (std::optional<AFS::FileId> folderInfo = future.get()) //throw FileError
+ {
+ output.existing.emplace(folderPath);
+
+ //find folder aliases (e.g. path differing in case)
+ const AFS::FileId fileId = *folderInfo;
+ if (!fileId.empty())
+ exFoldersById.emplace(fileId, folderPath);
+
+ output.normalizedPathsEx.emplace(folderPath, fileId.empty() ? folderPath : exFoldersById.find(fileId)->second);
+ }
else
output.notExisting.insert(folderPath);
}
diff --git a/FreeFileSync/Source/base/dir_lock.cpp b/FreeFileSync/Source/base/dir_lock.cpp
index 86006337..cfcf6699 100755
--- a/FreeFileSync/Source/base/dir_lock.cpp
+++ b/FreeFileSync/Source/base/dir_lock.cpp
@@ -436,9 +436,9 @@ public:
tidyUp();
//optimization: check if we already own a lock for this path
- auto iterGuid = fileToGuid_.find(lockFilePath);
- if (iterGuid != fileToGuid_.end())
- if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(iterGuid->second)) //returns null-lock if not found
+ auto itGuid = guidByPath_.find(lockFilePath);
+ if (itGuid != guidByPath_.end())
+ if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(itGuid->second)) //returns null-lock if not found
return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership
try //check based on lock GUID, deadlock prevention: "lockFilePath" may be an alternative name for a lock already owned by this process
@@ -446,7 +446,7 @@ public:
const std::string lockId = retrieveLockId(lockFilePath); //throw FileError
if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(lockId)) //returns null-lock if not found
{
- fileToGuid_[lockFilePath] = lockId; //found an alias for one of our active locks
+ guidByPath_[lockFilePath] = lockId; //found an alias for one of our active locks
return activeLock;
}
}
@@ -456,9 +456,8 @@ public:
auto newLock = std::make_shared<SharedDirLock>(lockFilePath, notifyStatus, cbInterval); //throw FileError
const std::string& newLockGuid = retrieveLockId(lockFilePath); //throw FileError
- //update registry
- fileToGuid_[lockFilePath] = newLockGuid; //throw()
- guidToLock_[newLockGuid] = newLock; //
+ guidByPath_[lockFilePath] = newLockGuid; //update registry
+ locksByGuid_[newLockGuid] = newLock; //
return newLock;
}
@@ -469,23 +468,21 @@ private:
LockAdmin& operator=(const LockAdmin&) = delete;
using UniqueId = std::string;
- using FileToGuidMap = std::map<Zstring, UniqueId, LessFilePath>; //n:1 handle uppper/lower case correctly
- using GuidToLockMap = std::map<UniqueId, std::weak_ptr<SharedDirLock>>; //1:1
std::shared_ptr<SharedDirLock> getActiveLock(const UniqueId& lockId) //returns null if none found
{
- auto it = guidToLock_.find(lockId);
- return it != guidToLock_.end() ? it->second.lock() : nullptr; //try to get shared_ptr; throw()
+ auto it = locksByGuid_.find(lockId);
+ return it != locksByGuid_.end() ? it->second.lock() : nullptr; //try to get shared_ptr; throw()
}
void tidyUp() //remove obsolete entries
{
- erase_if(guidToLock_, [ ](const GuidToLockMap::value_type& v) { return !v.second.lock(); });
- erase_if(fileToGuid_, [&](const FileToGuidMap::value_type& v) { return guidToLock_.find(v.second) == guidToLock_.end(); });
+ eraseIf(locksByGuid_, [](const auto& v) { return !v.second.lock(); });
+ eraseIf(guidByPath_, [&](const auto& v) { return locksByGuid_.find(v.second) == locksByGuid_.end(); });
}
- FileToGuidMap fileToGuid_; //lockname |-> GUID; locks can be referenced by a lockFilePath or alternatively a GUID
- GuidToLockMap guidToLock_; //GUID |-> "shared lock ownership"
+ std::map<Zstring, UniqueId> guidByPath_; //lockFilePath |-> GUID; n:1; locks can be referenced by a lockFilePath or alternatively a GUID
+ std::map<UniqueId, std::weak_ptr<SharedDirLock>> locksByGuid_; //GUID |-> "shared lock ownership"; 1:1
};
diff --git a/FreeFileSync/Source/base/dir_lock.h b/FreeFileSync/Source/base/dir_lock.h
index 910e551d..20795804 100755
--- a/FreeFileSync/Source/base/dir_lock.h
+++ b/FreeFileSync/Source/base/dir_lock.h
@@ -17,7 +17,7 @@ namespace fff
{
/*
RAII structure to place a directory lock against other FFS processes:
- - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc.
+ - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts, case-differences etc.
- ownership shared between all object instances refering to a specific lock location(= GUID)
- can be copied safely and efficiently! (ref-counting)
- detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds)
diff --git a/FreeFileSync/Source/base/ffs_paths.cpp b/FreeFileSync/Source/base/ffs_paths.cpp
index 3668f059..43ea8fff 100755
--- a/FreeFileSync/Source/base/ffs_paths.cpp
+++ b/FreeFileSync/Source/base/ffs_paths.cpp
@@ -16,18 +16,44 @@ using namespace zen;
namespace
{
inline
-Zstring getExecutablePathPf() //directory containing executable WITH path separator at end
+Zstring getExeFolderPath() //directory containing executable WITH path separator at end
{
- return appendSeparator(beforeLast(utfTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE));
+ return beforeLast(utfTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
}
+
+
+inline
+Zstring getExeFolderParentPath()
+{
+ return beforeLast(getExeFolderPath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+}
+}
+
+
+
+
+VolumeId fff::getVolumeSerialOs() //throw FileError
+{
+ return getFileId("/").volumeId; //throw FileError
}
+VolumeId fff::getVolumeSerialFfs() //throw FileError
+{
+ return getFileId(getExeFolderPath()).volumeId; //throw FileError
+}
bool fff::isPortableVersion()
{
- return !endsWith(getExecutablePathPf(), "/bin/"); //this check is a bit lame...
+ return false; //users want local installation type: https://freefilesync.org/forum/viewtopic.php?t=5750
+ //try
+ //{
+ // return getVolumeSerialFfs() != getVolumeSerialOs(); //throw FileError
+ //}
+ //catch (FileError&) {}
+ //assert(false);
+ //return false;
}
@@ -39,10 +65,10 @@ Zstring fff::getResourceDirPf()
wxTheApp->SetAppName(L"FreeFileSync");
ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName));
- if (isPortableVersion())
- return getExecutablePathPf();
- else //use OS' standard paths
- return appendSeparator(utfTo<Zstring>(wxStandardPathsBase::Get().GetResourcesDir()));
+ //if (isPortableVersion())
+ return appendSeparator(getExeFolderParentPath());
+ //else //use OS' standard paths
+ // return appendSeparator(utfTo<Zstring>(wxStandardPathsBase::Get().GetResourcesDir()));
}
@@ -53,24 +79,39 @@ Zstring fff::getConfigDirPathPf()
wxTheApp->SetAppName(L"FreeFileSync");
ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName));
+ Zstring cfgFolderPath;
if (isPortableVersion())
- return getExecutablePathPf();
- //use OS' standard paths
- Zstring configDirPath = utfTo<Zstring>(wxStandardPaths::Get().GetUserDataDir());
-
- try
+ cfgFolderPath = getExeFolderParentPath();
+ else //OS standard path (XDG layout): ~/.config/FreeFileSync
{
- createDirectoryIfMissingRecursion(configDirPath); //throw FileError
+ //wxBug: wxStandardPaths::GetUserDataDir() does not honor FileLayout_XDG flag
+ wxStandardPaths::Get().SetFileLayout(wxStandardPaths::FileLayout_XDG);
+ cfgFolderPath = appendSeparator(utfTo<Zstring>(wxStandardPaths::Get().GetUserConfigDir())) + "FreeFileSync";
}
- catch (FileError&) { assert(false); }
- return appendSeparator(configDirPath);
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-variable"
+#endif
+ static int initOnce = [&] //"magic static" is the lesser evil in this wxWidgets context...
+ {
+ try //create the config folder if not existing + create "Logs" subfolder while we're at it
+ {
+ createDirectoryIfMissingRecursion(appendSeparator(cfgFolderPath) + Zstr("Logs")); //throw FileError
+ }
+ catch (FileError&) { assert(false); }
+ return 0;
+ }();
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+ return appendSeparator(cfgFolderPath);
}
//this function is called by RealTimeSync!!!
Zstring fff::getFreeFileSyncLauncherPath()
{
- return getExecutablePathPf() + Zstr("FreeFileSync");
+ return getExeFolderParentPath() + Zstr("/FreeFileSync");
}
diff --git a/FreeFileSync/Source/base/ffs_paths.h b/FreeFileSync/Source/base/ffs_paths.h
index 3cb4c07b..4be75986 100755
--- a/FreeFileSync/Source/base/ffs_paths.h
+++ b/FreeFileSync/Source/base/ffs_paths.h
@@ -8,6 +8,7 @@
#define FFS_PATHS_H_842759083425342534253
#include <zen/zstring.h>
+#include <zen/file_id_def.h>
namespace fff
@@ -22,6 +23,10 @@ Zstring getConfigDirPathPf(); //config directory WITH trailing path separator
bool isPortableVersion();
+
+zen::VolumeId getVolumeSerialOs (); //throw FileError
+zen::VolumeId getVolumeSerialFfs(); //
+
Zstring getFreeFileSyncLauncherPath(); //full path to application launcher C:\...\FreeFileSync.exe
}
diff --git a/FreeFileSync/Source/base/file_hierarchy.cpp b/FreeFileSync/Source/base/file_hierarchy.cpp
index d6e65ebd..39af1e8e 100755
--- a/FreeFileSync/Source/base/file_hierarchy.cpp
+++ b/FreeFileSync/Source/base/file_hierarchy.cpp
@@ -20,8 +20,8 @@ std::wstring fff::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL
AbstractPath tmpPathR = itemPathR;
for (;;)
{
- std::optional<AbstractPath> parentPathL = AFS::getParentFolderPath(tmpPathL);
- std::optional<AbstractPath> parentPathR = AFS::getParentFolderPath(tmpPathR);
+ std::optional<AbstractPath> parentPathL = AFS::getParentPath(tmpPathL);
+ std::optional<AbstractPath> parentPathR = AFS::getParentPath(tmpPathR);
if (!parentPathL || !parentPathR)
break;
@@ -33,14 +33,14 @@ std::wstring fff::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL
tmpPathL = *parentPathL;
tmpPathR = *parentPathR;
- commonTrail = AFS::appendPaths(itemNameL, commonTrail, FILE_NAME_SEPARATOR);
+ commonTrail = nativeAppendPaths(itemNameL, commonTrail);
}
if (!commonTrail.empty())
return utfTo<std::wstring>(commonTrail);
auto getLastComponent = [](const AbstractPath& itemPath)
{
- if (!AFS::getParentFolderPath(itemPath)) //= device root
+ if (!AFS::getParentPath(itemPath)) //= device root
return AFS::getDisplayPath(itemPath);
return utfTo<std::wstring>(AFS::getItemName(itemPath));
};
diff --git a/FreeFileSync/Source/base/file_hierarchy.h b/FreeFileSync/Source/base/file_hierarchy.h
index 4d06f73b..0b466c44 100755
--- a/FreeFileSync/Source/base/file_hierarchy.h
+++ b/FreeFileSync/Source/base/file_hierarchy.h
@@ -18,7 +18,7 @@
#include <zen/stl_tools.h>
#include <zen/file_id_def.h>
#include "structures.h"
-#include "hard_filter.h"
+#include "path_filter.h"
#include "../fs/abstract.h"
@@ -184,10 +184,10 @@ private:
virtual Zstring getRelativePathR() const = 0; //
};
-template <> inline AbstractPath PathInformation::getAbstractPath<LEFT_SIDE >() const { return getAbstractPathL(); }
+template <> inline AbstractPath PathInformation::getAbstractPath< LEFT_SIDE>() const { return getAbstractPathL(); }
template <> inline AbstractPath PathInformation::getAbstractPath<RIGHT_SIDE>() const { return getAbstractPathR(); }
-template <> inline Zstring PathInformation::getRelativePath<LEFT_SIDE >() const { return getRelativePathL(); }
+template <> inline Zstring PathInformation::getRelativePath< LEFT_SIDE>() const { return getRelativePathL(); }
template <> inline Zstring PathInformation::getRelativePath<RIGHT_SIDE>() const { return getRelativePathR(); }
//------------------------------------------------------------------
@@ -286,7 +286,7 @@ public:
bool folderAvailableLeft,
const AbstractPath& folderPathRight,
bool folderAvailableRight,
- const HardFilter::FilterRef& filter,
+ const FilterRef& filter,
CompareVariant cmpVar,
int fileTimeTolerance,
const std::vector<unsigned int>& ignoreTimeShiftMinutes) :
@@ -303,7 +303,7 @@ public:
template <SelectedSide side> void setAvailable(bool value); //update after creating the directory in FFS
//get settings which were used while creating BaseFolderPair
- const HardFilter& getFilter() const { return *filter_; }
+ const PathFilter& getFilter() const { return filter_.ref(); }
CompareVariant getCompVariant() const { return cmpVar_; }
int getFileTimeTolerance() const { return fileTimeTolerance_; }
const std::vector<unsigned int>& getIgnoredTimeShift() const { return ignoreTimeShiftMinutes_; }
@@ -314,7 +314,7 @@ private:
AbstractPath getAbstractPathL() const override { return folderPathLeft_; }
AbstractPath getAbstractPathR() const override { return folderPathRight_; }
- const HardFilter::FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files!
+ const FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files!
const CompareVariant cmpVar_;
const int fileTimeTolerance_;
const std::vector<unsigned int> ignoreTimeShiftMinutes_;
@@ -480,7 +480,7 @@ private:
FileSystemObject (const FileSystemObject&) = delete;
FileSystemObject& operator=(const FileSystemObject&) = delete;
- AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath<LEFT_SIDE >(), getRelativePath<LEFT_SIDE >()); }
+ AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath< LEFT_SIDE>(), getRelativePath< LEFT_SIDE>()); }
AbstractPath getAbstractPathR() const override { return AFS::appendRelPath(base().getAbstractPath<RIGHT_SIDE>(), getRelativePath<RIGHT_SIDE>()); }
virtual void removeObjectL() = 0;
@@ -593,8 +593,8 @@ public:
bool isSymlinkSrc);
private:
- Zstring getRelativePathL() const override { return AFS::appendPaths(parent().getRelativePath<LEFT_SIDE >(), getItemName<LEFT_SIDE >(), FILE_NAME_SEPARATOR); }
- Zstring getRelativePathR() const override { return AFS::appendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR); }
+ Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< LEFT_SIDE>(), getItemName< LEFT_SIDE>()); }
+ Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>()); }
SyncOperation applyMoveOptimization(SyncOperation op) const;
@@ -637,8 +637,8 @@ public:
int64_t lastWriteTimeSrc);
private:
- Zstring getRelativePathL() const override { return AFS::appendPaths(parent().getRelativePath<LEFT_SIDE >(), getItemName<LEFT_SIDE >(), FILE_NAME_SEPARATOR); }
- Zstring getRelativePathR() const override { return AFS::appendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR); }
+ Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< LEFT_SIDE>(), getItemName< LEFT_SIDE>()); }
+ Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>()); }
void flip() override;
void removeObjectL() override { attrL_ = LinkAttributes(); }
@@ -841,7 +841,7 @@ void FileSystemObject::setSynced(const Zstring& itemName)
cmpResult_ = FILE_EQUAL;
setSyncDir(SyncDirection::NONE);
- propagateChangedItemName<LEFT_SIDE >(itemNameOldL);
+ propagateChangedItemName< LEFT_SIDE>(itemNameOldL);
propagateChangedItemName<RIGHT_SIDE>(itemNameOldR);
}
@@ -917,9 +917,9 @@ template <SelectedSide side> inline
void ContainerObject::updateRelPathsRecursion(const FileSystemObject& fsAlias)
{
assert(SelectParam<side>::ref(relPathL_, relPathR_) != //perf: only call if actual item name changed!
- AFS::appendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>(), FILE_NAME_SEPARATOR));
+ nativeAppendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>()));
- SelectParam<side>::ref(relPathL_, relPathR_) = AFS::appendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>(), FILE_NAME_SEPARATOR);
+ SelectParam<side>::ref(relPathL_, relPathR_) = nativeAppendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>());
for (FolderPair& folder : subFolders_)
folder.updateRelPathsRecursion<side>(folder);
@@ -928,14 +928,14 @@ void ContainerObject::updateRelPathsRecursion(const FileSystemObject& fsAlias)
inline
ContainerObject::ContainerObject(const FileSystemObject& fsAlias) :
- relPathL_(AFS::appendPaths(fsAlias.parent().relPathL_, fsAlias.getItemName<LEFT_SIDE>(), FILE_NAME_SEPARATOR)),
+ relPathL_(nativeAppendPaths(fsAlias.parent().relPathL_, fsAlias.getItemName<LEFT_SIDE>())),
relPathR_(
fsAlias.parent().relPathL_.c_str() == //
fsAlias.parent().relPathR_.c_str() && //take advantage of FileSystemObject's Zstring reuse:
- fsAlias.getItemName<LEFT_SIDE >().c_str() == //=> perf: 12% faster merge phase; -4% peak memory
+ fsAlias.getItemName< LEFT_SIDE>().c_str() == //=> perf: 12% faster merge phase; -4% peak memory
fsAlias.getItemName<RIGHT_SIDE>().c_str() ? //
relPathL_ : //ternary-WTF! (implicit copy-constructor call!!) => no big deal for a Zstring
- AFS::appendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR)),
+ nativeAppendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName<RIGHT_SIDE>())),
base_(fsAlias.parent().base_)
{
assert(relPathL_.c_str() == relPathR_.c_str() || relPathL_ != relPathR_);
diff --git a/FreeFileSync/Source/base/generate_logfile.cpp b/FreeFileSync/Source/base/generate_logfile.cpp
index dbb8e42a..dcb621be 100755
--- a/FreeFileSync/Source/base/generate_logfile.cpp
+++ b/FreeFileSync/Source/base/generate_logfile.cpp
@@ -22,22 +22,22 @@ std::wstring generateLogHeader(const ProcessSummary& s, const ErrorLog& log, con
//assemble summary box
std::vector<std::wstring> summary;
- //write header
- std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE);
+ const std::wstring tabSpace(4, L' '); //4, the one true space count for tabs
+
+ std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE); //+ L" [" + formatTime<std::wstring>(FORMAT_TIME, startTime + L"]";
if (!s.jobName.empty())
- headerLine += L" | " + s.jobName;
- headerLine += L" | " + finalStatusMsg;
+ headerLine += L" " + s.jobName;
summary.push_back(headerLine);
summary.push_back(L"");
+ summary.push_back(tabSpace + finalStatusMsg);
- const std::wstring tabSpace(4, L' '); //4, the one true space count for tabs
const int errorCount = log.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR);
const int warningCount = log.getItemCount(MSG_TYPE_WARNING);
- if (errorCount > 0) summary.push_back(tabSpace + _("Error" ) + L": " + formatNumber(errorCount));
- if (warningCount > 0) summary.push_back(tabSpace + _("Warning") + L": " + formatNumber(warningCount));
+ if (errorCount > 0) summary.push_back(tabSpace + _("Errors:") + L" " + formatNumber(errorCount));
+ if (warningCount > 0) summary.push_back(tabSpace + _("Warnings:") + L" " + formatNumber(warningCount));
std::wstring itemsProc = tabSpace + _("Items processed:") + L" " + formatNumber(s.statsProcessed.items); //show always, even if 0!
@@ -45,7 +45,7 @@ std::wstring generateLogHeader(const ProcessSummary& s, const ErrorLog& log, con
summary.push_back(itemsProc);
if ((s.statsTotal.items < 0 && s.statsTotal.bytes < 0) || //no total items/bytes: e.g. for pure folder comparison
- s.statsProcessed == s.statsTotal) //...if everything was processed successfully
+ s.statsProcessed == s.statsTotal) //...if everything was processed successfully
;
else
summary.push_back(tabSpace + _("Items remaining:") +
@@ -55,7 +55,7 @@ std::wstring generateLogHeader(const ProcessSummary& s, const ErrorLog& log, con
const int64_t totalTimeSec = std::chrono::duration_cast<std::chrono::seconds>(s.totalTime).count();
summary.push_back(tabSpace + _("Total time:") + L" " + copyStringTo<std::wstring>(wxTimeSpan::Seconds(totalTimeSec).Format()));
- //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-byte-UTF16 codes are usually wider than fixed width chars anyway!
+ //calculate max width, this considers UTF-16, not Unicode code points...but maybe good idea? those 2-byte-UTF16 chars are usually wider than fixed-width chars anyway!
size_t sepLineLen = 0;
for (const std::wstring& str : summary) sepLineLen = std::max(sepLineLen, str.size());
@@ -202,8 +202,8 @@ std::vector<LogFileInfo> getLogFiles(const AbstractPath& logFolderPath) //throw
auto tsEnd = fi.itemName.end() - 4;
if (tsBegin != tsEnd && tsEnd[-1] == STATUS_END_TOKEN)
- tsEnd = search_last(tsBegin, tsEnd,
- std::begin(STATUS_BEGIN_TOKEN), std::end(STATUS_BEGIN_TOKEN) - 1);
+ tsEnd = searchLast(tsBegin, tsEnd,
+ std::begin(STATUS_BEGIN_TOKEN), std::end(STATUS_BEGIN_TOKEN) - 1);
if (tsEnd - tsBegin >= TIME_STAMP_LENGTH &&
tsEnd[-4] == Zstr('.') &&
@@ -261,6 +261,7 @@ void limitLogfileCount(const AbstractPath& logFolderPath, //throw FileError
for (const LogFileInfo& lfi : logFiles)
if (lfi.timeStamp < cutOffTime &&
logFilePathsToKeep.find(lfi.filePath) == logFilePathsToKeep.end()) //don't trim latest log files corresponding to last used config files!
+ //nitpicker's corner: what about path differences due to case? e.g. user-overriden log file path changed in case
{
if (notifyStatus) notifyStatus(_("Cleaning up log files:") + L" " + fmtPath(AFS::getDisplayPath(lfi.filePath)));
try
@@ -280,23 +281,6 @@ void limitLogfileCount(const AbstractPath& logFolderPath, //throw FileError
Zstring fff::getDefaultLogFolderPath() { return getConfigDirPathPf() + Zstr("Logs") ; }
-MessageType fff::getFinalMsgType(SyncResult finalStatus)
-{
- switch (finalStatus)
- {
- case SyncResult::FINISHED_WITH_SUCCESS:
- return MSG_TYPE_INFO;
- case SyncResult::FINISHED_WITH_WARNINGS:
- return MSG_TYPE_WARNING;
- case SyncResult::FINISHED_WITH_ERROR:
- case SyncResult::ABORTED: //= user cancel; *not* a MSG_TYPE_FATAL_ERROR!
- return MSG_TYPE_ERROR;
- }
- assert(false);
- return MSG_TYPE_FATAL_ERROR;
-}
-
-
AbstractPath fff::saveLogFile(const ProcessSummary& summary, //throw FileError
const ErrorLog& log,
const std::chrono::system_clock::time_point& syncStartTime,
diff --git a/FreeFileSync/Source/base/generate_logfile.h b/FreeFileSync/Source/base/generate_logfile.h
index dbb3cbd0..9892fa83 100755
--- a/FreeFileSync/Source/base/generate_logfile.h
+++ b/FreeFileSync/Source/base/generate_logfile.h
@@ -26,8 +26,6 @@ AbstractPath saveLogFile(const ProcessSummary& summary, //throw FileError
int logfilesMaxAgeDays,
const std::set<AbstractPath>& logFilePathsToKeep,
const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/);
-
-zen::MessageType getFinalMsgType(SyncResult finalStatus);
}
#endif //GENERATE_LOGFILE_H_931726432167489732164
diff --git a/FreeFileSync/Source/base/localization.cpp b/FreeFileSync/Source/base/localization.cpp
index de1b7087..3b7faee3 100755
--- a/FreeFileSync/Source/base/localization.cpp
+++ b/FreeFileSync/Source/base/localization.cpp
@@ -147,7 +147,7 @@ std::vector<TranslationInfo> loadTranslations()
assert(!lngHeader.localeName .empty());
assert(!lngHeader.flagFile .empty());
/*
- Some ISO codes are used by multiple wxLanguage IDs which can lead to incorrect mapping!!!
+ Some ISO codes are used by multiple wxLanguage IDs which can lead to incorrect mapping by wxLocale::FindLanguageInfo()!!!
=> Identify by description, e.g. "Chinese (Traditional)". The following ids are affected:
wxLANGUAGE_CHINESE_TRADITIONAL
wxLANGUAGE_ENGLISH_UK
@@ -330,6 +330,75 @@ wxLanguage mapLanguageDialect(wxLanguage language)
}
+//we need to interface with wxWidgets' translation handling for a few translations used in their internal source files
+// => since there is no better API: dynamically generate a MO file and feed it to wxTranslation
+class MemoryTranslationLoader : public wxTranslationsLoader
+{
+public:
+ MemoryTranslationLoader(wxLanguage langId, std::map<std::string, std::wstring>&& transMapping) :
+ canonicalName_(wxLocale::GetLanguageCanonicalName(langId))
+ {
+ //https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
+ transMapping[""] = L"Content-Type: text/plain; charset=UTF-8\n";
+
+ const int headerSize = 28;
+ writeNumber<uint32_t>(moBuf_, 0x950412de); //magic number
+ writeNumber<uint32_t>(moBuf_, 0); //format version
+ writeNumber<uint32_t>(moBuf_, transMapping.size()); //string count
+ writeNumber<uint32_t>(moBuf_, headerSize); //string references offset: original
+ writeNumber<uint32_t>(moBuf_, headerSize + 8 * transMapping.size()); //string references offset: translation
+ writeNumber<uint32_t>(moBuf_, 0); //size of hashing table
+ writeNumber<uint32_t>(moBuf_, 0); //offset of hashing table
+
+ const int stringsOffset = headerSize + 2 * 8 * transMapping.size();
+ std::string stringsList;
+
+ for (const auto& [original, translation] : transMapping)
+ {
+ writeNumber<uint32_t>(moBuf_, original.size()); //string length
+ writeNumber<uint32_t>(moBuf_, stringsOffset + stringsList.size()); //string offset
+ stringsList.append(original.c_str(), original.size() + 1); //include 0-termination
+ }
+
+ for (const auto& item : transMapping)
+ {
+ const auto& translation = utfTo<std::string>(item.second);
+ writeNumber<uint32_t>(moBuf_, translation.size()); //string length
+ writeNumber<uint32_t>(moBuf_, stringsOffset + stringsList.size()); //string offset
+ stringsList.append(translation.c_str(), translation.size() + 1); //include 0-termination
+ }
+
+ writeArray(moBuf_, stringsList.c_str(), stringsList.size());
+ }
+
+ wxMsgCatalog* LoadCatalog(const wxString& domain, const wxString& lang) override
+ {
+ //"lang" is NOT (exactly) what we return from GetAvailableTranslations(), but has a little "extra", e.g.: de_DE.WINDOWS-1252 or ar.WINDOWS-1252
+ if (equalAsciiNoCase(extractIsoLangCode(lang), extractIsoLangCode(canonicalName_)))
+ return wxMsgCatalog::CreateFromData(wxScopedCharBuffer::CreateNonOwned(moBuf_.ref().c_str(), moBuf_.ref().size()), domain);
+ assert(false);
+ return nullptr;
+ }
+
+ wxArrayString GetAvailableTranslations(const wxString& domain) const override
+ {
+ wxArrayString available;
+ available.push_back(canonicalName_);
+ return available;
+ }
+
+private:
+ static wxString extractIsoLangCode(wxString langCode)
+ {
+ langCode = beforeLast(langCode, L".", IF_MISSING_RETURN_ALL);
+ return beforeLast(langCode, L"_", IF_MISSING_RETURN_ALL);
+ }
+
+ const wxString canonicalName_;
+ MemoryStreamOut<std::string> moBuf_;
+};
+
+
//global wxWidgets localization: sets up C localization runtime as well!
class wxWidgetsLocale
{
@@ -356,6 +425,7 @@ public:
locale_->Init(wxLANGUAGE_DEFAULT); //use sys-lang to preserve sub-language specific rules (e.g. german swiss number punctation)
else
locale_->Init(lng); //have to use the supplied language to enable RTL layout different than user settings
+
locLng_ = lng;
}
@@ -404,7 +474,10 @@ void fff::setLanguage(wxLanguage lng) //throw FileError
//load language file into buffer
if (langFilePath.empty()) //if languageFile is empty, texts will be english by default
+ {
setTranslator(nullptr);
+ lng = wxLANGUAGE_ENGLISH_US;
+ }
else
try
{
@@ -420,11 +493,25 @@ void fff::setLanguage(wxLanguage lng) //throw FileError
}
catch (plural::ParsingError&)
{
- throw FileError(replaceCpy<std::wstring>(L"%x: Invalid plural form definition", L"%x", fmtPath(langFilePath))); //user should never see this!
+ throw FileError(L"Invalid plural form definition: " + fmtPath(langFilePath)); //user should never see this!
}
//handle RTL swapping: we need wxWidgets to do this
- wxWidgetsLocale::getInstance().init(langFilePath.empty() ? wxLANGUAGE_ENGLISH : lng);
+ wxWidgetsLocale::getInstance().init(lng);
+
+ //add translation for wxWidgets-internal strings:
+ assert(wxTranslations::Get()); //already initialized by wxLocale
+ if (wxTranslations* wxtrans = wxTranslations::Get())
+ {
+ std::map<std::string, std::wstring> transMapping =
+ {
+ };
+ wxtrans->SetLanguage(lng); //!= wxLocale's language, which could be wxLANGUAGE_DEFAULT (see wxWidgetsLocale)
+ wxtrans->SetLoader(new MemoryTranslationLoader(lng, std::move(transMapping)));
+ const bool catalogAdded = wxtrans->AddCatalog(wxString(), lng);
+ (void)catalogAdded;
+ assert(catalogAdded);
+ }
}
diff --git a/FreeFileSync/Source/base/lock_holder.h b/FreeFileSync/Source/base/lock_holder.h
index d4e3371c..7bc470ba 100755
--- a/FreeFileSync/Source/base/lock_holder.h
+++ b/FreeFileSync/Source/base/lock_holder.h
@@ -13,28 +13,28 @@ namespace fff
//intermediate locks created by DirLock use this extension, too:
const Zchar LOCK_FILE_ENDING[] = Zstr(".ffs_lock"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace!
+//Attention: 1. call after having checked directory existence!
+// 2. perf: remove folder aliases (e.g. case differences) *before* calling this function!!!
+
//hold locks for a number of directories without blocking during lock creation
-//call after having checked directory existence!
class LockHolder
{
public:
- LockHolder(const std::set<Zstring, LessFilePath>& dirPathsExisting, //resolved paths
- bool& warnDirectoryLockFailed,
- ProcessCallback& pcb /*throw X*/)
+ LockHolder(const std::set<Zstring>& folderPaths, bool& warnDirectoryLockFailed, ProcessCallback& pcb /*throw X*/)
{
using namespace zen;
- std::map<Zstring, FileError, LessFilePath> failedLocks;
+ std::map<Zstring, FileError> failedLocks;
- for (const Zstring& dirpath : dirPathsExisting)
+ for (const Zstring& folderPath : folderPaths)
try
{
//lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages)
- lockHolder_.emplace_back(appendSeparator(dirpath) + Zstr("sync") + LOCK_FILE_ENDING,
+ lockHolder_.emplace_back(appendSeparator(folderPath) + Zstr("sync") + LOCK_FILE_ENDING,
[&](const std::wstring& msg) { pcb.reportStatus(msg); /*throw X*/ },
UI_UPDATE_INTERVAL / 2); //throw FileError
}
- catch (const FileError& e) { failedLocks.emplace(dirpath, e); }
+ catch (const FileError& e) { failedLocks.emplace(folderPath, e); }
if (!failedLocks.empty())
{
diff --git a/FreeFileSync/Source/base/norm_filter.h b/FreeFileSync/Source/base/norm_filter.h
index c2606ffc..f96a0aef 100755
--- a/FreeFileSync/Source/base/norm_filter.h
+++ b/FreeFileSync/Source/base/norm_filter.h
@@ -7,7 +7,7 @@
#ifndef NORM_FILTER_H_974896787346251
#define NORM_FILTER_H_974896787346251
-#include "hard_filter.h"
+#include "path_filter.h"
#include "soft_filter.h"
@@ -15,10 +15,10 @@ namespace fff
{
struct NormalizedFilter //grade-a filter: global/local filter settings combined, units resolved, ready for use
{
- NormalizedFilter(const HardFilter::FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {}
+ NormalizedFilter(const FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {}
//"hard" filter: relevant during comparison, physically skips files
- HardFilter::FilterRef nameFilter;
+ FilterRef nameFilter;
//"soft" filter: relevant after comparison; equivalent to user selection
SoftFilter timeSizeFilter;
};
diff --git a/FreeFileSync/Source/base/parallel_scan.cpp b/FreeFileSync/Source/base/parallel_scan.cpp
index 9ffb5f67..35951a37 100755
--- a/FreeFileSync/Source/base/parallel_scan.cpp
+++ b/FreeFileSync/Source/base/parallel_scan.cpp
@@ -316,7 +316,7 @@ private:
struct TraverserConfig
{
const AbstractPath baseFolderPath; //thread-safe like an int! :)
- const HardFilter::FilterRef filter; //always bound!
+ const FilterRef filter;
const SymLinkHandling handleSymlinks;
std::map<Zstring, std::wstring>& failedDirReads;
@@ -396,7 +396,7 @@ void DirCallback::onFile(const AFS::FileInfo& fi) //throw ThreadInterruption
//------------------------------------------------------------------------------------
//apply filter before processing (use relative name!)
- if (!cfg_.filter->passFileFilter(fileRelPath))
+ if (!cfg_.filter.ref().passFileFilter(fileRelPath))
return;
//sync.ffs_db database and lock files are excluded via filter!
@@ -431,7 +431,7 @@ std::shared_ptr<AFS::TraverserCallback> DirCallback::onFolder(const AFS::FolderI
//------------------------------------------------------------------------------------
//apply filter before processing (use relative name!)
bool childItemMightMatch = true;
- const bool passFilter = cfg_.filter->passDirFilter(folderRelPath, &childItemMightMatch);
+ const bool passFilter = cfg_.filter.ref().passDirFilter(folderRelPath, &childItemMightMatch);
if (!passFilter && !childItemMightMatch)
return nullptr; //do NOT traverse subdirs
//else: attention! ensure directory filtering is applied later to exclude actually filtered directories
@@ -473,7 +473,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const AFS::SymlinkInfo& si) //thr
return LINK_SKIP;
case SymLinkHandling::DIRECT:
- if (cfg_.filter->passFileFilter(linkRelPath)) //always use file filter: Link type may not be "stable" on Linux!
+ if (cfg_.filter.ref().passFileFilter(linkRelPath)) //always use file filter: Link type may not be "stable" on Linux!
{
output_.addSubLink(si.itemName, LinkAttributes(si.modTime));
cfg_.acb.incItemsScanned(); //add 1 element to the progress indicator
@@ -483,10 +483,10 @@ DirCallback::HandleLink DirCallback::onSymlink(const AFS::SymlinkInfo& si) //thr
case SymLinkHandling::FOLLOW:
//filter symlinks before trying to follow them: handle user-excluded broken symlinks!
//since we don't know yet what type the symlink will resolve to, only do this when both filter variants agree:
- if (!cfg_.filter->passFileFilter(linkRelPath))
+ if (!cfg_.filter.ref().passFileFilter(linkRelPath))
{
bool childItemMightMatch = true;
- if (!cfg_.filter->passDirFilter(linkRelPath, &childItemMightMatch))
+ if (!cfg_.filter.ref().passDirFilter(linkRelPath, &childItemMightMatch))
if (!childItemMightMatch)
return LINK_SKIP;
}
@@ -520,7 +520,7 @@ DirCallback::HandleError DirCallback::reportError(const std::wstring& msg, size_
void fff::parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead,
std::map<DirectoryKey, DirectoryValue>& output,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
const TravErrorCb& onError, const TravStatusCb& onStatusUpdate,
std::chrono::milliseconds cbInterval)
{
@@ -530,10 +530,10 @@ void fff::parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead,
// => one worker thread *per device*: avoid excessive parallelism
// => parallel folder traversal considers "parallel file operations" as specified by user
// => (S)FTP: avoid hitting connection limits inadvertently
- std::map<AbstractPath, std::set<DirectoryKey>> perDeviceFolders;
+ std::map<AfsDevice, std::set<DirectoryKey>> perDeviceFolders;
for (const DirectoryKey& key : foldersToRead)
- perDeviceFolders[AFS::getRootPath(key.folderPath)].insert(key);
+ perDeviceFolders[key.folderPath.afsDevice].insert(key);
//communication channel used by threads
AsyncCallback acb(perDeviceFolders.size() /*threadsToFinish*/, cbInterval); //manage life time: enclose InterruptibleThread's!!!
@@ -543,17 +543,17 @@ void fff::parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead,
ZEN_ON_SCOPE_FAIL( for (InterruptibleThread& wt : worker) wt.interrupt(); ); //interrupt all first, then join
//init worker threads
- for (const auto& [rootPath, dirKeys] : perDeviceFolders)
+ for (const auto& [afsDevice, dirKeys] : perDeviceFolders)
{
const int threadIdx = static_cast<int>(worker.size());
- const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, rootPath);
+ const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, afsDevice);
std::map<DirectoryKey, DirectoryValue*> workload;
for (const DirectoryKey& key : dirKeys)
workload.emplace(key, &output[key]); //=> DirectoryValue* unshared for lock-free worker-thread access
- worker.emplace_back([rootPath = rootPath /*clang bug :>*/, workload, threadIdx, &acb, parallelOps]() mutable
+ worker.emplace_back([afsDevice = afsDevice /*clang bug :>*/, workload, threadIdx, &acb, parallelOps]() mutable
{
setCurrentThreadName(("Comp Worker[" + numberTo<std::string>(threadIdx) + "]").c_str());
@@ -566,11 +566,10 @@ void fff::parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead,
for (auto& [folderKey, folderVal] : workload)
{
- const std::vector<Zstring> relPath = split(AFS::getRootRelativePath(folderKey.folderPath), FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY);
- assert(AFS::getRootPath(folderKey.folderPath) == rootPath);
- travWorkload.emplace_back(relPath, std::make_shared<BaseDirCallback>(folderKey, *folderVal, acb, threadIdx, lastReportTime));
+ assert(folderKey.folderPath.afsDevice == afsDevice);
+ travWorkload.emplace_back(folderKey.folderPath.afsPath, std::make_shared<BaseDirCallback>(folderKey, *folderVal, acb, threadIdx, lastReportTime));
}
- AFS::traverseFolderRecursive(rootPath, travWorkload, parallelOps); //throw ThreadInterruption
+ AFS::traverseFolderRecursive(afsDevice, travWorkload, parallelOps); //throw ThreadInterruption
});
}
diff --git a/FreeFileSync/Source/base/parallel_scan.h b/FreeFileSync/Source/base/parallel_scan.h
index dbb0c1d9..71dfd586 100755
--- a/FreeFileSync/Source/base/parallel_scan.h
+++ b/FreeFileSync/Source/base/parallel_scan.h
@@ -10,7 +10,7 @@
#include <map>
#include <set>
#include <chrono>
-#include "hard_filter.h"
+#include "path_filter.h"
#include "structures.h"
#include "file_hierarchy.h"
@@ -20,7 +20,7 @@ namespace fff
struct DirectoryKey
{
AbstractPath folderPath;
- HardFilter::FilterRef filter; //always bound by design!
+ FilterRef filter;
SymLinkHandling handleSymlinks = SymLinkHandling::EXCLUDE;
};
@@ -31,11 +31,11 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs)
if (lhs.handleSymlinks != rhs.handleSymlinks)
return lhs.handleSymlinks < rhs.handleSymlinks;
- const int cmp = AbstractFileSystem::compareAbstractPath(lhs.folderPath, rhs.folderPath);
+ const int cmp = AbstractFileSystem::comparePath(lhs.folderPath, rhs.folderPath);
if (cmp != 0)
return cmp < 0;
- return *lhs.filter < *rhs.filter;
+ return lhs.filter.ref() < rhs.filter.ref();
}
@@ -51,15 +51,15 @@ struct DirectoryValue
};
-//attention: ensure directory filtering is applied later to exclude filtered folders which have been kept as parent folders
-
+//Attention: 1. ensure directory filtering is applied later to exclude filtered folders which have been kept as parent folders
+// 2. remove folder aliases (e.g. case differences) *before* calling this function!!!
using TravErrorCb = std::function<AFS::TraverserCallback::HandleError(const std::wstring& msg, size_t retryNumber)>;
using TravStatusCb = std::function< void (const std::wstring& statusLine, int itemsTotal)>;
void parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead,
std::map<DirectoryKey, DirectoryValue>& output,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
const TravErrorCb& onError, const TravStatusCb& onStatusUpdate, //NOT optional
std::chrono::milliseconds cbInterval);
}
diff --git a/FreeFileSync/Source/base/parse_lng.h b/FreeFileSync/Source/base/parse_lng.h
index c282c2de..e1e5e57d 100755
--- a/FreeFileSync/Source/base/parse_lng.h
+++ b/FreeFileSync/Source/base/parse_lng.h
@@ -249,7 +249,7 @@ public:
Token getNextToken()
{
//skip whitespace
- pos_ = std::find_if(pos_, stream_.end(), std::not_fn(zen::isWhiteSpace<char>));
+ pos_ = std::find_if_not(pos_, stream_.end(), zen::isWhiteSpace<char>);
if (pos_ == stream_.end())
return Token(Token::TK_END);
diff --git a/FreeFileSync/Source/base/parse_plural.h b/FreeFileSync/Source/base/parse_plural.h
index e735c421..d80a0dca 100755
--- a/FreeFileSync/Source/base/parse_plural.h
+++ b/FreeFileSync/Source/base/parse_plural.h
@@ -211,7 +211,7 @@ public:
Token getNextToken() //throw ParsingError
{
//skip whitespace
- pos_ = std::find_if(pos_, stream_.end(), std::not_fn(zen::isWhiteSpace<char>));
+ pos_ = std::find_if_not(pos_, stream_.end(), zen::isWhiteSpace<char>);
if (pos_ == stream_.end())
return Token::TK_END;
@@ -223,7 +223,7 @@ public:
return Token(tokenEnum);
}
- auto digitEnd = std::find_if(pos_, stream_.end(), std::not_fn(zen::isDigit<char>));
+ auto digitEnd = std::find_if_not(pos_, stream_.end(), zen::isDigit<char>);
if (pos_ == digitEnd)
throw ParsingError(); //unknown token
diff --git a/FreeFileSync/Source/base/path_filter.cpp b/FreeFileSync/Source/base/path_filter.cpp
new file mode 100755
index 00000000..79cb4a0f
--- /dev/null
+++ b/FreeFileSync/Source/base/path_filter.cpp
@@ -0,0 +1,366 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#include "path_filter.h"
+#include <set>
+#include <stdexcept>
+#include <vector>
+#include <typeinfo>
+#include <iterator>
+
+using namespace zen;
+using namespace fff;
+
+
+bool fff::operator<(const PathFilter& lhs, const PathFilter& rhs)
+{
+ if (typeid(lhs) != typeid(rhs))
+ return typeid(lhs).before(typeid(rhs)); //in worst case, order is guaranteed to be stable only during each program run
+
+ //lhs, rhs have same type:
+ return lhs.cmpLessSameType(rhs);
+}
+
+
+namespace
+{
+//constructing Zstrings of these in addFilterEntry becomes perf issue for large filter lists => use global POD!
+const Zchar sepAsterisk[] = Zstr("/*");
+const Zchar asteriskSep[] = Zstr("*/");
+static_assert(FILE_NAME_SEPARATOR == '/');
+
+
+void addFilterEntry(const Zstring& filterPhrase, std::vector<Zstring>& masksFileFolder, std::vector<Zstring>& masksFolder)
+{
+ warn_static("3. ignore path separator => bug regarding copyFilterAddingExclusion() after failed directory reads when dir has path separator from other OS in name")
+
+ //normalize filter input: 1. ignore Unicode normalization form 2. ignore case 3. ignore path separator
+ Zstring filterFmt = makeUpperCopy(filterPhrase);
+ if constexpr (FILE_NAME_SEPARATOR != Zstr('/' )) replace(filterFmt, Zstr('/'), FILE_NAME_SEPARATOR);
+ if constexpr (FILE_NAME_SEPARATOR != Zstr('\\')) replace(filterFmt, Zstr('\\'), FILE_NAME_SEPARATOR);
+ /*
+ phrase | action
+ +---------+--------
+ | \blah | remove \
+ | \*blah | remove \
+ | \*\blah | remove \
+ | \*\* | remove \
+ +---------+--------
+ | *blah |
+ | *\blah | -> add blah
+ | *\*blah | -> add *blah
+ +---------+--------
+ | blah\ | remove \; folder only
+ | blah*\ | remove \; folder only
+ | blah\*\ | remove \; folder only
+ +---------+--------
+ | blah* |
+ | blah\* | remove \*; folder only
+ | blah*\* | remove \*; folder only
+ +---------+--------
+ */
+ auto processTail = [&masksFileFolder, &masksFolder](const Zstring& phrase)
+ {
+ if (endsWith(phrase, FILE_NAME_SEPARATOR) || //only relevant for folder filtering
+ endsWith(phrase, sepAsterisk)) // abc\*
+ {
+ const Zstring dirPhrase = beforeLast(phrase, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
+ if (!dirPhrase.empty())
+ masksFolder.push_back(dirPhrase);
+ }
+ else if (!phrase.empty())
+ masksFileFolder.push_back(phrase);
+ };
+
+ if (startsWith(filterFmt, FILE_NAME_SEPARATOR)) // \abc
+ processTail(afterFirst(filterFmt, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE));
+ else
+ {
+ processTail(filterFmt);
+ if (startsWith(filterFmt, asteriskSep)) // *\abc
+ processTail(afterFirst(filterFmt, asteriskSep, IF_MISSING_RETURN_NONE));
+ }
+}
+
+
+template <class Char> inline
+const Char* cStringFind(const Char* str, Char ch) //= strchr(), wcschr()
+{
+ for (;;)
+ {
+ const Char s = *str;
+ if (s == ch) //ch is allowed to be 0 by contract! must return end of string in this case
+ return str;
+
+ if (s == 0)
+ return nullptr;
+ ++str;
+ }
+}
+
+
+/*
+struct FullMatch
+{
+ static bool matchesMaskEnd (const Zchar* path) { return *path == 0; }
+ static bool matchesMaskStar(const Zchar* path) { return true; }
+};
+*/
+
+struct ParentFolderMatch //strict match of parent folder path!
+{
+ static bool matchesMaskEnd (const Zchar* path) { return *path == FILE_NAME_SEPARATOR; }
+ static bool matchesMaskStar(const Zchar* path) { return cStringFind(path, FILE_NAME_SEPARATOR) != nullptr; }
+};
+
+struct AnyMatch
+{
+ static bool matchesMaskEnd (const Zchar* path) { return *path == 0 || *path == FILE_NAME_SEPARATOR; }
+ static bool matchesMaskStar(const Zchar* path) { return true; }
+};
+
+
+template <class PathEndMatcher>
+bool matchesMask(const Zchar* path, const Zchar* mask)
+{
+ for (;; ++mask, ++path)
+ {
+ Zchar m = *mask;
+ switch (m)
+ {
+ case 0:
+ return PathEndMatcher::matchesMaskEnd(path);
+
+ case Zstr('?'):
+ if (*path == 0)
+ return false;
+ break;
+
+ case Zstr('*'):
+ do //advance mask to next non-* char
+ {
+ m = *++mask;
+ }
+ while (m == Zstr('*'));
+
+ if (m == 0) //mask ends with '*':
+ return PathEndMatcher::matchesMaskStar(path);
+
+ //*? - pattern
+ if (m == Zstr('?'))
+ {
+ ++mask;
+ while (*path++ != 0)
+ if (matchesMask<PathEndMatcher>(path, mask))
+ return true;
+ return false;
+ }
+
+ //*[letter] - pattern
+ ++mask;
+ for (;;)
+ {
+ path = cStringFind(path, m);
+ if (!path)
+ return false;
+
+ ++path;
+ if (matchesMask<PathEndMatcher>(path, mask))
+ return true;
+ }
+
+ default:
+ if (*path != m)
+ return false;
+ }
+ }
+}
+
+
+//returns true if string matches at least the beginning of mask
+inline
+bool matchesMaskBegin(const Zchar* str, const Zchar* mask)
+{
+ for (;; ++mask, ++str)
+ {
+ const Zchar m = *mask;
+ switch (m)
+ {
+ case 0:
+ return *str == 0;
+
+ case Zstr('?'):
+ if (*str == 0)
+ return true;
+ break;
+
+ case Zstr('*'):
+ return true;
+
+ default:
+ if (*str != m)
+ return *str == 0;
+ }
+ }
+}
+
+
+template <class PathEndMatcher> inline
+bool matchesMask(const Zstring& name, const std::vector<Zstring>& masks)
+{
+ return std::any_of(masks.begin(), masks.end(), [&](const Zstring& mask) { return matchesMask<PathEndMatcher>(name.c_str(), mask.c_str()); });
+}
+
+
+inline
+bool matchesMaskBegin(const Zstring& name, const std::vector<Zstring>& masks)
+{
+ return std::any_of(masks.begin(), masks.end(), [&](const Zstring& mask) { return matchesMaskBegin(name.c_str(), mask.c_str()); });
+}
+}
+
+
+std::vector<Zstring> fff::splitByDelimiter(const Zstring& filterPhrase)
+{
+ //delimiters may be FILTER_ITEM_SEPARATOR or '\n'
+ std::vector<Zstring> output;
+
+ for (const Zstring& str : split(filterPhrase, FILTER_ITEM_SEPARATOR, SplitType::SKIP_EMPTY)) //split by less common delimiter first (create few, large strings)
+ for (Zstring entry : split(str, Zstr('\n'), SplitType::SKIP_EMPTY))
+ {
+ trim(entry);
+ if (!entry.empty())
+ output.push_back(std::move(entry));
+ }
+
+ return output;
+}
+
+//#################################################################################################
+
+NameFilter::NameFilter(const Zstring& includePhrase, const Zstring& excludePhrase)
+{
+ //setup include/exclude filters for files and directories
+ for (const Zstring& entry : splitByDelimiter(includePhrase)) addFilterEntry(entry, includeMasksFileFolder, includeMasksFolder);
+ for (const Zstring& entry : splitByDelimiter(excludePhrase)) addFilterEntry(entry, excludeMasksFileFolder, excludeMasksFolder);
+
+ removeDuplicates(includeMasksFileFolder);
+ removeDuplicates(includeMasksFolder);
+ removeDuplicates(excludeMasksFileFolder);
+ removeDuplicates(excludeMasksFolder);
+}
+
+
+void NameFilter::addExclusion(const Zstring& excludePhrase)
+{
+ for (const Zstring& entry : splitByDelimiter(excludePhrase)) addFilterEntry(entry, excludeMasksFileFolder, excludeMasksFolder);
+
+ removeDuplicates(excludeMasksFileFolder);
+ removeDuplicates(excludeMasksFolder);
+}
+
+
+bool NameFilter::passFileFilter(const Zstring& relFilePath) const
+{
+ assert(!startsWith(relFilePath, FILE_NAME_SEPARATOR));
+
+ //normalize input: 1. ignore Unicode normalization form 2. ignore case
+ const Zstring& pathFmt = makeUpperCopy(relFilePath);
+
+ if (matchesMask<AnyMatch >(pathFmt, excludeMasksFileFolder) || //either full match on file or partial match on any parent folder
+ matchesMask<ParentFolderMatch>(pathFmt, excludeMasksFolder)) //partial match on any parent folder only
+ return false;
+
+ return matchesMask<AnyMatch >(pathFmt, includeMasksFileFolder) ||
+ matchesMask<ParentFolderMatch>(pathFmt, includeMasksFolder);
+}
+
+
+bool NameFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const
+{
+ assert(!startsWith(relDirPath, FILE_NAME_SEPARATOR));
+ assert(!childItemMightMatch || *childItemMightMatch); //check correct usage
+
+ //normalize input: 1. ignore Unicode normalization form 2. ignore case
+ const Zstring& pathFmt = makeUpperCopy(relDirPath);
+
+ if (matchesMask<AnyMatch>(pathFmt, excludeMasksFileFolder) ||
+ matchesMask<AnyMatch>(pathFmt, excludeMasksFolder))
+ {
+ if (childItemMightMatch)
+ *childItemMightMatch = false; //perf: no need to traverse deeper; subfolders/subfiles would be excluded by filter anyway!
+ /*
+ Attention: the design choice that "childItemMightMatch" is optional implies that the filter must provide correct results no matter if this
+ value is considered by the client!
+ In particular, if *childItemMightMatch == false, then any filter evaluations for child items must also return "false"!
+ This is not a problem for folder traversal which stops at the first *childItemMightMatch == false anyway, but other code continues recursing further,
+ e.g. the database update code in db_file.cpp recurses unconditionally without filter check! It's possible to construct edge cases with incorrect
+ behavior if "childItemMightMatch" were not optional:
+ 1. two folders including a subfolder with some files are in sync with up-to-date database files
+ 2. deny access to this subfolder on both sides and start sync ignoring errors
+ 3. => database entries of this subfolder are incorrectly deleted! (if sub-folder is excluded, but child items are not!)
+ */
+ return false;
+ }
+
+ if (!matchesMask<AnyMatch>(pathFmt, includeMasksFileFolder) &&
+ !matchesMask<AnyMatch>(pathFmt, includeMasksFolder))
+ {
+ if (childItemMightMatch)
+ {
+ const Zstring& childPathBegin = pathFmt + FILE_NAME_SEPARATOR;
+
+ *childItemMightMatch = matchesMaskBegin(childPathBegin, includeMasksFileFolder) || //might match a file or folder in subdirectory
+ matchesMaskBegin(childPathBegin, includeMasksFolder); //
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+bool NameFilter::isNull(const Zstring& includePhrase, const Zstring& excludePhrase)
+{
+ const Zstring include = trimCpy(includePhrase);
+ const Zstring exclude = trimCpy(excludePhrase);
+
+ return include == Zstr("*") && exclude.empty();
+ //return NameFilter(includePhrase, excludePhrase).isNull(); -> very expensive for huge lists
+}
+
+
+bool NameFilter::isNull() const
+{
+ return includeMasksFileFolder.size() == 1 && includeMasksFileFolder[0] == Zstr("*") &&
+ includeMasksFolder .empty() &&
+ excludeMasksFileFolder.empty() &&
+ excludeMasksFolder .empty();
+ //avoid static non-POD null-NameFilter instance; instead test manually and verify function on startup:
+}
+
+
+
+bool NameFilter::cmpLessSameType(const PathFilter& other) const
+{
+ assert(typeid(*this) == typeid(other)); //always given in this context!
+
+ const NameFilter& otherNameFilt = static_cast<const NameFilter&>(other);
+
+ if (includeMasksFileFolder != otherNameFilt.includeMasksFileFolder)
+ return includeMasksFileFolder < otherNameFilt.includeMasksFileFolder;
+
+ if (includeMasksFolder != otherNameFilt.includeMasksFolder)
+ return includeMasksFolder < otherNameFilt.includeMasksFolder;
+
+ if (excludeMasksFileFolder != otherNameFilt.excludeMasksFileFolder)
+ return excludeMasksFileFolder < otherNameFilt.excludeMasksFileFolder;
+
+ if (excludeMasksFolder != otherNameFilt.excludeMasksFolder)
+ return excludeMasksFolder < otherNameFilt.excludeMasksFolder;
+
+ return false; //all equal
+}
diff --git a/FreeFileSync/Source/base/path_filter.h b/FreeFileSync/Source/base/path_filter.h
new file mode 100755
index 00000000..0b80fbce
--- /dev/null
+++ b/FreeFileSync/Source/base/path_filter.h
@@ -0,0 +1,239 @@
+// *****************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
+// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved *
+// *****************************************************************************
+
+#ifndef HARD_FILTER_H_825780275842758345
+#define HARD_FILTER_H_825780275842758345
+
+#include <vector>
+#include <memory>
+#include <zen/zstring.h>
+
+
+namespace fff
+{
+//------------------------------------------------------------------
+/*
+Semantics of PathFilter:
+1. using it creates a NEW folder hierarchy! -> must be considered by <Two way> variant!
+2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder!
+
+ class hierarchy:
+
+ PathFilter (interface)
+ /|\
+ _________|_____________
+ | | |
+NullFilter NameFilter CombinedFilter
+*/
+class PathFilter;
+using FilterRef = zen::SharedRef<const PathFilter>; //always bound by design! Thread-safety: internally synchronized!
+
+class PathFilter //interface for filtering
+{
+public:
+ virtual ~PathFilter() {}
+
+ virtual bool passFileFilter(const Zstring& relFilePath) const = 0;
+ virtual bool passDirFilter (const Zstring& relDirPath, bool* childItemMightMatch) const = 0;
+ //childItemMightMatch: file/dir in subdirectories could(!) match
+ //note: this hint is only set if passDirFilter returns false!
+
+ virtual bool isNull() const = 0; //filter is equivalent to NullFilter
+
+ virtual FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const = 0;
+
+private:
+ friend bool operator<(const PathFilter& lhs, const PathFilter& rhs);
+
+ virtual bool cmpLessSameType(const PathFilter& other) const = 0; //typeid(*this) == typeid(other) in this context!
+};
+
+bool operator<(const PathFilter& lhs, const PathFilter& rhs); //GCC: friend-declaration is not a "proper" declaration
+inline bool operator==(const PathFilter& lhs, const PathFilter& rhs) { return !(lhs < rhs) && !(rhs < lhs); }
+inline bool operator!=(const PathFilter& lhs, const PathFilter& rhs) { return !(lhs == rhs); }
+
+
+//small helper method: merge two hard filters (thereby remove Null-filters)
+FilterRef combineFilters(const FilterRef& first, const FilterRef& second);
+
+
+class NullFilter : public PathFilter //no filtering at all
+{
+public:
+ bool passFileFilter(const Zstring& relFilePath) const override { return true; }
+ bool passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const override;
+ bool isNull() const override { return true; }
+ FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const override;
+
+private:
+ bool cmpLessSameType(const PathFilter& other) const override;
+};
+
+
+class NameFilter : public PathFilter //filter by base-relative file path
+{
+public:
+ NameFilter(const Zstring& includePhrase, const Zstring& excludePhrase);
+
+ void addExclusion(const Zstring& excludePhrase);
+
+ bool passFileFilter(const Zstring& relFilePath) const override;
+ bool passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const override;
+
+ bool isNull() const override;
+ static bool isNull(const Zstring& includePhrase, const Zstring& excludePhrase); //*fast* check without expensive NameFilter construction!
+ FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const override;
+
+private:
+ bool cmpLessSameType(const PathFilter& other) const override;
+
+ std::vector<Zstring> includeMasksFileFolder; //
+ std::vector<Zstring> includeMasksFolder; //upper-case + Unicode-normalized by construction
+ std::vector<Zstring> excludeMasksFileFolder; //
+ std::vector<Zstring> excludeMasksFolder; //
+};
+
+
+class CombinedFilter : public PathFilter //combine two filters to match if and only if both match
+{
+public:
+ CombinedFilter(const NameFilter& first, const NameFilter& second) : first_(first), second_(second) { assert(!first.isNull() && !second.isNull()); } //if either is null, then wy use CombinedFilter?
+
+ bool passFileFilter(const Zstring& relFilePath) const override;
+ bool passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const override;
+ bool isNull() const override;
+ FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const override;
+
+private:
+ bool cmpLessSameType(const PathFilter& other) const override;
+
+ const NameFilter first_;
+ const NameFilter second_;
+};
+
+const Zchar FILTER_ITEM_SEPARATOR = Zstr('|');
+
+
+
+
+
+//--------------- inline implementation ---------------------------------------
+inline
+bool NullFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const
+{
+ assert(!childItemMightMatch || *childItemMightMatch); //check correct usage
+ return true;
+}
+
+
+inline
+bool NullFilter::cmpLessSameType(const PathFilter& other) const
+{
+ assert(typeid(*this) == typeid(other)); //always given in this context!
+ return false;
+}
+
+
+inline
+FilterRef NullFilter::copyFilterAddingExclusion(const Zstring& excludePhrase) const
+{
+ auto filter = zen::makeSharedRef<NameFilter>(Zstr("*"), excludePhrase);
+ if (filter.ref().isNull())
+ return zen::makeSharedRef<const NullFilter>();
+ return filter;
+}
+
+
+inline
+FilterRef NameFilter::copyFilterAddingExclusion(const Zstring& excludePhrase) const
+{
+ auto tmp = zen::makeSharedRef<NameFilter>(*this);
+ tmp.ref().addExclusion(excludePhrase);
+ return tmp;
+}
+
+
+inline
+bool CombinedFilter::passFileFilter(const Zstring& relFilePath) const
+{
+ return first_ .passFileFilter(relFilePath) && //short-circuit behavior
+ second_.passFileFilter(relFilePath);
+}
+
+
+inline
+bool CombinedFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const
+{
+ if (first_.passDirFilter(relDirPath, childItemMightMatch))
+ return second_.passDirFilter(relDirPath, childItemMightMatch);
+ else
+ {
+ if (childItemMightMatch && *childItemMightMatch)
+ second_.passDirFilter(relDirPath, childItemMightMatch);
+ return false;
+ }
+}
+
+
+inline
+bool CombinedFilter::isNull() const
+{
+ return first_.isNull() && second_.isNull();
+}
+
+
+inline
+FilterRef CombinedFilter::copyFilterAddingExclusion(const Zstring& excludePhrase) const
+{
+ NameFilter tmp(first_);
+ tmp.addExclusion(excludePhrase);
+
+ return zen::makeSharedRef<CombinedFilter>(tmp, second_);
+}
+
+
+inline
+bool CombinedFilter::cmpLessSameType(const PathFilter& other) const
+{
+ assert(typeid(*this) == typeid(other)); //always given in this context!
+
+ const CombinedFilter& otherCombFilt = static_cast<const CombinedFilter&>(other);
+
+ if (first_ != otherCombFilt.first_)
+ return first_ < otherCombFilt.first_;
+
+ return second_ < otherCombFilt.second_;
+}
+
+
+inline
+FilterRef constructFilter(const Zstring& includePhrase,
+ const Zstring& excludePhrase,
+ const Zstring& includePhrase2,
+ const Zstring& excludePhrase2)
+{
+ if (NameFilter::isNull(includePhrase, Zstring()))
+ {
+ auto filterTmp = zen::makeSharedRef<NameFilter>(includePhrase2, excludePhrase + Zstr("\n") + excludePhrase2);
+ if (filterTmp.ref().isNull())
+ return zen::makeSharedRef<NullFilter>();
+
+ return filterTmp;
+ }
+ else
+ {
+ if (NameFilter::isNull(includePhrase2, Zstring()))
+ return zen::makeSharedRef<NameFilter>(includePhrase, excludePhrase + Zstr("\n") + excludePhrase2);
+ else
+ return zen::makeSharedRef<CombinedFilter>(NameFilter(includePhrase, excludePhrase + Zstr("\n") + excludePhrase2), NameFilter(includePhrase2, Zstring()));
+ }
+}
+
+
+std::vector<Zstring> splitByDelimiter(const Zstring& filterPhrase); //keep external linkage for unit test
+}
+
+#endif //HARD_FILTER_H_825780275842758345
diff --git a/FreeFileSync/Source/base/perf_check.cpp b/FreeFileSync/Source/base/perf_check.cpp
index 7ea523e8..9493a8a2 100755
--- a/FreeFileSync/Source/base/perf_check.cpp
+++ b/FreeFileSync/Source/base/perf_check.cpp
@@ -67,7 +67,7 @@ std::optional<std::wstring> PerfCheck::getBytesPerSecond() const
const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeSpeed_);
if (!numeric::isNull(timeDelta))
- return formatFilesizeShort(numeric::round(bytesDelta / timeDelta)) + _("/sec");
+ return replaceCpy(_("%x/sec"), L"%x", formatFilesizeShort(numeric::round(bytesDelta / timeDelta)));
return {};
}
@@ -78,7 +78,7 @@ std::optional<std::wstring> PerfCheck::getItemsPerSecond() const
const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeSpeed_);
if (!numeric::isNull(timeDelta))
- return replaceCpy(_("%x items/sec"), L"%x", formatTwoDigitPrecision(itemsDelta / timeDelta));
+ return replaceCpy(_("%x/sec"), L"%x", replaceCpy(_("%x items"), L"%x", formatTwoDigitPrecision(itemsDelta / timeDelta)));
return {};
}
diff --git a/FreeFileSync/Source/base/process_xml.cpp b/FreeFileSync/Source/base/process_xml.cpp
index e3d1b89f..b8081253 100755
--- a/FreeFileSync/Source/base/process_xml.cpp
+++ b/FreeFileSync/Source/base/process_xml.cpp
@@ -999,7 +999,7 @@ void readConfig(const XmlIn& in, DirectionConfig& dirCfg)
}
-void readConfig(const XmlIn& in, SyncConfig& syncCfg, std::map<AbstractPath, size_t>& deviceParallelOps, int formatVer)
+void readConfig(const XmlIn& in, SyncConfig& syncCfg, std::map<AfsDevice, size_t>& deviceParallelOps, int formatVer)
{
readConfig(in, syncCfg.directionCfg);
@@ -1086,7 +1086,7 @@ void readConfig(const XmlIn& in, FilterConfig& filter, int formatVer)
}
-void readConfig(const XmlIn& in, LocalPairConfig& lpc, std::map<AbstractPath, size_t>& deviceParallelOps, int formatVer)
+void readConfig(const XmlIn& in, LocalPairConfig& lpc, std::map<AfsDevice, size_t>& deviceParallelOps, int formatVer)
{
//read folder pairs
in["Left" ](lpc.folderPathPhraseLeft);
@@ -1884,7 +1884,7 @@ void writeConfig(const DirectionConfig& dirCfg, XmlOut& out)
}
-void writeConfig(const SyncConfig& syncCfg, const std::map<AbstractPath, size_t>& deviceParallelOps, XmlOut& out)
+void writeConfig(const SyncConfig& syncCfg, const std::map<AfsDevice, size_t>& deviceParallelOps, XmlOut& out)
{
writeConfig(syncCfg.directionCfg, out);
@@ -1921,7 +1921,7 @@ void writeConfig(const FilterConfig& filter, XmlOut& out)
}
-void writeConfig(const LocalPairConfig& lpc, const std::map<AbstractPath, size_t>& deviceParallelOps, XmlOut& out)
+void writeConfig(const LocalPairConfig& lpc, const std::map<AfsDevice, size_t>& deviceParallelOps, XmlOut& out)
{
XmlOut outPair = out.ref().addChild("Pair");
diff --git a/FreeFileSync/Source/base/resolve_path.cpp b/FreeFileSync/Source/base/resolve_path.cpp
index 49699fa4..b43d3463 100755
--- a/FreeFileSync/Source/base/resolve_path.cpp
+++ b/FreeFileSync/Source/base/resolve_path.cpp
@@ -61,7 +61,7 @@ Zstring resolveRelativePath(const Zstring& relativePath)
if (startsWith(relativePath, "~/"))
return appendSeparator(*homeDir) + afterFirst(relativePath, '/', IF_MISSING_RETURN_NONE);
- else if (relativePath == "~")
+ else //relativePath == "~"
return *homeDir;
}
@@ -173,7 +173,7 @@ Zstring expandVolumeName(Zstring pathPhrase) // [volname]:\folder [volnam
}
-void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, LessFilePath>& output)
+void getFolderAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, LessNativePath>& output)
{
//3. environment variables: C:\Users\<user> -> %UserProfile%
@@ -203,20 +203,20 @@ void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, L
const Zstring pathExp = fff::expandMacros(pathPhrase);
if (pathExp != pathPhrase)
if (output.insert(pathExp).second)
- getDirectoryAliasesRecursive(pathExp, output); //recurse!
+ getFolderAliasesRecursive(pathExp, output); //recurse!
}
}
}
-std::vector<Zstring> fff::getDirectoryAliases(const Zstring& folderPathPhrase)
+std::vector<Zstring> fff::getFolderPathAliases(const Zstring& folderPathPhrase)
{
- const Zstring dirPath = trimCpy(folderPathPhrase, true, false);
+ const Zstring dirPath = trimCpy(folderPathPhrase);
if (dirPath.empty())
return {};
- std::set<Zstring, LessFilePath> tmp;
- getDirectoryAliasesRecursive(dirPath, tmp);
+ std::set<Zstring, LessNativePath> tmp;
+ getFolderAliasesRecursive(dirPath, tmp);
tmp.erase(dirPath);
tmp.erase(Zstring());
@@ -233,9 +233,7 @@ Zstring fff::getResolvedFilePath(const Zstring& pathPhrase) //noexcept
path = expandMacros(path); //expand before trimming!
//remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix()
- trim(path, true, false);
- //don't remove all whitespace from right, e.g. 0xa0 may be used as part of a folder name
- trim(path, false, true, [](Zchar c) { return c == Zstr(' '); });
+ trim(path); //attention: don't remove all whitespace from right, e.g. 0xa0 may be used as part of a folder name
path = expandVolumeName(path); //may block for slow USB sticks and idle HDDs!
diff --git a/FreeFileSync/Source/base/resolve_path.h b/FreeFileSync/Source/base/resolve_path.h
index dc23d1de..8b8f131e 100755
--- a/FreeFileSync/Source/base/resolve_path.h
+++ b/FreeFileSync/Source/base/resolve_path.h
@@ -27,7 +27,7 @@ Zstring getResolvedFilePath(const Zstring& pathPhrase); //noexcept
//macro substitution only
Zstring expandMacros(const Zstring& text);
-std::vector<Zstring> getDirectoryAliases(const Zstring& folderPathPhrase); //may block for slow USB sticks when resolving [<volume name>]
+std::vector<Zstring> getFolderPathAliases(const Zstring& folderPathPhrase); //may block for slow USB sticks when resolving [<volume name>]
}
diff --git a/FreeFileSync/Source/base/status_handler_impl.h b/FreeFileSync/Source/base/status_handler_impl.h
index af578b82..bf4d7789 100755
--- a/FreeFileSync/Source/base/status_handler_impl.h
+++ b/FreeFileSync/Source/base/status_handler_impl.h
@@ -390,15 +390,15 @@ struct ParallelContext
namespace
{
void massParallelExecute(const std::vector<std::pair<AbstractPath, ParallelWorkItem>>& workload,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
const std::string& threadGroupName,
ProcessCallback& callback /*throw X*/)
{
using namespace zen;
- std::map<AbstractPath, std::vector<const std::pair<AbstractPath, ParallelWorkItem>*>> perDeviceWorkload;
+ std::map<AfsDevice, std::vector<const std::pair<AbstractPath, ParallelWorkItem>*>> perDeviceWorkload;
for (const auto& item : workload)
- perDeviceWorkload[AFS::getRootPath(item.first)].push_back(&item);
+ perDeviceWorkload[item.first.afsDevice].push_back(&item);
struct ThreadGroupContext
{
@@ -412,29 +412,29 @@ void massParallelExecute(const std::vector<std::pair<AbstractPath, ParallelWorkI
AsyncCallback acb; //manage life time: enclose ThreadGroup's!!!
std::atomic<int> activeDeviceCount(perDeviceWorkload.size()); //
- Protected<std::map<AbstractPath, ThreadGroupContext>*> deviceThreadGroupsShared; //
+ Protected<std::map<AfsDevice, ThreadGroupContext>*> deviceThreadGroupsShared; //
//---------------------------------------------------------------------------------------------------------
- std::map<AbstractPath, ThreadGroupContext> deviceThreadGroups; //worker threads live here...
+ std::map<AfsDevice, ThreadGroupContext> deviceThreadGroups; //worker threads live here...
//---------------------------------------------------------------------------------------------------------
//Attention: carefully orchestrate access to deviceThreadGroups and its contained worker threads! e.g. synchronize potential access during ~DeviceThreadGroup!
- for (const auto& [rootPath, wl] : perDeviceWorkload)
+ for (const auto& [afsDevice, wl] : perDeviceWorkload)
{
- const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, rootPath);
+ const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, afsDevice);
const size_t statusPrio = deviceThreadGroups.size();
- auto scheduleExtraTask = [&acb, &deviceThreadGroupsShared, rootPath = rootPath /*clang bug :>*/](const AfsPath& afsPath, const ParallelWorkItem& task)
+ auto scheduleExtraTask = [&acb, &deviceThreadGroupsShared, afsDevice = afsDevice /*clang bug :>*/](const AfsPath& afsPath, const ParallelWorkItem& task)
{
- const AbstractPath& itemPath = AFS::appendRelPath(rootPath, afsPath.value);
+ const AbstractPath itemPath(afsDevice, afsPath);
deviceThreadGroupsShared.access([&](auto* deviceThreadGroupsPtr)
{
if (!deviceThreadGroupsPtr)
throw ThreadInterruption();
- ThreadGroupContext& ctx = deviceThreadGroupsPtr->find(rootPath)->second; //exists after construction above!
+ ThreadGroupContext& ctx = deviceThreadGroupsPtr->find(afsDevice)->second; //exists after construction above!
ctx.threadGroup.run([&acb, statusPrio = ctx.statusPrio, itemPath, task, &scheduleExtraTask = ctx.scheduleExtraTask]
{
@@ -446,10 +446,10 @@ void massParallelExecute(const std::vector<std::pair<AbstractPath, ParallelWorkI
});
});
};
- deviceThreadGroups.emplace(rootPath, ThreadGroupContext(parallelOps,
- threadGroupName + " " + utfTo<std::string>(AFS::getDisplayPath(rootPath)),
- statusPrio,
- scheduleExtraTask));
+ deviceThreadGroups.emplace(afsDevice, ThreadGroupContext(parallelOps,
+ threadGroupName + " " + utfTo<std::string>(AFS::getDisplayPath(AbstractPath(afsDevice, AfsPath()))),
+ statusPrio,
+ scheduleExtraTask));
}
deviceThreadGroupsShared.access([&](auto*& deviceThreadGroupsPtr) { deviceThreadGroupsPtr = &deviceThreadGroups; });
//[!] deviceThreadGroups is shared with worker threads from here on!
diff --git a/FreeFileSync/Source/base/structures.cpp b/FreeFileSync/Source/base/structures.cpp
index e39084ea..ea5f9ad3 100755
--- a/FreeFileSync/Source/base/structures.cpp
+++ b/FreeFileSync/Source/base/structures.cpp
@@ -10,7 +10,7 @@
#include <ctime>
#include <zen/i18n.h>
#include <zen/time.h>
-#include "hard_filter.h"
+#include "path_filter.h"
#include "../fs/concrete.h"
using namespace zen;
@@ -226,37 +226,35 @@ std::wstring fff::getSyncVariantName(const MainConfiguration& mainCfg)
}
-size_t fff::getDeviceParallelOps(const std::map<AbstractPath, size_t>& deviceParallelOps, const AbstractPath& ap)
+size_t fff::getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice)
{
- const AbstractPath& rootPath = AFS::getRootPath(ap);
- auto it = deviceParallelOps.find(rootPath);
+ auto it = deviceParallelOps.find(afsDevice);
return std::max<size_t>(it != deviceParallelOps.end() ? it->second : 1, 1);
}
-void fff::setDeviceParallelOps(std::map<AbstractPath, size_t>& deviceParallelOps, const AbstractPath& ap, size_t parallelOps)
+void fff::setDeviceParallelOps(std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice, size_t parallelOps)
{
assert(parallelOps > 0);
- const AbstractPath rootPath = AFS::getRootPath(ap);
- if (!AFS::isNullPath(rootPath))
+ if (!AFS::isNullDevice(afsDevice))
{
if (parallelOps > 1)
- deviceParallelOps[rootPath] = parallelOps;
+ deviceParallelOps[afsDevice] = parallelOps;
else
- deviceParallelOps.erase(rootPath);
+ deviceParallelOps.erase(afsDevice);
}
}
-size_t fff::getDeviceParallelOps(const std::map<AbstractPath, size_t>& deviceParallelOps, const Zstring& folderPathPhrase)
+size_t fff::getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase)
{
- return getDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase));
+ return getDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase).afsDevice);
}
-void fff::setDeviceParallelOps(std::map<AbstractPath, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps)
+void fff::setDeviceParallelOps(std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps)
{
- setDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase), parallelOps);
+ setDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase).afsDevice, parallelOps);
}
@@ -443,13 +441,11 @@ FilterConfig mergeFilterConfig(const FilterConfig& global, const FilterConfig& l
FilterConfig out = local;
//hard filter
- if (NameFilter::isNull(out.includeFilter, Zstring())) //fancy way of checking for "*" include
+ if (NameFilter::isNull(local.includeFilter, Zstring())) //fancy way of checking for "*" include
out.includeFilter = global.includeFilter;
- //else: if both global and local include filter contain data, only local filter is preserved
+ //else : if both global and local include filters are set, only local filter is preserved
- trim(out.excludeFilter, true, false);
- out.excludeFilter = global.excludeFilter + Zstr("\n") + out.excludeFilter;
- trim(out.excludeFilter, true, false);
+ out.excludeFilter = trimCpy(trimCpy(global.excludeFilter) + Zstr("\n\n") + trimCpy(local.excludeFilter));
//soft filter
time_t loctimeFrom = 0;
@@ -603,7 +599,7 @@ MainConfiguration fff::merge(const std::vector<MainConfiguration>& mainCfgs)
lpc.localFilter = FilterConfig();
}
- std::map<AbstractPath, size_t> mergedParallelOps;
+ std::map<AfsDevice, size_t> mergedParallelOps;
for (const MainConfiguration& mainCfg : mainCfgs)
for (const auto& [rootPath, parallelOps] : mainCfg.deviceParallelOps)
mergedParallelOps[rootPath] = std::max(mergedParallelOps[rootPath], parallelOps);
diff --git a/FreeFileSync/Source/base/structures.h b/FreeFileSync/Source/base/structures.h
index 8668acd2..f5f6aeb5 100755
--- a/FreeFileSync/Source/base/structures.h
+++ b/FreeFileSync/Source/base/structures.h
@@ -305,7 +305,7 @@ struct FilterConfig
unitSizeMax (unitSizeMaxIn) {}
/*
- Semantics of HardFilter:
+ Semantics of PathFilter:
1. using it creates a NEW folder hierarchy! -> must be considered by <Two way> variant! (fortunately it turns out, doing nothing already has perfect semantics :)
2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder!
*/
@@ -403,7 +403,7 @@ struct MainConfiguration
LocalPairConfig firstPair; //there needs to be at least one pair!
std::vector<LocalPairConfig> additionalPairs;
- std::map<AbstractPath /*device root*/, size_t /*parallel operations*/> deviceParallelOps; //should only include devices with >= 2 parallel ops
+ std::map<AfsDevice, size_t /*parallel operations*/> deviceParallelOps; //should only include devices with >= 2 parallel ops
bool ignoreErrors = false; //true: errors will still be logged
size_t automaticRetryCount = 0;
@@ -418,11 +418,10 @@ struct MainConfiguration
std::wstring getCompVariantName(const MainConfiguration& mainCfg);
std::wstring getSyncVariantName(const MainConfiguration& mainCfg);
-size_t getDeviceParallelOps(const std::map<AbstractPath, size_t>& deviceParallelOps, const AbstractPath& ap);
-void setDeviceParallelOps( std::map<AbstractPath, size_t>& deviceParallelOps, const AbstractPath& ap, size_t parallelOps);
-size_t getDeviceParallelOps(const std::map<AbstractPath, size_t>& deviceParallelOps, const Zstring& folderPathPhrase);
-void setDeviceParallelOps( std::map<AbstractPath, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps);
-
+size_t getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice);
+void setDeviceParallelOps( std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice, size_t parallelOps);
+size_t getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase);
+void setDeviceParallelOps( std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps);
inline
bool operator==(const MainConfiguration& lhs, const MainConfiguration& rhs)
diff --git a/FreeFileSync/Source/base/synchronization.cpp b/FreeFileSync/Source/base/synchronization.cpp
index 25723ce2..fbb84f4d 100755
--- a/FreeFileSync/Source/base/synchronization.cpp
+++ b/FreeFileSync/Source/base/synchronization.cpp
@@ -485,8 +485,8 @@ AFS::ItemType getItemType(const AbstractPath& ap, std::mutex& singleThread) //th
{ return parallelScope([ap] { return AFS::getItemType(ap); /*throw FileError*/ }, singleThread); }
inline
-std::optional<AFS::ItemType> getItemTypeIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
-{ return parallelScope([ap] { return AFS::getItemTypeIfExists(ap); /*throw FileError*/ }, singleThread); }
+std::optional<AFS::ItemType> itemStillExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
+{ return parallelScope([ap] { return AFS::itemStillExists(ap); /*throw FileError*/ }, singleThread); }
inline
bool removeFileIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError
@@ -1180,7 +1180,7 @@ void FolderPairSyncer::setup2StepMove(FilePair& sourceFile, //throw FileError, T
//generate (hopefully) unique file name to avoid clashing with some remnant ffs_tmp file
const Zstring shortGuid = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(getCrc16(generateGUID())));
const Zstring fileName = sourceFile.getItemName<side>();
- auto it = find_last(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "."
+ auto it = findLast(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "."
const Zstring sourceRelPathTmp = Zstring(fileName.begin(), it) + Zstr('.') + shortGuid + AFS::TEMP_FILE_ENDING;
//-------------------------------------------------------------------------------------------
@@ -1246,7 +1246,7 @@ auto FolderPairSyncer::createMoveTargetFolder(FileSystemObject& fsObj) -> CmtfSt
reportInfo(txtCreatingFolder_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption
//shallow-"copying" a folder might not fail if source is missing, so we need to check this first:
- if (parallel::getItemTypeIfExists(parentFolder->getAbstractPath<sideSrc>(), singleThread_)) //throw FileError
+ if (parallel::itemStillExists(parentFolder->getAbstractPath<sideSrc>(), singleThread_)) //throw FileError
{
AsyncItemStatReporter statReporter(1, 0, acb_);
try //target existing: undefined behavior! (fail/overwrite)
@@ -1613,7 +1613,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
catch (const FileError& e)
{
bool sourceWasDeleted = false;
- try { sourceWasDeleted = !parallel::getItemTypeIfExists(file.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ }
+ try { sourceWasDeleted = !parallel::itemStillExists(file.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ }
catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant
//do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it!
@@ -1637,7 +1637,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp)
AsyncItemStatReporter statReporter(1, 0, acb_);
delHandlerTrg.removeFileWithCallback({ file.getAbstractPath<sideTrg>(), file.getAttributes<sideTrg>() },
- file.getRelativePath<sideTrg>(), statReporter, singleThread_); //throw FileError, X
+ file.getRelativePath<sideTrg>(), statReporter, singleThread_); //throw FileError, X
file.removeObject<sideTrg>(); //update FilePair
}
break;
@@ -1831,12 +1831,12 @@ void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation sy
}
catch (const FileError& e)
{
- bool sourceWasDeleted = false;
- try { sourceWasDeleted = !parallel::getItemTypeIfExists(symlink.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ }
+ bool sourceExists = true;
+ try { sourceExists = !!parallel::itemStillExists(symlink.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ }
catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant
//do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it!
- if (sourceWasDeleted)
+ if (!sourceExists)
{
//even if the source item does not exist anymore, significant I/O work was done => report
statReporter.reportDelta(1, 0);
@@ -1964,7 +1964,7 @@ void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation sy
reportInfo(txtCreatingFolder_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption
//shallow-"copying" a folder might not fail if source is missing, so we need to check this first:
- if (parallel::getItemTypeIfExists(folder.getAbstractPath<sideSrc>(), singleThread_)) //throw FileError
+ if (parallel::itemStillExists(folder.getAbstractPath<sideSrc>(), singleThread_)) //throw FileError
{
AsyncItemStatReporter statReporter(1, 0, acb_);
try
@@ -2170,9 +2170,8 @@ bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, Proc
{
if (baseFolder.isAvailable<sideSrc>()) //copy file permissions
{
- if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(baseFolderPath))
- if (AFS::getParentFolderPath(*parentPath)) //not device root
- AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError
+ if (std::optional<AbstractPath> parentPath = AFS::getParentPath(baseFolderPath))
+ AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError
AFS::copyNewFolder(baseFolder.getAbstractPath<sideSrc>(), baseFolderPath, copyFilePermissions); //throw FileError
}
@@ -2217,7 +2216,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
bool runWithBackgroundPriority,
const std::vector<FolderPairSyncCfg>& syncConfig,
FolderComparison& folderCmp,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
WarningDialogs& warnings,
ProcessCallback& callback)
{
@@ -2278,7 +2277,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
std::vector<SyncStatistics::ConflictInfo> unresolvedConflicts;
- std::vector<std::tuple<AbstractPath, const HardFilter*, bool /*write access*/>> readWriteCheckBaseFolders;
+ std::vector<std::tuple<AbstractPath, const PathFilter*, bool /*write access*/>> readWriteCheckBaseFolders;
std::vector<std::pair<AbstractPath, AbstractPath>> significantDiffPairs;
@@ -2287,8 +2286,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//status of base directories which are set to DeletionPolicy::RECYCLER (and contain actual items to be deleted)
std::map<AbstractPath, bool> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder!
- std::set<AbstractPath> verCheckVersioningPaths;
- std::vector<std::pair<AbstractPath, const HardFilter*>> verCheckBaseFolderPaths; //hard filter creates new logical hierarchies for otherwise equal AbstractPath...
+ std::set<AbstractPath> verCheckVersioningPaths;
+ std::vector<std::pair<AbstractPath, const PathFilter*>> verCheckBaseFolderPaths; //hard filter creates new logical hierarchies for otherwise equal AbstractPath...
//start checking folder pairs
for (auto itBase = begin(folderCmp); itBase != end(folderCmp); ++itBase)
@@ -2302,8 +2301,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
append(unresolvedConflicts, folderPairStat.getConflicts());
//exclude a few pathological cases (including empty left, right folders)
- if (AFS::equalAbstractPath(baseFolder.getAbstractPath< LEFT_SIDE>(),
- baseFolder.getAbstractPath<RIGHT_SIDE>()))
+ if (baseFolder.getAbstractPath< LEFT_SIDE>() ==
+ baseFolder.getAbstractPath<RIGHT_SIDE>())
{
jobType[folderIndex] = FolderPairJobType::SKIP;
continue;
@@ -2384,12 +2383,12 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
//prepare: check if versioning path itself will be synchronized (and was not excluded via filter)
verCheckVersioningPaths.insert(versioningFolderPath);
- verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath<LEFT_SIDE >(), &baseFolder.getFilter());
+ verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(), &baseFolder.getFilter());
verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath<RIGHT_SIDE>(), &baseFolder.getFilter());
}
//prepare: check if folders are used by multiple pairs in read/write access
- readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath<LEFT_SIDE >(), &baseFolder.getFilter(), writeLeft);
+ readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(), &baseFolder.getFilter(), writeLeft);
readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath<RIGHT_SIDE>(), &baseFolder.getFilter(), writeRight);
//check if more than 50% of total number of files/dirs are to be created/overwritten/deleted
@@ -2506,8 +2505,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
if (std::get<bool>(*it)) //write access
for (auto it2 = readWriteCheckBaseFolders.begin(); it2 != readWriteCheckBaseFolders.end(); ++it2)
if (!std::get<bool>(*it2) || it < it2) //avoid duplicate comparisons
- if (std::optional<PathDependency> pd = getPathDependency(std::get<AbstractPath>(*it), *std::get<const HardFilter*>(*it),
- std::get<AbstractPath>(*it2), *std::get<const HardFilter*>(*it2)))
+ if (std::optional<PathDependency> pd = getPathDependency(std::get<AbstractPath>(*it), *std::get<const PathFilter*>(*it),
+ std::get<AbstractPath>(*it2), *std::get<const PathFilter*>(*it2)))
{
dependentFolders.insert(pd->basePathParent);
dependentFolders.insert(pd->basePathChild);
@@ -2537,7 +2536,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
{
std::wstring line = L"\n\n" + _("Versioning folder:") + L" \t" + AFS::getDisplayPath(versioningFolderPath) +
L"\n" + _("Base folder:") + L" \t" + AFS::getDisplayPath(folderPath);
- if (AFS::equalAbstractPath(pd->basePathParent, folderPath) && !pd->relPath.empty())
+ if (pd->basePathParent == folderPath && !pd->relPath.empty())
line += L"\n" + _("Exclude:") + L" \t" + utfTo<std::wstring>(FILE_NAME_SEPARATOR + pd->relPath + FILE_NAME_SEPARATOR);
uniqueMsgs[folderPath] = line;
@@ -2660,10 +2659,10 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime
catch (...) { assert(false); } //what is this?
);
- size_t parallelOps = std::max(getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath<LEFT_SIDE >()),
- getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath<RIGHT_SIDE>()));
+ size_t parallelOps = std::max(getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath< LEFT_SIDE>().afsDevice),
+ getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath<RIGHT_SIDE>().afsDevice));
if (folderPairCfg.handleDeletion == DeletionPolicy::VERSIONING)
- parallelOps = std::max(parallelOps, getDeviceParallelOps(deviceParallelOps, versioningFolderPath));
+ parallelOps = std::max(parallelOps, getDeviceParallelOps(deviceParallelOps, versioningFolderPath.afsDevice));
FolderPairSyncer::SyncCtx syncCtx =
{
diff --git a/FreeFileSync/Source/base/synchronization.h b/FreeFileSync/Source/base/synchronization.h
index 7bd5db5e..08d6ca55 100755
--- a/FreeFileSync/Source/base/synchronization.h
+++ b/FreeFileSync/Source/base/synchronization.h
@@ -94,7 +94,7 @@ void synchronize(const std::chrono::system_clock::time_point& syncStartTime,
bool runWithBackgroundPriority,
const std::vector<FolderPairSyncCfg>& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise!
FolderComparison& folderCmp, //
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
WarningDialogs& warnings,
ProcessCallback& callback);
}
diff --git a/FreeFileSync/Source/base/versioning.cpp b/FreeFileSync/Source/base/versioning.cpp
index 64536076..aa0b0bdf 100755
--- a/FreeFileSync/Source/base/versioning.cpp
+++ b/FreeFileSync/Source/base/versioning.cpp
@@ -15,13 +15,13 @@ Zstring getDotExtension(const Zstring& filePath) //including "." if extension is
//const Zstring& extension = getFileExtension(filePath);
//return extension.empty() ? extension : Zstr('.') + extension;
- auto it = find_last(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR);
+ auto it = findLast(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR);
if (it == filePath.end())
it = filePath.begin();
else
++it;
- return Zstring(find_last(it, filePath.end(), Zstr('.')), filePath.end());
+ return Zstring(findLast(it, filePath.end(), Zstr('.')), filePath.end());
};
}
@@ -30,7 +30,7 @@ Zstring getDotExtension(const Zstring& filePath) //including "." if extension is
//or "Sample 2012-05-15 131513"
std::pair<time_t, Zstring> fff::impl::parseVersionedFileName(const Zstring& fileName)
{
- const StringRef<const Zchar> ext(find_last(fileName.begin(), fileName.end(), Zstr('.')), fileName.end());
+ const StringRef<const Zchar> ext(findLast(fileName.begin(), fileName.end(), Zstr('.')), fileName.end());
if (fileName.size() < 2 * ext.length() + 18)
return {};
@@ -111,43 +111,20 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract
auto fixTargetPathIssues = [&](const FileError& prevEx) //throw FileError
{
- std::optional<AFS::PathStatus> psTmp;
- try { psTmp = AFS::getPathStatus(targetPath); /*throw FileError*/ }
- catch (const FileError& e) { throw FileError(prevEx.toString(), e.toString()); }
- const AFS::PathStatus& ps = *psTmp;
- //previous exception contains context-level information, but current exception is the immediate problem => combine both
- //=> e.g. prevEx might be about missing parent folder; FFS considers session faulty and tries to create a new one,
- //which might fail with: LIBSSH2_ERROR_AUTHENTICATION_FAILED (due to limit on #sessions?) https://freefilesync.org/forum/viewtopic.php?t=4765#p16016
-
- if (ps.relPath.empty()) //already existing
+ try
{
+ AFS::getItemType(targetPath); //throw FileError
+ //already existing! =>
if (deletionError)
std::rethrow_exception(deletionError);
throw prevEx; //yes, slicing, but not relevant here
}
+ catch (FileError&) {} //not yet existing or access error
//parent folder missing => create + retry
//parent folder existing => maybe created shortly after move attempt by parallel thread! => retry
- AbstractPath intermediatePath = ps.existingPath;
-
- std::for_each(ps.relPath.begin(), ps.relPath.end() - 1, [&](const Zstring& itemName)
- {
- try
- {
- AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError
- }
- catch (FileError&)
- {
- try //already existing => possible, if moveExistingItemToVersioning() is run in parallel
- {
- if (AFS::getItemType(intermediatePath) != AFS::ItemType::FILE) //throw FileError
- return; //=continue
- }
- catch (FileError&) {}
-
- throw;
- }
- });
+ if (const std::optional<AbstractPath> targetParentPath = AFS::getParentPath(targetPath))
+ AFS::createFolderIfMissingRecursion(*targetParentPath); //throw FileError
};
try //first try to move directly without copying
@@ -191,7 +168,7 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract
void FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring& relativePath, const IOCallback& notifyUnbufferedIO) const //throw FileError
{
- if (std::optional<AFS::ItemType> type = AFS::getItemTypeIfExists(fileDescr.path)) //throw FileError
+ if (std::optional<AFS::ItemType> type = AFS::itemStillExists(fileDescr.path)) //throw FileError
{
if (*type == AFS::ItemType::SYMLINK)
revisionSymlinkImpl(fileDescr.path, relativePath, nullptr /*onBeforeMove*/); //throw FileError
@@ -228,7 +205,7 @@ void FileVersioner::revisionFileImpl(const FileDescriptor& fileDescr, const Zstr
void FileVersioner::revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath) const //throw FileError
{
- if (AFS::getItemTypeIfExists(linkPath)) //throw FileError
+ if (AFS::itemStillExists(linkPath)) //throw FileError
revisionSymlinkImpl(linkPath, relativePath, nullptr /*onBeforeMove*/); //throw FileError
//else -> missing source item is not an error => check BEFORE deleting target
}
@@ -253,7 +230,7 @@ void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring
const IOCallback& notifyUnbufferedIO) const
{
//no error situation if directory is not existing! manual deletion relies on it!
- if (std::optional<AFS::ItemType> type = AFS::getItemTypeIfExists(folderPath)) //throw FileError
+ if (std::optional<AFS::ItemType> type = AFS::itemStillExists(folderPath)) //throw FileError
{
if (*type == AFS::ItemType::SYMLINK) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well!
revisionSymlinkImpl(folderPath, relativePath, onBeforeFileMove); //throw FileError
@@ -317,7 +294,7 @@ struct VersionInfo
AbstractPath filePath;
bool isSymlink = false;
};
-using VersionInfoMap = std::map<Zstring, std::vector<VersionInfo>, LessFilePath>; //relPathOrig => <version infos>
+using VersionInfoMap = std::map<Zstring, std::vector<VersionInfo>>; //relPathOrig => <version infos>
//subfolder\Sample.txt 2012-05-15 131513.txt => subfolder\Sample.txt version:2012-05-15 131513
//2012-05-15 131513\subfolder\Sample.txt => " "
@@ -330,7 +307,7 @@ void findFileVersions(VersionInfoMap& versions,
{
auto addVersion = [&](const Zstring& fileName, const Zstring& fileNameOrig, time_t versionTime, bool isSymlink)
{
- const Zstring& relPathOrig = AFS::appendPaths(relPathOrigParent, fileNameOrig, FILE_NAME_SEPARATOR);
+ const Zstring& relPathOrig = nativeAppendPaths(relPathOrigParent, fileNameOrig);
const AbstractPath& filePath = AFS::appendRelPath(parentFolderPath, fileName);
versions[relPathOrig].push_back(VersionInfo{ versionTime, filePath, isSymlink });
@@ -372,7 +349,7 @@ void findFileVersions(VersionInfoMap& versions,
findFileVersions(versions, attrAndSub.second,
AFS::appendRelPath(parentFolderPath, folderName),
- AFS::appendPaths(relPathOrigParent, folderName, FILE_NAME_SEPARATOR),
+ nativeAppendPaths(relPathOrigParent, folderName),
versionTimeParent);
}
}
@@ -393,7 +370,7 @@ void getFolderItemCount(std::map<AbstractPath, size_t>& folderItemCount, const F
bool fff::operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolder& rhs)
{
- const int cmp = AFS::compareAbstractPath(lhs.versioningFolderPath, rhs.versioningFolderPath);
+ const int cmp = AFS::comparePath(lhs.versioningFolderPath, rhs.versioningFolderPath);
if (cmp != 0)
return cmp < 0;
@@ -410,27 +387,39 @@ bool fff::operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolde
}
-void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolders,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& folderLimits,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
ProcessCallback& callback /*throw X*/)
{
//--------- determine existing folder paths for traversal ---------
std::set<DirectoryKey> foldersToRead;
+ std::set<VersioningLimitFolder> folderLimitsNorm; //get rid of folder aliases (e.g. path differing in case)
{
- std::set<AbstractPath> folderPaths;
- for (const VersioningLimitFolder& vlf : limitFolders)
+ std::set<AbstractPath> pathsToCheck;
+ std::vector<VersioningLimitFolder> folderLimitsTmp;
+
+ for (const VersioningLimitFolder& vlf : folderLimits)
if (vlf.versionMaxAgeDays > 0 || vlf.versionCountMax > 0) //only analyze versioning folders when needed!
- folderPaths.emplace(vlf.versioningFolderPath);
+ {
+ pathsToCheck.insert(vlf.versioningFolderPath);
+ folderLimitsTmp.push_back(vlf);
+ }
//we don't want to show an error if version path does not yet exist!
tryReportingError([&]
{
- const FolderStatus status = getFolderStatusNonBlocking(folderPaths, deviceParallelOps, //re-check *all* directories on each try!
+ const FolderStatus status = getFolderStatusNonBlocking(pathsToCheck, deviceParallelOps, //re-check *all* directories on each try!
false /*allowUserInteraction*/, callback); //throw X
-
foldersToRead.clear();
for (const AbstractPath& folderPath : status.existing)
- foldersToRead.emplace(DirectoryKey({ folderPath, std::make_shared<NullFilter>(), SymLinkHandling::DIRECT }));
+ foldersToRead.insert(DirectoryKey({ getNormalizedPath(status, folderPath), makeSharedRef<NullFilter>(), SymLinkHandling::DIRECT }));
+
+ folderLimitsNorm.clear();
+ for (VersioningLimitFolder vlf : folderLimitsTmp)
+ {
+ vlf.versioningFolderPath = getNormalizedPath(status, vlf.versioningFolderPath);
+ folderLimitsNorm.insert(vlf);
+ }
if (!status.failedChecks.empty())
{
@@ -465,7 +454,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde
return AFS::TraverserCallback::ON_ERROR_CONTINUE;
};
- const std::wstring textScanning = _("Searching for excess file versions:") + L" ";
+ const std::wstring textScanning = _("Searching for old file versions:") + L" ";
auto onStatusUpdate = [&](const std::wstring& statusLine, int itemsTotal)
{
@@ -499,8 +488,6 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde
//make sure the versioning folder is never found empty and is not deleted:
++folderItemCount[versioningFolderPath];
- warn_static("TODO: unicode normalization, case-sensitivity!")
-
//similarly, failed folder traversal should not make folders look empty:
for (const auto& [relPath, errorMsg] : folderVal.failedFolderReads) ++folderItemCount[AFS::appendRelPath(versioningFolderPath, relPath)];
for (const auto& [relPath, errorMsg] : folderVal.failedItemReads ) ++folderItemCount[AFS::appendRelPath(versioningFolderPath, beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE))];
@@ -518,7 +505,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde
return localToTimeT(tc); //returns -1 on error => swallow => no versions trimmed by versionMaxAgeDays
}();
- for (const VersioningLimitFolder& vlf : limitFolders)
+ for (const VersioningLimitFolder& vlf : folderLimitsNorm)
{
auto it = versionDetails.find(vlf.versioningFolderPath);
if (it != versionDetails.end())
@@ -553,7 +540,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde
//--------- remove excess file versions ---------
Protected<std::map<AbstractPath, size_t>&> folderItemCountShared(folderItemCount);
- const std::wstring textRemoving = _("Removing excess file versions:") + L" ";
+ const std::wstring textRemoving = _("Removing old file versions:") + L" ";
const std::wstring textDeletingFolder = _("Deleting folder %x");
ParallelWorkItem deleteEmptyFolderTask;
@@ -566,12 +553,12 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde
}, ctx.acb);
if (errMsg.empty())
- if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(ctx.itemPath))
+ if (std::optional<AbstractPath> parentPath = AFS::getParentPath(ctx.itemPath))
{
bool scheduleDelete = false;
folderItemCountShared.access([&](auto& folderItemCount2) { scheduleDelete = --folderItemCount2[*parentPath] == 0; });
if (scheduleDelete)
- ctx.scheduleExtraTask(AfsPath(AFS::getRootRelativePath(*parentPath)), deleteEmptyFolderTask); //throw ThreadInterruption
+ ctx.scheduleExtraTask(parentPath->afsPath, deleteEmptyFolderTask); //throw ThreadInterruption
}
};
@@ -594,13 +581,13 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde
}, ctx.acb);
if (errMsg.empty())
- if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(ctx.itemPath))
+ if (std::optional<AbstractPath> parentPath = AFS::getParentPath(ctx.itemPath))
{
bool scheduleDelete = false;
folderItemCountShared.access([&](auto& folderItemCount2) { scheduleDelete = --folderItemCount2[*parentPath] == 0; });
if (scheduleDelete)
- ctx.scheduleExtraTask(AfsPath(AFS::getRootRelativePath(*parentPath)), deleteEmptyFolderTask); //throw ThreadInterruption
- assert(AFS::getRootPath(*parentPath) == AFS::getRootPath(ctx.itemPath));
+ ctx.scheduleExtraTask(parentPath->afsPath, deleteEmptyFolderTask); //throw ThreadInterruption
+ assert(parentPath->afsDevice == ctx.itemPath.afsDevice);
}
warn_static("get rid of scheduleExtraTask and recursively delete parent folders!? need scheduleExtraTask for something else?")
diff --git a/FreeFileSync/Source/base/versioning.h b/FreeFileSync/Source/base/versioning.h
index f1cefcc5..b2d31090 100755
--- a/FreeFileSync/Source/base/versioning.h
+++ b/FreeFileSync/Source/base/versioning.h
@@ -102,8 +102,8 @@ struct VersioningLimitFolder
bool operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolder& rhs);
-void applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolders,
- const std::map<AbstractPath, size_t>& deviceParallelOps,
+void applyVersioningLimit(const std::set<VersioningLimitFolder>& folderLimits,
+ const std::map<AfsDevice, size_t>& deviceParallelOps,
ProcessCallback& callback /*throw X*/);
diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp
index 33b6ec0e..72b88fde 100755
--- a/FreeFileSync/Source/fs/abstract.cpp
+++ b/FreeFileSync/Source/fs/abstract.cpp
@@ -25,34 +25,40 @@ bool fff::isValidRelPath(const Zstring& relPath)
}
-int AFS::compareAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs)
+int AFS::compareDevice(const AbstractFileSystem& lhs, const AbstractFileSystem& rhs)
{
//note: in worst case, order is guaranteed to be stable only during each program run
- if (typeid(*lhs.afs).before(typeid(*rhs.afs)))
+ if (typeid(lhs).before(typeid(rhs)))
return -1;
- if (typeid(*rhs.afs).before(typeid(*lhs.afs)))
+ if (typeid(rhs).before(typeid(lhs)))
return 1;
- assert(typeid(*lhs.afs) == typeid(*rhs.afs));
+ assert(typeid(lhs) == typeid(rhs));
//caveat: typeid returns static type for pointers, dynamic type for references!!!
- const int rv = lhs.afs->compareDeviceRootSameAfsType(*rhs.afs);
+ return lhs.compareDeviceSameAfsType(rhs);
+}
+
+
+int AFS::comparePath(const AbstractPath& lhs, const AbstractPath& rhs)
+{
+ const int rv = compareDevice(lhs.afsDevice.ref(), rhs.afsDevice.ref());
if (rv != 0)
return rv;
- return compareFilePath(lhs.afsPath.value, rhs.afsPath.value);
+ return compareString(lhs.afsPath.value, rhs.afsPath.value);
}
-std::optional<AbstractPath> AFS::getParentFolderPath(const AbstractPath& ap)
+std::optional<AbstractPath> AFS::getParentPath(const AbstractPath& ap)
{
- if (const std::optional<AfsPath> parentAfsPath = getParentAfsPath(ap.afsPath))
- return AbstractPath(ap.afs, *parentAfsPath);
+ if (const std::optional<AfsPath> parentAfsPath = getParentPath(ap.afsPath))
+ return AbstractPath(ap.afsDevice, *parentAfsPath);
return {};
}
-std::optional<AfsPath> AFS::getParentAfsPath(const AfsPath& afsPath)
+std::optional<AfsPath> AFS::getParentPath(const AfsPath& afsPath)
{
if (afsPath.value.empty())
return {};
@@ -61,25 +67,6 @@ std::optional<AfsPath> AFS::getParentAfsPath(const AfsPath& afsPath)
}
-void AFS::traverseFolderRecursive(const AbstractPath& basePath, const AFS::TraverserWorkload& workload, size_t parallelOps)
-{
- TraverserWorkloadImpl wlImpl;
- for (const auto& [relPathComponents, cb] : workload)
- {
- AfsPath afsPath = basePath.afsPath;
- for (const Zstring& itemName : relPathComponents)
- {
- assert(!contains(itemName, FILE_NAME_SEPARATOR));
- if (!afsPath.value.empty())
- afsPath.value += FILE_NAME_SEPARATOR;
- afsPath.value += itemName;
- }
- wlImpl.emplace_back(afsPath, cb);
- }
- basePath.afs->traverseFolderRecursive(wlImpl, parallelOps); //throw
-}
-
-
namespace
{
struct FlatTraverserCallback : public AFS::TraverserCallback
@@ -193,9 +180,9 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con
auto copyFilePlain = [&](const AbstractPath& apTargetTmp)
{
//caveat: typeid returns static type for pointers, dynamic type for references!!!
- if (typeid(*apSource.afs) == typeid(*apTargetTmp.afs))
- return apSource.afs->copyFileForSameAfsType(apSource.afsPath, attrSource,
- apTargetTmp, copyFilePermissions, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
+ if (typeid(apSource.afsDevice.ref()) == typeid(apTargetTmp.afsDevice.ref()))
+ return apSource.afsDevice.ref().copyFileForSameAfsType(apSource.afsPath, attrSource,
+ apTargetTmp, copyFilePermissions, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
//target existing: undefined behavior! (fail/overwrite/auto-rename)
//fall back to stream-based file copy:
@@ -203,7 +190,7 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con
throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(AFS::getDisplayPath(apTargetTmp))),
_("Operation not supported for different base folder types."));
- return apSource.afs->copyFileAsStream(apSource.afsPath, attrSource, apTargetTmp, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
+ return apSource.afsDevice.ref().copyFileAsStream(apSource.afsPath, attrSource, apTargetTmp, notifyUnbufferedIO); //throw FileError, ErrorFileLocked
//target existing: undefined behavior! (fail/overwrite/auto-rename)
};
@@ -212,19 +199,27 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con
warn_static("doesnt make sense for Google Drive")
- std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(apTarget);
+ std::optional<AbstractPath> parentPath = AFS::getParentPath(apTarget);
if (!parentPath)
throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(AFS::getDisplayPath(apTarget))), L"Path is device root.");
const Zstring fileName = AFS::getItemName(apTarget);
//- generate (hopefully) unique file name to avoid clashing with some remnant ffs_tmp file
//- do not loop and avoid pathological cases, e.g. https://freefilesync.org/forum/viewtopic.php?t=1592
- const Zstring shortGuid = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(getCrc16(generateGUID())));
- auto it = find_last(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "."
- const Zstring fileNameTmp = Zstring(fileName.begin(), it) + Zstr('.') + shortGuid + TEMP_FILE_ENDING;
+ const Zstring& shortGuid = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(getCrc16(generateGUID())));
+ const Zstring& tmpExt = Zstr('.') + shortGuid + TEMP_FILE_ENDING;
+ auto it = findLast(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "."
+
+ //don't make the temp name longer than the original; avoid hitting file system name length limitations: "lpMaximumComponentLength is commonly 255 characters"
+ if (fileName.size() > 200) //BUT don't trim short names! we want early failure on filename-related issues
+ it = std::min(it, fileName.end() - tmpExt.size());
+ warn_static("utf8 anyone? atribtrarily trimming string, really?")
+
+
+ const Zstring& fileNameTmp = Zstring(fileName.begin(), it) + tmpExt;
const AbstractPath apTargetTmp = AFS::appendRelPath(*parentPath, fileNameTmp);
- //AbstractPath apTargetTmp(apTarget.afs, AfsPath(apTarget.afsPath.value + TEMP_FILE_ENDING));
+ //AbstractPath apTargetTmp(apTarget.afsDevice, AfsPath(apTarget.afsPath.value + TEMP_FILE_ENDING));
//-------------------------------------------------------------------------------------------
const AFS::FileCopyResult result = copyFilePlain(apTargetTmp); //throw FileError, ErrorFileLocked
@@ -268,8 +263,18 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con
void AFS::createFolderIfMissingRecursion(const AbstractPath& ap) //throw FileError
{
- if (!getParentFolderPath(ap)) //device root
- return static_cast<void>(/*ItemType =*/ getItemType(ap)); //throw FileError
+ const std::optional<AbstractPath> parentPath = getParentPath(ap);
+ if (!parentPath) //device root
+ return;
+
+ try //generally we expect that path already exists (see: versioning, base folder, log file path) => check first
+ {
+ if (getItemType(ap) != ItemType::FILE) //throw FileError
+ return;
+ }
+ catch (FileError&) {} //not yet existing or access error? let's find out...
+
+ createFolderIfMissingRecursion(*parentPath); //throw FileError
try
{
@@ -278,82 +283,52 @@ void AFS::createFolderIfMissingRecursion(const AbstractPath& ap) //throw FileErr
}
catch (FileError&)
{
- const PathStatus ps = getPathStatus(ap); //throw FileError
- if (ps.existingType == ItemType::FILE)
- throw;
+ try
+ {
+ if (getItemType(ap) != ItemType::FILE) //throw FileError
+ return; //already existing => possible, if createFolderIfMissingRecursion() is run in parallel
+ }
+ catch (FileError&) {} //not yet existing or access error
+ //catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } -> details needed???
- //ps.relPath.size() == 1 => same createFolderPlain() call from above? Maybe parent folder was created by parallel thread shortly after failure!
- AbstractPath intermediatePath = ps.existingPath;
- for (const Zstring& itemName : ps.relPath)
- try
- {
- createFolderPlain(intermediatePath = appendRelPath(intermediatePath, itemName)); //throw FileError
- }
- catch (FileError&)
- {
- try //already existing => possible, if createFolderIfMissingRecursion() is run in parallel
- {
- if (getItemType(intermediatePath) != ItemType::FILE) //throw FileError
- continue;
- }
- catch (FileError&) {}
-
- throw;
- }
+ throw;
}
}
-//essentially a(n abstract) duplicate of zen::getPathStatus()
-AFS::PathStatusImpl AFS::getPathStatusViaFolderTraversal(const AfsPath& afsPath) const //throw FileError
+std::optional<AFS::ItemType> AFS::itemStillExistsViaFolderTraversal(const AfsPath& afsPath) const //throw FileError
{
- const std::optional<AfsPath> parentAfsPath = getParentAfsPath(afsPath);
try
{
- return { getItemType(afsPath), afsPath, {} }; //throw FileError
+ return getItemType(afsPath); //throw FileError
}
- catch (FileError&)
+ catch (const FileError& e) //not existing or access error
{
+ const std::optional<AfsPath> parentAfsPath = getParentPath(afsPath);
if (!parentAfsPath) //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 = getItemName(afsPath);
- assert(!itemName.empty());
-
- PathStatusImpl ps = getPathStatusViaFolderTraversal(*parentAfsPath); //throw FileError
- if (ps.relPath.empty() &&
- ps.existingType != ItemType::FILE) //obscure, but possible (and not an error)
- try
- {
- traverseFolderFlat(*parentAfsPath, //throw FileError
- [&](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; });
- }
- catch (const ItemType& type) { return { type, afsPath, {} }; } //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)
-
- ps.relPath.push_back(itemName);
- return ps;
-}
+ const Zstring itemName = getItemName(afsPath);
+ assert(!itemName.empty());
-std::optional<AFS::ItemType> AFS::getItemTypeIfExists(const AbstractPath& ap) //throw FileError
-{
- const PathStatus ps = getPathStatus(ap); //throw FileError
- if (ps.relPath.empty())
- return ps.existingType;
- return {};
-}
-
-
-AFS::PathStatus AFS::getPathStatus(const AbstractPath& ap) //throw FileError
-{
- const PathStatusImpl psi = ap.afs->getPathStatus(ap.afsPath); //throw FileError
- return { psi.existingType, AbstractPath(ap.afs, psi.existingAfsPath), psi.relPath };
+ const std::optional<ItemType> parentType = itemStillExistsViaFolderTraversal(*parentAfsPath); //throw FileError
+ if (parentType && *parentType != ItemType::FILE /*obscure, but possible (and not an error)*/)
+ try
+ {
+ traverseFolderFlat(*parentAfsPath, //throw FileError
+ [&](const FileInfo& fi) { if (fi.itemName == itemName) throw ItemType::FILE; },
+ [&](const FolderInfo& fi) { if (fi.itemName == itemName) throw ItemType::FOLDER; },
+ [&](const SymlinkInfo& si) { if (si.itemName == itemName) throw ItemType::SYMLINK; });
+ }
+ catch (const ItemType&) //finding the item after getItemType() previously failed is exceptional
+ {
+ throw e; //yes, slicing
+ }
+ return {};
+ }
}
@@ -404,7 +379,7 @@ void AFS::removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileErro
warn_static("what about parallelOps?")
//no error situation if directory is not existing! manual deletion relies on it!
- if (std::optional<ItemType> type = AFS::getItemTypeIfExists(ap)) //throw FileError
+ if (std::optional<ItemType> type = AFS::itemStillExists(ap)) //throw FileError
{
if (*type == AFS::ItemType::SYMLINK)
{
@@ -432,7 +407,7 @@ bool AFS::removeFileIfExists(const AbstractPath& ap) //throw FileError
{
try
{
- if (!AFS::getItemTypeIfExists(ap)) //throw FileError
+ if (!AFS::itemStillExists(ap)) //throw FileError
return false;
}
catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant
@@ -453,7 +428,7 @@ bool AFS::removeSymlinkIfExists(const AbstractPath& ap) //throw FileError
{
try
{
- if (!AFS::getItemTypeIfExists(ap)) //throw FileError
+ if (!AFS::itemStillExists(ap)) //throw FileError
return false;
}
catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant
@@ -473,7 +448,7 @@ void AFS::removeEmptyFolderIfExists(const AbstractPath& ap) //throw FileError
{
try
{
- if (!AFS::getItemTypeIfExists(ap)) //throw FileError
+ if (!AFS::itemStillExists(ap)) //throw FileError
return;
}
catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant
diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h
index d5de09c7..f7da2887 100755
--- a/FreeFileSync/Source/fs/abstract.h
+++ b/FreeFileSync/Source/fs/abstract.h
@@ -16,11 +16,13 @@
namespace fff
{
-struct AbstractFileSystem;
-
bool isValidRelPath(const Zstring& relPath);
+struct AbstractFileSystem;
+
//==============================================================================================================
+using AfsDevice = zen::SharedRef<const AbstractFileSystem>;
+
struct AfsPath //= path relative to the file system root folder (no leading/traling separator)
{
AfsPath() {}
@@ -28,74 +30,70 @@ struct AfsPath //= path relative to the file system root folder (no leading/tral
Zstring value;
};
-class AbstractPath //THREAD-SAFETY: like an int!
+struct AbstractPath //THREAD-SAFETY: like an int!
{
-public:
- AbstractPath(const std::shared_ptr<const AbstractFileSystem>& afsIn, const AfsPath& afsPathIn) : afs(afsIn), afsPath(afsPathIn) {}
+ AbstractPath(const AfsDevice& afsIn, const AfsPath& afsPathIn) : afsDevice(afsIn), afsPath(afsPathIn) {}
- //template <class T1, class T2> -> don't use forwarding constructor! => it circumvents AfsPath's explicit constructor
- //AbstractPath(T1&& afsIn, T2&& afsPathIn) : afs(std::forward<T1>(afsIn)), afsPath(std::forward<T2>(afsPathIn)) { assert(isValidRelPath(afsPathIn)); }
+ //template <class T1, class T2> -> don't use forwarding constructor: it circumvents AfsPath's explicit constructor!
+ //AbstractPath(T1&& afsIn, T2&& afsPathIn) : afsDevice(std::forward<T1>(afsIn)), afsPath(std::forward<T2>(afsPathIn)) {}
-private:
- friend struct AbstractFileSystem;
-
- std::shared_ptr<const AbstractFileSystem> afs; //always bound; "const AbstractFileSystem" => all accesses expected to be thread-safe!!!
- AfsPath afsPath;
+ AfsDevice afsDevice; //"const AbstractFileSystem" => all accesses expected to be thread-safe!!!
+ AfsPath afsPath; //relative to device root
};
-
//==============================================================================================================
struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model thread-safe access!
{
- static int compareAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs);
+ //=============== convenience =================
+ static Zstring getItemName(const AbstractPath& ap) { assert(getParentPath(ap)); return getItemName(ap.afsPath); }
+ static Zstring getItemName(const AfsPath& afsPath) { using namespace zen; return afterLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); }
- static bool equalAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs) { return compareAbstractPath(lhs, rhs) == 0; }
+ static bool isNullPath(const AbstractPath& ap) { return isNullDevice(ap.afsDevice) /*&& ap.afsPath.value.empty()*/; }
- static bool isNullPath(const AbstractPath& ap) { return ap.afs->isNullFileSystem() /*&& ap.afsPath.value.empty()*/; }
+ static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath);
- static std::wstring getDisplayPath(const AbstractPath& ap) { return ap.afs->getDisplayPath(ap.afsPath); }
+ static std::optional<AbstractPath> getParentPath(const AbstractPath& ap);
+ static std::optional<AfsPath> getParentPath(const AfsPath& afsPath);
+ //=============================================
- static Zstring getInitPathPhrase(const AbstractPath& ap) { return ap.afs->getInitPathPhrase(ap.afsPath); }
+ static int compareDevice(const AbstractFileSystem& lhs, const AbstractFileSystem& rhs);
- static std::optional<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->getNativeItemPath(ap.afsPath); }
+ static int comparePath(const AbstractPath& lhs, const AbstractPath& rhs);
- static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath);
+ static bool isNullDevice(const AfsDevice& afsDevice) { return afsDevice.ref().isNullFileSystem(); }
- static Zstring getItemName(const AbstractPath& ap) { assert(getParentFolderPath(ap)); return getItemName(ap.afsPath); }
+ static std::wstring getDisplayPath(const AbstractPath& ap) { return ap.afsDevice.ref().getDisplayPath(ap.afsPath); }
- static std::optional<AbstractPath> getParentFolderPath(const AbstractPath& ap);
+ static Zstring getInitPathPhrase(const AbstractPath& ap) { return ap.afsDevice.ref().getInitPathPhrase(ap.afsPath); }
- static AbstractPath getRootPath (const AbstractPath& ap) { return AbstractPath(ap.afs, AfsPath()); }
- static Zstring getRootRelativePath(const AbstractPath& ap) { return ap.afsPath.value; }
+ static std::optional<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afsDevice.ref().getNativeItemPath(ap.afsPath); }
//----------------------------------------------------------------------------------------------------------------
- static void connectNetworkFolder(const AbstractPath& ap, bool allowUserInteraction) { return ap.afs->connectNetworkFolder(ap.afsPath, allowUserInteraction); } //throw FileError
+ static void connectNetworkFolder(const AbstractPath& ap, bool allowUserInteraction) { return ap.afsDevice.ref().connectNetworkFolder(ap.afsPath, allowUserInteraction); } //throw FileError
- static int geAccessTimeout(const AbstractPath& ap) { return ap.afs->getAccessTimeout(); } //returns "0" if no timeout in force
+ static int geAccessTimeout(const AbstractPath& ap) { return ap.afsDevice.ref().getAccessTimeout(); } //returns "0" if no timeout in force
//----------------------------------------------------------------------------------------------------------------
+ using FileId = zen::Zbase<char>;
+
enum class ItemType
{
FILE,
FOLDER,
SYMLINK,
};
- struct PathStatus
- {
- ItemType existingType;
- AbstractPath existingPath; //itemPath =: existingPath + relPath
- std::vector<Zstring> relPath; //
- };
//(hopefully) fast: does not distinguish between error/not existing
- static ItemType getItemType(const AbstractPath& ap) { return ap.afs->getItemType(ap.afsPath); } //throw FileError
- //execute potentially SLOW folder traversal but distinguish error/not existing
- static std::optional<ItemType> getItemTypeIfExists(const AbstractPath& ap); //throw FileError
- static PathStatus getPathStatus(const AbstractPath& ap); //throw FileError
+ static ItemType getItemType(const AbstractPath& ap) { return ap.afsDevice.ref().getItemType(ap.afsPath); } //throw FileError
+
+ //assumes: - base path still exists
+ // - all child item path parts must correspond to folder traversal
+ // => we can conclude whether an item is *not* existing anymore by doing a *case-sensitive* name search => potentially SLOW!
+ static std::optional<ItemType> itemStillExists(const AbstractPath& ap) { return ap.afsDevice.ref().itemStillExists(ap.afsPath); } //throw FileError
//----------------------------------------------------------------------------------------------------------------
//target existing: undefined behavior! (fail/overwrite)
//does NOT create parent directories recursively if not existing
- static void createFolderPlain(const AbstractPath& ap) { ap.afs->createFolderPlain(ap.afsPath); } //throw FileError
+ static void createFolderPlain(const AbstractPath& ap) { ap.afsDevice.ref().createFolderPlain(ap.afsPath); } //throw FileError
//no error if already existing
//creates parent directories recursively if not existing
@@ -108,21 +106,22 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
const std::function<void (const std::wstring& displayPath)>& onBeforeFileDeletion, //optional
const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion); //one call for each object!
- static void removeFilePlain (const AbstractPath& ap) { ap.afs->removeFilePlain (ap.afsPath); } //throw FileError
- static void removeSymlinkPlain(const AbstractPath& ap) { ap.afs->removeSymlinkPlain(ap.afsPath); } //throw FileError
- static void removeFolderPlain (const AbstractPath& ap) { ap.afs->removeFolderPlain (ap.afsPath); } //throw FileError
+ static void removeFilePlain (const AbstractPath& ap) { ap.afsDevice.ref().removeFilePlain (ap.afsPath); } //throw FileError
+ static void removeSymlinkPlain(const AbstractPath& ap) { ap.afsDevice.ref().removeSymlinkPlain(ap.afsPath); } //throw FileError
+ static void removeFolderPlain (const AbstractPath& ap) { ap.afsDevice.ref().removeFolderPlain (ap.afsPath); } //throw FileError
//----------------------------------------------------------------------------------------------------------------
- static void setModTime(const AbstractPath& ap, time_t modTime) { ap.afs->setModTime(ap.afsPath, modTime); } //throw FileError, follows symlinks
+ static void setModTime(const AbstractPath& ap, time_t modTime) { ap.afsDevice.ref().setModTime(ap.afsPath, modTime); } //throw FileError, follows symlinks
+
+ static FileId /*optional*/ getFileId(const AbstractPath& ap) { return ap.afsDevice.ref().getFileId(ap.afsPath); } //throw FileError
- static AbstractPath getSymlinkResolvedPath(const AbstractPath& ap) { return ap.afs->getSymlinkResolvedPath(ap.afsPath); } //throw FileError
- static std::string getSymlinkBinaryContent(const AbstractPath& ap) { return ap.afs->getSymlinkBinaryContent(ap.afsPath); } //throw FileError
+ static AbstractPath getSymlinkResolvedPath(const AbstractPath& ap) { return ap.afsDevice.ref().getSymlinkResolvedPath (ap.afsPath); } //throw FileError
+ static std::string getSymlinkBinaryContent(const AbstractPath& ap) { return ap.afsDevice.ref().getSymlinkBinaryContent(ap.afsPath); } //throw FileError
//----------------------------------------------------------------------------------------------------------------
//noexcept; optional return value:
- static zen::ImageHolder getFileIcon (const AbstractPath& ap, int pixelSize) { return ap.afs->getFileIcon (ap.afsPath, pixelSize); }
- static zen::ImageHolder getThumbnailImage(const AbstractPath& ap, int pixelSize) { return ap.afs->getThumbnailImage(ap.afsPath, pixelSize); }
+ static zen::ImageHolder getFileIcon (const AbstractPath& ap, int pixelSize) { return ap.afsDevice.ref().getFileIcon (ap.afsPath, pixelSize); }
+ static zen::ImageHolder getThumbnailImage(const AbstractPath& ap, int pixelSize) { return ap.afsDevice.ref().getThumbnailImage(ap.afsPath, pixelSize); }
//----------------------------------------------------------------------------------------------------------------
- using FileId = zen::Zbase<char>;
struct StreamAttributes
{
@@ -167,13 +166,13 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
//return value always bound:
static std::unique_ptr<InputStream> getInputStream(const AbstractPath& ap, const zen::IOCallback& notifyUnbufferedIO) //throw FileError, ErrorFileLocked, X
- { return ap.afs->getInputStream(ap.afsPath, notifyUnbufferedIO); }
+ { return ap.afsDevice.ref().getInputStream(ap.afsPath, notifyUnbufferedIO); }
//target existing: undefined behavior! (fail/overwrite/auto-rename)
static std::unique_ptr<OutputStream> getOutputStream(const AbstractPath& ap, //throw FileError
const uint64_t* streamSize, //optional
const zen::IOCallback& notifyUnbufferedIO) //
- { return std::make_unique<OutputStream>(ap.afs->getOutputStream(ap.afsPath, streamSize, notifyUnbufferedIO), ap, streamSize); }
+ { return std::make_unique<OutputStream>(ap.afsDevice.ref().getOutputStream(ap.afsPath, streamSize, notifyUnbufferedIO), ap, streamSize); }
//----------------------------------------------------------------------------------------------------------------
struct SymlinkInfo
@@ -222,16 +221,19 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) = 0; //failed to get data for single file/dir/symlink only!
};
- using TraverserWorkload = std::vector<std::pair<std::vector<Zstring> /*relPath*/, std::shared_ptr<TraverserCallback> /*throw X*/>>;
+ using TraverserWorkload = std::vector<std::pair<AfsPath, std::shared_ptr<TraverserCallback> /*throw X*/>>;
//- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...)
- static void traverseFolderRecursive(const AbstractPath& basePath, const TraverserWorkload& workload, size_t parallelOps);
+ static void traverseFolderRecursive(const AfsDevice& afsDevice, const TraverserWorkload& workload /*throw X*/, size_t parallelOps)
+ {
+ afsDevice.ref().traverseFolderRecursive(workload, parallelOps); //throw
+ }
static void traverseFolderFlat(const AbstractPath& ap, //throw FileError
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) //
- { ap.afs->traverseFolderFlat(ap.afsPath, onFile, onFolder, onSymlink); }
+ { ap.afsDevice.ref().traverseFolderFlat(ap.afsPath, onFile, onFolder, onSymlink); }
//----------------------------------------------------------------------------------------------------------------
static bool supportPermissionCopy(const AbstractPath& apSource, const AbstractPath& apTarget); //throw FileError
@@ -273,9 +275,9 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
//----------------------------------------------------------------------------------------------------------------
- static uint64_t getFreeDiskSpace(const AbstractPath& ap) { return ap.afs->getFreeDiskSpace(ap.afsPath); } //throw FileError, returns 0 if not available
+ static uint64_t getFreeDiskSpace(const AbstractPath& ap) { return ap.afsDevice.ref().getFreeDiskSpace(ap.afsPath); } //throw FileError, returns 0 if not available
- static bool supportsRecycleBin(const AbstractPath& ap, const std::function<void ()>& onUpdateGui) { return ap.afs->supportsRecycleBin(ap.afsPath, onUpdateGui); } //throw FileError
+ static bool supportsRecycleBin(const AbstractPath& ap, const std::function<void ()>& onUpdateGui) { return ap.afsDevice.ref().supportsRecycleBin(ap.afsPath, onUpdateGui); } //throw FileError
struct RecycleSession
{
@@ -288,43 +290,28 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t
};
//precondition: supportsRecycleBin() must return true!
- static std::unique_ptr<RecycleSession> createRecyclerSession(const AbstractPath& ap) { return ap.afs->createRecyclerSession(ap.afsPath); } //throw FileError, return value must be bound!
+ static std::unique_ptr<RecycleSession> createRecyclerSession(const AbstractPath& ap) { return ap.afsDevice.ref().createRecyclerSession(ap.afsPath); } //throw FileError, return value must be bound!
- static void recycleItemIfExists(const AbstractPath& ap) { ap.afs->recycleItemIfExists(ap.afsPath); } //throw FileError
+ static void recycleItemIfExists(const AbstractPath& ap) { ap.afsDevice.ref().recycleItemIfExists(ap.afsPath); } //throw FileError
//================================================================================================================
//no need to protect access:
- static Zstring appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep);
-
virtual ~AbstractFileSystem() {}
-protected: //grant derived classes access to AbstractPath:
- static const AbstractFileSystem& getAfs (const AbstractPath& ap) { return *ap.afs; }
- static AfsPath getAfsPath(const AbstractPath& ap) { return ap.afsPath; }
-
- static Zstring getItemName(const AfsPath& afsPath) { using namespace zen; return afterLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); }
- static std::optional<AfsPath> getParentAfsPath(const AfsPath& afsPath);
- struct PathStatusImpl
- {
- ItemType existingType;
- AfsPath existingAfsPath; //afsPath =: existingAfsPath + relPath
- std::vector<Zstring> relPath; //
- };
- PathStatusImpl getPathStatusViaFolderTraversal(const AfsPath& afsPath) const; //throw FileError
+protected:
+ std::optional<ItemType> itemStillExistsViaFolderTraversal(const AfsPath& afsPath) const; //throw FileError
void traverseFolderFlat(const AfsPath& afsPath, //throw FileError
- const std::function<void (const FileInfo& fi)>& onFile, //
- const std::function<void (const FolderInfo& fi)>& onFolder, //optional
+ 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; //
//target existing: undefined behavior! (fail/overwrite/auto-rename)
FileCopyResult copyFileAsStream(const AfsPath& afsPathSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked
const AbstractPath& apTarget, const zen::IOCallback& notifyUnbufferedIO) const; //may be nullptr; throw X!
- using TraverserWorkloadImpl = std::vector<std::pair<AfsPath, std::shared_ptr<TraverserCallback> /*throw X*/>>;
-
private:
virtual std::optional<Zstring> getNativeItemPath(const AfsPath& afsPath) const { return {}; };
@@ -334,11 +321,11 @@ private:
virtual bool isNullFileSystem() const = 0;
- virtual int compareDeviceRootSameAfsType(const AbstractFileSystem& afsRhs) const = 0;
+ virtual int compareDeviceSameAfsType(const AbstractFileSystem& afsRhs) const = 0;
//----------------------------------------------------------------------------------------------------------------
virtual ItemType getItemType(const AfsPath& afsPath) const = 0; //throw FileError
- virtual PathStatusImpl getPathStatus(const AfsPath& afsPath) const = 0; //throw FileError
+ virtual std::optional<ItemType> itemStillExists(const AfsPath& afsPath) const = 0; //throw FileError
//----------------------------------------------------------------------------------------------------------------
//target existing: undefined behavior! (fail/overwrite)
@@ -351,6 +338,8 @@ private:
//----------------------------------------------------------------------------------------------------------------
virtual void setModTime(const AfsPath& afsPath, time_t modTime) const = 0; //throw FileError, follows symlinks
+ virtual FileId /*optional*/ getFileId(const AfsPath& afsPath) const = 0; //throw FileError
+
virtual AbstractPath getSymlinkResolvedPath(const AfsPath& afsPath) const = 0; //throw FileError
virtual std::string getSymlinkBinaryContent(const AfsPath& afsPath) const = 0; //throw FileError
//----------------------------------------------------------------------------------------------------------------
@@ -361,7 +350,7 @@ private:
const uint64_t* streamSize, //optional
const zen::IOCallback& notifyUnbufferedIO) const = 0; //
//----------------------------------------------------------------------------------------------------------------
- virtual void traverseFolderRecursive(const TraverserWorkloadImpl& workload /*throw X*/, size_t parallelOps) const = 0;
+ virtual void traverseFolderRecursive(const TraverserWorkload& workload /*throw X*/, size_t parallelOps) const = 0;
//----------------------------------------------------------------------------------------------------------------
virtual bool supportsPermissions(const AfsPath& afsPath) const = 0; //throw FileError
@@ -398,8 +387,12 @@ private:
};
-inline bool operator< (const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::compareAbstractPath(lhs, rhs) < 0; }
-inline bool operator==(const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::compareAbstractPath(lhs, rhs) == 0; }
+inline bool operator< (const AfsDevice& lhs, const AfsDevice& rhs) { return AbstractFileSystem::compareDevice(lhs.ref(), rhs.ref()) < 0; }
+inline bool operator==(const AfsDevice& lhs, const AfsDevice& rhs) { return AbstractFileSystem::compareDevice(lhs.ref(), rhs.ref()) == 0; }
+inline bool operator!=(const AfsDevice& lhs, const AfsDevice& rhs) { return !(lhs == rhs); }
+
+inline bool operator< (const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::comparePath(lhs, rhs) < 0; }
+inline bool operator==(const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::comparePath(lhs, rhs) == 0; }
inline bool operator!=(const AbstractPath& lhs, const AbstractPath& rhs) { return !(lhs == rhs); }
@@ -413,40 +406,8 @@ inline bool operator!=(const AbstractPath& lhs, const AbstractPath& rhs) { retur
inline
AbstractPath AbstractFileSystem::appendRelPath(const AbstractPath& ap, const Zstring& relPath)
{
- using namespace zen;
-
assert(isValidRelPath(relPath));
- return AbstractPath(ap.afs, AfsPath(appendPaths(ap.afsPath.value, relPath, FILE_NAME_SEPARATOR)));
-}
-
-
-inline
-Zstring AbstractFileSystem::appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep)
-{
- using namespace zen;
-
- assert(!startsWith(relPath, pathSep) && !endsWith(relPath, pathSep));
- if (relPath.empty())
- return basePath;
- if (basePath.empty())
- return relPath;
-
- if (startsWith(relPath, pathSep))
- {
- if (relPath.size() == 1)
- return basePath;
-
- if (endsWith(basePath, pathSep))
- return basePath + (relPath.c_str() + 1);
- }
- else if (!endsWith(basePath, pathSep))
- {
- Zstring output = basePath;
- output.reserve(basePath.size() + 1 + relPath.size()); //append all three strings using a single memory allocation
- return std::move(output) + pathSep + relPath; //
- }
-
- return basePath + relPath;
+ return AbstractPath(ap.afsDevice, AfsPath(nativeAppendPaths(ap.afsPath.value, relPath)));
}
//--------------------------------------------------------------------------
@@ -504,11 +465,11 @@ AbstractFileSystem::FileId AbstractFileSystem::OutputStream::finalize() //throw
inline
bool AbstractFileSystem::supportPermissionCopy(const AbstractPath& apSource, const AbstractPath& apTarget) //throw FileError
{
- if (typeid(*apSource.afs) != typeid(*apTarget.afs))
+ if (typeid(apSource.afsDevice.ref()) != typeid(apTarget.afsDevice.ref()))
return false;
- return apSource.afs->supportsPermissions(apSource.afsPath) && //throw FileError
- apTarget.afs->supportsPermissions(apTarget.afsPath);
+ return apSource.afsDevice.ref().supportsPermissions(apSource.afsPath) && //throw FileError
+ apTarget.afsDevice.ref().supportsPermissions(apTarget.afsPath);
}
@@ -517,8 +478,8 @@ void AbstractFileSystem::moveAndRenameItem(const AbstractPath& apSource, const A
{
using namespace zen;
- if (typeid(*apSource.afs) == typeid(*apTarget.afs))
- return apSource.afs->moveAndRenameItemForSameAfsType(apSource.afsPath, apTarget); //throw FileError, ErrorDifferentVolume
+ if (typeid(apSource.afsDevice.ref()) == typeid(apTarget.afsDevice.ref()))
+ return apSource.afsDevice.ref().moveAndRenameItemForSameAfsType(apSource.afsPath, apTarget); //throw FileError, ErrorDifferentVolume
throw ErrorDifferentVolume(replaceCpy(replaceCpy(_("Cannot move file %x to %y."),
L"%x", L"\n" + fmtPath(getDisplayPath(apSource))),
@@ -532,8 +493,8 @@ void AbstractFileSystem::copyNewFolder(const AbstractPath& apSource, const Abstr
{
using namespace zen;
- if (typeid(*apSource.afs) == typeid(*apTarget.afs))
- return apSource.afs->copyNewFolderForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError
+ if (typeid(apSource.afsDevice.ref()) == typeid(apTarget.afsDevice.ref()))
+ return apSource.afsDevice.ref().copyNewFolderForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError
//fall back:
if (copyFilePermissions)
@@ -550,8 +511,8 @@ void AbstractFileSystem::copySymlink(const AbstractPath& apSource, const Abstrac
{
using namespace zen;
- if (typeid(*apSource.afs) == typeid(*apTarget.afs))
- return apSource.afs->copySymlinkForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError
+ if (typeid(apSource.afsDevice.ref()) == typeid(apTarget.afsDevice.ref()))
+ return apSource.afsDevice.ref().copySymlinkForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError
throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."),
L"%x", L"\n" + fmtPath(getDisplayPath(apSource))),
diff --git a/FreeFileSync/Source/fs/concrete_impl.h b/FreeFileSync/Source/fs/concrete_impl.h
index 08e075a8..c6ccd2d6 100755
--- a/FreeFileSync/Source/fs/concrete_impl.h
+++ b/FreeFileSync/Source/fs/concrete_impl.h
@@ -69,13 +69,13 @@ public:
//context of controlling thread, non-blocking:
template <class Function>
- void run(Task<Context, Function>&& wi)
+ void run(Task<Context, Function>&& wi, bool insertFront = false)
{
threadGroup_->run([this, wi = std::move(wi)]
{
try { this->returnResult<Function>({ wi, nullptr, wi.getResult() }); } //throw FileError
catch (...) { this->returnResult<Function>({ wi, std::current_exception(), {} }); }
- });
+ }, insertFront);
std::lock_guard<std::mutex> dummy(lockResult_);
++resultsPending_;
@@ -197,7 +197,9 @@ void GenericDirTraverser<Functions...>::evalResult(TaskResult<TravContext, Funct
cb->reportItemError(e.toString(), result.wi.ctx.errorRetryCount, result.wi.ctx.errorItemName)) //throw X
{
case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY:
- scheduler_.template run<Function>({ std::move(result.wi.getResult), TravContext{ result.wi.ctx.errorItemName, result.wi.ctx.errorRetryCount + 1, cb }});
+ //user expects that the task is retried immediately => we can't do much about other errors already waiting in the queue, but at least *prepend* to the work load!
+ scheduler_.template run<Function>({ std::move(result.wi.getResult), TravContext{ result.wi.ctx.errorItemName, result.wi.ctx.errorRetryCount + 1, cb }},
+ true /*insertFront*/);
return;
case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE:
diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp
index c924777e..a3e0a8f2 100755
--- a/FreeFileSync/Source/fs/native.cpp
+++ b/FreeFileSync/Source/fs/native.cpp
@@ -235,14 +235,19 @@ void traverseFolderRecursiveNative(const std::vector<std::pair<Zstring, std::sha
}
}
-namespace fff //specialization in original namespace needed by GCC 6
-{
+
template <>
template <>
void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::evalResultValue<GetDirDetails>(const GetDirDetails::Result& r, std::shared_ptr<AFS::TraverserCallback>& cb /*throw X*/)
{
- for (const FsItemRaw& rawItem : r)
- scheduler_.run<GetItemDetails>({ GetItemDetails(rawItem), TravContext{ rawItem.itemName, 0 /*errorRetryCount*/, cb }});
+ //attention: if we simply appended to the work queue this would repeatedly allow for situations where a large number of directories are traversed one after another
+ // without intermittent calls to evalResultValue<GetItemDetails>() => user incorrectly thinks the app is hanging! https://freefilesync.org/forum/viewtopic.php?t=5729
+ //solution: *prepend* GetItemDetails() tasks (in correct order) to the work queue ASAP:
+ std::for_each(r.rbegin(), r.rend(), [&](const FsItemRaw& rawItem)
+ {
+ scheduler_.run<GetItemDetails>({ GetItemDetails(rawItem), TravContext{ rawItem.itemName, 0 /*errorRetryCount*/, cb }},
+ true /*insertFront*/);
+ });
}
@@ -292,7 +297,7 @@ void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::e
else //a file or named pipe, etc.
cb->onFile({ r.raw.itemName, r.target.fileSize, r.target.modTime, convertToAbstractFileId(r.target.fileId), &linkInfo }); //throw X
}
-}
+
namespace
{
@@ -303,13 +308,13 @@ namespace
class RecycleSessionNative : public AbstractFileSystem::RecycleSession
{
public:
- RecycleSessionNative(const Zstring baseFolderPath) : baseFolderPathPf_(appendSeparator(baseFolderPath)) {}
+ RecycleSessionNative(const Zstring baseFolderPath) : baseFolderPath_(baseFolderPath) {}
bool recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) override; //throw FileError
void tryCleanup(const std::function<void (const std::wstring& displayPath)>& notifyDeletionStatus) override; //throw FileError
private:
- const Zstring baseFolderPathPf_; //ends with path separator
+ const Zstring baseFolderPath_; //ends with path separator
};
//===========================================================================================================================
@@ -387,7 +392,7 @@ public:
NativeFileSystem(const Zstring& rootPath) : rootPath_(rootPath) {}
private:
- Zstring getNativePath(const AfsPath& afsPath) const { return appendPaths(rootPath_, afsPath.value, FILE_NAME_SEPARATOR); }
+ Zstring getNativePath(const AfsPath& afsPath) const { return nativeAppendPaths(rootPath_, afsPath.value); }
std::optional<Zstring> getNativeItemPath(const AfsPath& afsPath) const override { return getNativePath(afsPath); }
@@ -397,11 +402,11 @@ private:
bool isNullFileSystem() const override { return rootPath_.empty(); }
- int compareDeviceRootSameAfsType(const AbstractFileSystem& afsRhs) const override
+ int compareDeviceSameAfsType(const AbstractFileSystem& afsRhs) const override
{
const Zstring& rootPathRhs = static_cast<const NativeFileSystem&>(afsRhs).rootPath_;
- return compareLocalPath(rootPath_, rootPathRhs);
+ return compareNativePath(rootPath_, rootPathRhs);
}
//----------------------------------------------------------------------------------------------------------------
@@ -421,9 +426,9 @@ private:
return AFS::ItemType::FILE;
}
- PathStatusImpl getPathStatus(const AfsPath& afsPath) const override //throw FileError
+ std::optional<ItemType> itemStillExists(const AfsPath& afsPath) const override //throw FileError
{
- return getPathStatusViaFolderTraversal(afsPath); //throw FileError
+ return itemStillExistsViaFolderTraversal(afsPath); //throw FileError
}
//----------------------------------------------------------------------------------------------------------------
@@ -459,6 +464,12 @@ private:
zen::setFileTime(getNativePath(afsPath), modTime, ProcSymlink::FOLLOW); //throw FileError
}
+ FileId /*optional*/ getFileId(const AfsPath& afsPath) const override //throw FileError
+ {
+ initComForThread(); //throw FileError
+ return convertToAbstractFileId(zen::getFileId(getNativePath(afsPath))); //throw FileError
+ }
+
AbstractPath getSymlinkResolvedPath(const AfsPath& afsPath) const override //throw FileError
{
initComForThread(); //throw FileError
@@ -470,7 +481,7 @@ private:
throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(nativePath)),
replaceCpy<std::wstring>(L"Invalid path %x.", L"%x", fmtPath(resolvedPath)));
- return AbstractPath(std::make_shared<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath));
+ return AbstractPath(makeSharedRef<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath));
}
std::string getSymlinkBinaryContent(const AfsPath& afsPath) const override //throw FileError
@@ -500,7 +511,7 @@ private:
}
//----------------------------------------------------------------------------------------------------------------
- void traverseFolderRecursive(const TraverserWorkloadImpl& workload /*throw X*/, size_t parallelOps) const override
+ void traverseFolderRecursive(const TraverserWorkload& workload /*throw X*/, size_t parallelOps) const override
{
//initComForThread() -> done on traverser worker threads
@@ -517,7 +528,7 @@ private:
FileCopyResult copyFileForSameAfsType(const AfsPath& afsPathSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked
const AbstractPath& apTarget, bool copyFilePermissions, const IOCallback& notifyUnbufferedIO) const override //may be nullptr; throw X!
{
- const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(getAfs(apTarget)).getNativePath(getAfsPath(apTarget));
+ const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath);
initComForThread(); //throw FileError
@@ -539,7 +550,7 @@ private:
initComForThread(); //throw FileError
const Zstring& sourcePath = getNativePath(afsPathSource);
- const Zstring& targetPath = static_cast<const NativeFileSystem&>(getAfs(apTarget)).getNativePath(getAfsPath(apTarget));
+ const Zstring& targetPath = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath);
zen::createDirectory(targetPath); //throw FileError, ErrorTargetExisting
@@ -548,7 +559,7 @@ private:
//do NOT copy attributes for volume root paths which return as: FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY
//https://freefilesync.org/forum/viewtopic.php?t=5550
- if (getParentAfsPath(afsPathSource)) //=> not a root path
+ if (getParentPath(afsPathSource)) //=> not a root path
tryCopyDirectoryAttributes(sourcePath, targetPath); //throw FileError
if (copyFilePermissions)
@@ -557,7 +568,7 @@ private:
void copySymlinkForSameAfsType(const AfsPath& afsPathSource, const AbstractPath& apTarget, bool copyFilePermissions) const override //throw FileError
{
- const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(getAfs(apTarget)).getNativePath(getAfsPath(apTarget));
+ const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath);
initComForThread(); //throw FileError
zen::copySymlink(getNativePath(afsPathSource), nativePathTarget, copyFilePermissions); //throw FileError
@@ -568,14 +579,14 @@ private:
{
//perf test: detecting different volumes by path is ~30 times faster than having MoveFileEx fail with ERROR_NOT_SAME_DEVICE (6µs vs 190µs)
//=> maybe we can even save some actual I/O in some cases?
- if (compareDeviceRootSameAfsType(getAfs(apTarget)) != 0)
+ if (compareDeviceSameAfsType(apTarget.afsDevice.ref()) != 0)
throw ErrorDifferentVolume(replaceCpy(replaceCpy(_("Cannot move file %x to %y."),
L"%x", L"\n" + fmtPath(getDisplayPath(afsPathSource))),
L"%y", L"\n" + fmtPath(AFS::getDisplayPath(apTarget))),
formatSystemError(L"compareDeviceRoot", EXDEV)
);
initComForThread(); //throw FileError
- const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(getAfs(apTarget)).getNativePath(getAfsPath(apTarget));
+ const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath);
zen::renameFile(getNativePath(afsPathSource), nativePathTarget); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume
}
@@ -694,7 +705,7 @@ AbstractPath fff::createItemPathNative(const Zstring& itemPathPhrase) //noexcept
AbstractPath fff::createItemPathNativeNoFormatting(const Zstring& nativePath) //noexcept
{
if (const std::optional<PathComponents> comp = parsePathComponents(nativePath))
- return AbstractPath(std::make_shared<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath));
+ return AbstractPath(makeSharedRef<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath));
else //path syntax broken
- return AbstractPath(std::make_shared<NativeFileSystem>(nativePath), AfsPath());
+ return AbstractPath(makeSharedRef<NativeFileSystem>(nativePath), AfsPath());
}
diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp
index 8b52bacf..51eefbf1 100755
--- a/FreeFileSync/Source/ui/batch_status_handler.cpp
+++ b/FreeFileSync/Source/ui/batch_status_handler.cpp
@@ -79,18 +79,23 @@ BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(const Zstring&
const SyncResult finalStatus = [&]
{
if (getAbortStatus())
+ {
+ errorLog_.logMsg(_("Stopped"), MSG_TYPE_ERROR); //= user cancel; *not* a MSG_TYPE_FATAL_ERROR!
return SyncResult::ABORTED;
+ }
else if (errorLog_.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR) > 0)
return SyncResult::FINISHED_WITH_ERROR;
else if (errorLog_.getItemCount(MSG_TYPE_WARNING) > 0)
return SyncResult::FINISHED_WITH_WARNINGS;
- else
- return SyncResult::FINISHED_WITH_SUCCESS;
+
+ if (getStatsTotal(currentPhase()) == ProgressStats())
+ errorLog_.logMsg(_("Nothing to synchronize"), MSG_TYPE_INFO);
+ return SyncResult::FINISHED_WITH_SUCCESS;
}();
assert(finalStatus == SyncResult::ABORTED || currentPhase() == PHASE_SYNCHRONIZING);
- ProcessSummary summary
+ const ProcessSummary summary
{
finalStatus, jobName_,
getStatsCurrent(currentPhase()),
@@ -98,12 +103,6 @@ BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(const Zstring&
totalTime
};
- const std::wstring& finalStatusLabel = finalStatus == SyncResult::FINISHED_WITH_SUCCESS &&
- summary.statsTotal.items == 0 &&
- summary.statsTotal.bytes == 0 ? _("Nothing to synchronize") :
- getFinalStatusLabel(finalStatus);
- errorLog_.logMsg(finalStatusLabel, getFinalMsgType(finalStatus));
-
//post sync command
Zstring commandLine = [&]
{
diff --git a/FreeFileSync/Source/ui/cfg_grid.cpp b/FreeFileSync/Source/ui/cfg_grid.cpp
index db49bee4..3d5023b4 100755
--- a/FreeFileSync/Source/ui/cfg_grid.cpp
+++ b/FreeFileSync/Source/ui/cfg_grid.cpp
@@ -84,7 +84,7 @@ void ConfigView::addCfgFiles(const std::vector<Zstring>& filePaths)
std::tie(detail.name, detail.cfgType, detail.isLastRunCfg) = [&]
{
- if (equalLocalPath(filePath, lastRunConfigPath_))
+ if (equalNativePath(filePath, lastRunConfigPath_))
return std::make_tuple(utfTo<Zstring>(L"<" + _("Last session") + L">"), Details::CFG_TYPE_GUI, true);
const Zstring fileName = afterLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
@@ -110,9 +110,9 @@ void ConfigView::addCfgFiles(const std::vector<Zstring>& filePaths)
void ConfigView::removeItems(const std::vector<Zstring>& filePaths)
{
- const std::set<Zstring, LessLocalPath> pathsSorted(filePaths.begin(), filePaths.end());
+ const std::set<Zstring, LessNativePath> pathsSorted(filePaths.begin(), filePaths.end());
- erase_if(cfgListView_, [&](auto it) { return pathsSorted.find(it->first) != pathsSorted.end(); });
+ eraseIf(cfgListView_, [&](auto it) { return pathsSorted.find(it->first) != pathsSorted.end(); });
for (const Zstring& filePath : filePaths)
cfgList_.erase(filePath);
@@ -581,7 +581,7 @@ void cfggrid::addAndSelect(Grid& grid, const std::vector<Zstring>& filePaths, bo
grid.clearSelection(GridEventPolicy::DENY);
- const std::set<Zstring, LessLocalPath> pathsSorted(filePaths.begin(), filePaths.end());
+ const std::set<Zstring, LessNativePath> pathsSorted(filePaths.begin(), filePaths.end());
std::optional<size_t> selectionTopRow;
for (size_t i = 0; i < grid.getRowCount(); ++i)
diff --git a/FreeFileSync/Source/ui/cfg_grid.h b/FreeFileSync/Source/ui/cfg_grid.h
index 66cd4dd8..3983df06 100755
--- a/FreeFileSync/Source/ui/cfg_grid.h
+++ b/FreeFileSync/Source/ui/cfg_grid.h
@@ -134,7 +134,7 @@ private:
const Zstring lastRunConfigPath_ = getLastRunConfigPath(); //let's not use another static...
- using CfgFileList = std::map<Zstring /*file path*/, Details, LessLocalPath>;
+ using CfgFileList = std::map<Zstring /*file path*/, Details, LessNativePath>;
CfgFileList cfgList_;
std::vector<CfgFileList::iterator> cfgListView_; //sorted view on cfgList_
diff --git a/FreeFileSync/Source/ui/command_box.cpp b/FreeFileSync/Source/ui/command_box.cpp
index 9d8c7a9b..2ef53a51 100755
--- a/FreeFileSync/Source/ui/command_box.cpp
+++ b/FreeFileSync/Source/ui/command_box.cpp
@@ -75,7 +75,7 @@ void CommandBox::addItemHistory()
equalNoCase(newCommand, cmd))
return;
- erase_if(history_, [&](const Zstring& item) { return equalNoCase(newCommand, item); });
+ eraseIf(history_, [&](const Zstring& item) { return equalNoCase(newCommand, item); });
history_.insert(history_.begin(), newCommand);
@@ -192,7 +192,7 @@ void CommandBox::OnKeyEvent(wxKeyEvent& event)
//this->SetSelection(wxNOT_FOUND);
//delete selected row
- erase_if(history_, [&](const Zstring& item) { return item == selValue; });
+ eraseIf(history_, [&](const Zstring& item) { return item == selValue; });
SetString(pos, wxString()); //in contrast to Delete(), this one does not kill the drop-down list and gives a nice visual feedback!
//Delete(pos);
diff --git a/FreeFileSync/Source/ui/file_view.cpp b/FreeFileSync/Source/ui/file_view.cpp
index fdf23760..f5d2f79e 100755
--- a/FreeFileSync/Source/ui/file_view.cpp
+++ b/FreeFileSync/Source/ui/file_view.cpp
@@ -262,7 +262,7 @@ void FileView::removeInvalidRows()
rowPositionsFirstChild_.clear();
//remove rows that have been deleted meanwhile
- erase_if(sortedRef_, [&](const RefIndex& refIdx) { return !FileSystemObject::retrieve(refIdx.objId); });
+ eraseIf(sortedRef_, [&](const RefIndex& refIdx) { return !FileSystemObject::retrieve(refIdx.objId); });
}
diff --git a/FreeFileSync/Source/ui/folder_history_box.cpp b/FreeFileSync/Source/ui/folder_history_box.cpp
index 888d2a0f..932e6730 100755
--- a/FreeFileSync/Source/ui/folder_history_box.cpp
+++ b/FreeFileSync/Source/ui/folder_history_box.cpp
@@ -62,8 +62,8 @@ void FolderHistoryBox::setValueAndUpdateList(const wxString& folderPathPhrase)
//populate selection list....
std::vector<wxString> dirList;
{
- //add some aliases to allow user changing to volume name and back, if possible
- std::vector<Zstring> aliases = getDirectoryAliases(utfTo<Zstring>(folderPathPhrase)); //may block when resolving [<volume name>]
+ //allow user changing to volume name and back, if possible
+ std::vector<Zstring> aliases = getFolderPathAliases(utfTo<Zstring>(folderPathPhrase)); //may block when resolving [<volume name>]
std::transform(aliases.begin(), aliases.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfTo<wxString>(str); });
}
if (sharedHistory_.get())
diff --git a/FreeFileSync/Source/ui/folder_history_box.h b/FreeFileSync/Source/ui/folder_history_box.h
index bcbe343b..e83346e1 100755
--- a/FreeFileSync/Source/ui/folder_history_box.h
+++ b/FreeFileSync/Source/ui/folder_history_box.h
@@ -41,7 +41,7 @@ public:
const Zstring nameTmp = zen::trimCpy(folderPathPhrase);
//insert new folder or put it to the front if already existing
- zen::erase_if(folderPathPhrases_, [&](const Zstring& item) { return equalNoCase(item, nameTmp); });
+ zen::eraseIf(folderPathPhrases_, [&](const Zstring& item) { return equalNoCase(item, nameTmp); });
folderPathPhrases_.insert(folderPathPhrases_.begin(), nameTmp);
@@ -49,7 +49,7 @@ public:
folderPathPhrases_.resize(maxSize_);
}
- void delItem(const Zstring& folderPathPhrase) { zen::erase_if(folderPathPhrases_, [&](const Zstring& item) { return equalNoCase(item, folderPathPhrase); }); }
+ void delItem(const Zstring& folderPathPhrase) { zen::eraseIf(folderPathPhrases_, [&](const Zstring& item) { return equalNoCase(item, folderPathPhrase); }); }
private:
size_t maxSize_ = 0;
diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp
index c050994b..7f44e50e 100755
--- a/FreeFileSync/Source/ui/folder_selector.cpp
+++ b/FreeFileSync/Source/ui/folder_selector.cpp
@@ -145,7 +145,7 @@ void FolderSelector::onItemPathDropped(FileDropEvent& event)
try
{
if (AFS::getItemType(itemPath) == AFS::ItemType::FILE) //throw FileError
- if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(itemPath))
+ if (std::optional<AbstractPath> parentPath = AFS::getParentPath(itemPath))
return AFS::getInitPathPhrase(*parentPath);
}
catch (FileError&) {} //e.g. good for inactive mapped network shares, not so nice for C:\pagefile.sys
diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp
index 24c86623..201724f6 100755
--- a/FreeFileSync/Source/ui/gui_generated.cpp
+++ b/FreeFileSync/Source/ui/gui_generated.cpp
@@ -1545,7 +1545,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer159->Add( bSizerCompMisc, 0, wxEXPAND, 5 );
- bSizer2561->Add( bSizer159, 1, wxEXPAND, 5 );
+ bSizer2561->Add( bSizer159, 0, wxEXPAND, 5 );
m_staticlinePerformance = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
bSizer2561->Add( m_staticlinePerformance, 0, wxEXPAND, 5 );
@@ -1609,7 +1609,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizerPerformance->Add( bSizer260, 1, wxALL|wxEXPAND, 5 );
- bSizer2561->Add( bSizerPerformance, 0, wxEXPAND, 5 );
+ bSizer2561->Add( bSizerPerformance, 1, wxEXPAND, 5 );
m_panelComparisonSettings->SetSizer( bSizer2561 );
@@ -2304,7 +2304,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer2372->Add( m_panelLogfile, 1, 0, 5 );
- bSizerSyncMisc->Add( bSizer2372, 1, wxALL|wxALIGN_CENTER_VERTICAL, 10 );
+ bSizerSyncMisc->Add( bSizer2372, 1, wxALL, 10 );
m_staticline57 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
bSizerSyncMisc->Add( m_staticline57, 0, wxEXPAND, 5 );
@@ -2315,7 +2315,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
wxBoxSizer* bSizer251;
bSizer251 = new wxBoxSizer( wxHORIZONTAL );
- m_staticText89 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Run a command after synchronization:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText89 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Run a command:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText89->Wrap( -1 );
bSizer251->Add( m_staticText89, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
@@ -2334,10 +2334,10 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w
bSizer247->Add( m_comboBoxPostSyncCommand, 0, wxTOP|wxEXPAND, 5 );
- bSizerSyncMisc->Add( bSizer247, 1, wxALL|wxALIGN_CENTER_VERTICAL, 10 );
+ bSizerSyncMisc->Add( bSizer247, 1, wxALL, 10 );
- bSizer232->Add( bSizerSyncMisc, 0, wxEXPAND, 5 );
+ bSizer232->Add( bSizerSyncMisc, 1, wxEXPAND, 5 );
m_panelSyncSettings->SetSizer( bSizer232 );
@@ -4231,7 +4231,7 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const
wxBoxSizer* bSizer282;
bSizer282 = new wxBoxSizer( wxHORIZONTAL );
- m_checkBoxLogFilesMaxAge = new wxCheckBox( m_panel39, wxID_ANY, _("Remove old log files after x days:"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_checkBoxLogFilesMaxAge = new wxCheckBox( m_panel39, wxID_ANY, _("&Delete logs after x days:"), wxDefaultPosition, wxDefaultSize, 0 );
bSizer282->Add( m_checkBoxLogFilesMaxAge, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_spinCtrlLogFilesMaxAge = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 );
@@ -4466,7 +4466,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer186;
bSizer186 = new wxBoxSizer( wxVERTICAL );
- m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText94->Wrap( -1 );
bSizer186->Add( m_staticText94, 0, wxALL, 5 );
@@ -4727,7 +4727,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS
wxBoxSizer* bSizer185;
bSizer185 = new wxBoxSizer( wxVERTICAL );
- m_staticText93 = new wxStaticText( m_panel41, wxID_ANY, _("Published under the GNU General Public License"), wxDefaultPosition, wxDefaultSize, 0 );
+ m_staticText93 = new wxStaticText( m_panel41, wxID_ANY, _("Published under the GNU General Public License:"), wxDefaultPosition, wxDefaultSize, 0 );
m_staticText93->Wrap( -1 );
bSizer185->Add( m_staticText93, 0, wxALL, 5 );
diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp
index 0a52e18a..1e0460e5 100755
--- a/FreeFileSync/Source/ui/gui_status_handler.cpp
+++ b/FreeFileSync/Source/ui/gui_status_handler.cpp
@@ -9,12 +9,10 @@
#include <zen/shutdown.h>
#include <wx/app.h>
#include <wx/wupdlock.h>
-//#include <wx+/bitmap_button.h>
#include <wx+/popup_dlg.h>
#include "main_dlg.h"
#include "../base/generate_logfile.h"
#include "../base/resolve_path.h"
-//#include "../base/status_handler_impl.h"
#include "../fs/concrete.h"
using namespace zen;
@@ -133,7 +131,10 @@ StatusHandlerTemporaryPanel::Result StatusHandlerTemporaryPanel::reportFinalStat
const SyncResult finalStatus = [&]
{
if (getAbortStatus())
+ {
+ errorLog_.logMsg(_("Stopped"), MSG_TYPE_ERROR); //= user cancel; *not* a MSG_TYPE_FATAL_ERROR!
return SyncResult::ABORTED;
+ }
else if (errorLog_.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR) > 0)
return SyncResult::FINISHED_WITH_ERROR;
else if (errorLog_.getItemCount(MSG_TYPE_WARNING) > 0)
@@ -142,9 +143,7 @@ StatusHandlerTemporaryPanel::Result StatusHandlerTemporaryPanel::reportFinalStat
return SyncResult::FINISHED_WITH_SUCCESS;
}();
- errorLog_.logMsg(getFinalStatusLabel(finalStatus), getFinalMsgType(finalStatus));
-
- ProcessSummary summary
+ const ProcessSummary summary
{
finalStatus, {} /*jobName*/,
getStatsCurrent(currentPhase()),
@@ -152,7 +151,6 @@ StatusHandlerTemporaryPanel::Result StatusHandlerTemporaryPanel::reportFinalStat
totalTime
};
-
auto errorLogFinal = std::make_shared<const ErrorLog>(std::move(errorLog_));
errorLog_ = ErrorLog(); //see check in ~StatusHandlerTemporaryPanel()
@@ -350,18 +348,23 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStat
const SyncResult finalStatus = [&]
{
if (getAbortStatus())
+ {
+ errorLog_.logMsg(_("Stopped"), MSG_TYPE_ERROR); //= user cancel; *not* a MSG_TYPE_FATAL_ERROR!
return SyncResult::ABORTED;
+ }
else if (errorLog_.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR) > 0)
return SyncResult::FINISHED_WITH_ERROR;
else if (errorLog_.getItemCount(MSG_TYPE_WARNING) > 0)
return SyncResult::FINISHED_WITH_WARNINGS;
- else
- return SyncResult::FINISHED_WITH_SUCCESS;
+
+ if (getStatsTotal(currentPhase()) == ProgressStats())
+ errorLog_.logMsg(_("Nothing to synchronize"), MSG_TYPE_INFO);
+ return SyncResult::FINISHED_WITH_SUCCESS;
}();
assert(finalStatus == SyncResult::ABORTED || currentPhase() == PHASE_SYNCHRONIZING);
- ProcessSummary summary
+ const ProcessSummary summary
{
finalStatus, jobName_,
getStatsCurrent(currentPhase()),
@@ -369,12 +372,6 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStat
totalTime
};
- const std::wstring& finalStatusLabel = finalStatus == SyncResult::FINISHED_WITH_SUCCESS &&
- summary.statsTotal.items == 0 &&
- summary.statsTotal.bytes == 0 ? _("Nothing to synchronize") :
- getFinalStatusLabel(finalStatus);
- errorLog_.logMsg(finalStatusLabel, getFinalMsgType(finalStatus));
-
//post sync command
Zstring commandLine = [&]
{
diff --git a/FreeFileSync/Source/ui/log_panel.cpp b/FreeFileSync/Source/ui/log_panel.cpp
index fc7810e0..fca60a36 100755
--- a/FreeFileSync/Source/ui/log_panel.cpp
+++ b/FreeFileSync/Source/ui/log_panel.cpp
@@ -536,7 +536,7 @@ void LogPanel::copySelectionToClipboard()
if (auto prov = m_gridMessages->getDataProvider())
{
std::vector<Grid::ColAttributes> colAttr = m_gridMessages->getColumnConfig();
- erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
+ eraseIf(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
if (!colAttr.empty())
for (size_t row : m_gridMessages->getSelectedRows())
{
diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp
index 1cb67b60..92a545ee 100755
--- a/FreeFileSync/Source/ui/main_dlg.cpp
+++ b/FreeFileSync/Source/ui/main_dlg.cpp
@@ -263,13 +263,19 @@ XmlGlobalSettings tryLoadGlobalConfig(const Zstring& globalConfigFilePath) //blo
{
std::wstring warningMsg;
readConfig(globalConfigFilePath, globalCfg, warningMsg); //throw FileError
-
assert(warningMsg.empty()); //ignore parsing errors: should be migration problems only *cross-fingers*
}
- catch (const FileError& e)
+ catch (FileError&)
{
- if (!itemNotExisting(globalConfigFilePath)) //existing or access error
+ try
+ {
+ if (itemStillExists(globalConfigFilePath)) //throw FileError
+ throw;
+ }
+ catch (const FileError& e)
+ {
showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); //no parent window: main dialog not yet created!
+ }
}
return globalCfg;
}
@@ -284,7 +290,7 @@ void MainDialog::create(const Zstring& globalConfigFilePath)
//------------------------------------------------------------------------------------------
//check existence of all files in parallel:
- GetFirstResult<std::false_type> firstUnavailableFile;
+ AsyncFirstResult<std::false_type> firstUnavailableFile;
for (const Zstring& filePath : cfgFilePaths)
firstUnavailableFile.addJob([filePath]() -> std::optional<std::false_type>
@@ -372,8 +378,8 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath,
MainDialogGenerated(nullptr),
globalConfigFilePath_(globalConfigFilePath)
{
- m_folderPathLeft ->init(folderHistoryLeft_);
- m_folderPathRight->init(folderHistoryRight_);
+ m_folderPathLeft ->init(folderHistoryLeft_ .ptr());
+ m_folderPathRight->init(folderHistoryRight_.ptr());
//setup sash: detach + reparent:
m_splitterMain->SetSizer(nullptr); //alas wxFormbuilder doesn't allow us to have child windows without a sizer, so we have to remove it here
@@ -716,8 +722,8 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath,
//1. setConfig() indirectly calls cfggrid::addAndSelect() which changes cfg history scroll position
//2. Grid::makeRowVisible() requires final window height! => do this after window resizing is complete
if (m_gridCfgHistory->getRowCount() > 0)
- m_gridCfgHistory->scrollTo(numeric::clampCpy<size_t>(globalSettings.gui.mainDlg.cfgGridTopRowPos, //must be set *after* wxAuiManager::LoadPerspective() to have any effect
- 0, m_gridCfgHistory->getRowCount() - 1));
+ m_gridCfgHistory->scrollTo(std::clamp<size_t>(globalSettings.gui.mainDlg.cfgGridTopRowPos, //must be set *after* wxAuiManager::LoadPerspective() to have any effect
+ 0, m_gridCfgHistory->getRowCount() - 1));
//first selected item should always be visible:
const std::vector<size_t> selectedRows = m_gridCfgHistory->getSelectedRows();
@@ -764,7 +770,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath,
if (havePartialPair != haveFullPair) //either all pairs full or all half-filled -> validity check!
{
//check existence of all directories in parallel!
- GetFirstResult<std::false_type> firstMissingDir;
+ AsyncFirstResult<std::false_type> firstMissingDir;
for (const AbstractPath& folderPath : folderPathsToCheck)
firstMissingDir.addJob([folderPath]() -> std::optional<std::false_type>
{
@@ -867,36 +873,41 @@ void MainDialog::setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings)
//caveat set/get language asymmmetry! setLanguage(globalSettings.programLanguage); //throw FileError
//we need to set langugabe before creating this class!
+ wxSize newSize(fastFromDIP(900), fastFromDIP(600)); //default window size
+ std::optional<wxPoint> newPos;
//set dialog size and position:
// - width/height are invalid if the window is minimized (eg x,y == -32000; height = 28, width = 160)
// - multi-monitor setups: dialog may be placed on second monitor which is currently turned off
if (globalSettings.gui.mainDlg.dlgSize.GetWidth () > 0 &&
globalSettings.gui.mainDlg.dlgSize.GetHeight() > 0)
{
+ newSize = globalSettings.gui.mainDlg.dlgSize;
+
//calculate how much of the dialog will be visible on screen
- const int dialogAreaTotal = globalSettings.gui.mainDlg.dlgSize.GetWidth() * globalSettings.gui.mainDlg.dlgSize.GetHeight();
- int dialogAreaVisible = 0;
+ const int dlgArea = newSize.GetWidth() * newSize.GetHeight();
+ int dlgAreaMaxVisible = 0;
const int monitorCount = wxDisplay::GetCount();
for (int i = 0; i < monitorCount; ++i)
{
- wxRect intersection = wxDisplay(i).GetClientArea().Intersect(wxRect(globalSettings.gui.mainDlg.dlgPos, globalSettings.gui.mainDlg.dlgSize));
- dialogAreaVisible = std::max(dialogAreaVisible, intersection.GetWidth() * intersection.GetHeight());
+ wxRect intersection = wxDisplay(i).GetClientArea().Intersect(wxRect(globalSettings.gui.mainDlg.dlgPos, newSize));
+ dlgAreaMaxVisible = std::max(dlgAreaMaxVisible, intersection.GetWidth() * intersection.GetHeight());
}
- //wxGTK's wxWindow::SetSize seems unreliable and behaves like a wxWindow::SetClientSize
- //=> use wxWindow::SetClientSize instead (for the record: no such issue on Windows/OS X)
- SetClientSize(globalSettings.gui.mainDlg.dlgSize);
-
- if (dialogAreaVisible > 0.1 * dialogAreaTotal //at least 10% of the dialog should be visible!
+ if (dlgAreaMaxVisible > 0.1 * dlgArea //at least 10% of the dialog should be visible!
)
- SetPosition(globalSettings.gui.mainDlg.dlgPos);
- else
- Center();
+ newPos = globalSettings.gui.mainDlg.dlgPos;
}
- else //default window size and position
+
+ //old comment: "wxGTK's wxWindow::SetSize seems unreliable and behaves like a wxWindow::SetClientSize
+ // => use wxWindow::SetClientSize instead (for the record: no such issue on Windows/OS X)
+ //2018-10-15: Weird new problem on Centos/Ubuntu: SetClientSize() + SetPosition() fail to set correct dialog *position*, but SetSize() + SetPosition() do!
+ // => old issues with SetSize() seem to be gone... => revert to SetSize()
+ if (newPos)
+ SetSize(wxRect(*newPos, newSize));
+ else
{
- SetClientSize(wxSize(fastFromDIP(900), fastFromDIP(550))); //=~ 900 x 600 total size
+ SetSize(newSize);
Center();
}
@@ -939,8 +950,8 @@ void MainDialog::setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings)
//--------------------------------------------------------------------------------
//load list of last used folders
- *folderHistoryLeft_ = FolderHistory(globalSettings.gui.mainDlg.folderHistoryLeft, globalSettings.gui.mainDlg.folderHistItemsMax);
- *folderHistoryRight_ = FolderHistory(globalSettings.gui.mainDlg.folderHistoryRight, globalSettings.gui.mainDlg.folderHistItemsMax);
+ folderHistoryLeft_ .ref() = FolderHistory(globalSettings.gui.mainDlg.folderHistoryLeft, globalSettings.gui.mainDlg.folderHistItemsMax);
+ folderHistoryRight_.ref() = FolderHistory(globalSettings.gui.mainDlg.folderHistoryRight, globalSettings.gui.mainDlg.folderHistItemsMax);
//show/hide file icons
filegrid::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalSettings.gui.mainDlg.showIcons, convert(globalSettings.gui.mainDlg.iconSize));
@@ -1015,8 +1026,8 @@ XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
globalSettings.gui.mainDlg.lastUsedConfigFiles = activeConfigFiles_;
//write list of last used folders
- globalSettings.gui.mainDlg.folderHistoryLeft = folderHistoryLeft_ ->getList();
- globalSettings.gui.mainDlg.folderHistoryRight = folderHistoryRight_->getList();
+ globalSettings.gui.mainDlg.folderHistoryLeft = folderHistoryLeft_ .ref().getList();
+ globalSettings.gui.mainDlg.folderHistoryRight = folderHistoryRight_.ref().getList();
globalSettings.gui.mainDlg.textSearchRespectCase = m_checkBoxMatchCase->GetValue();
@@ -1046,7 +1057,7 @@ XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
Maximize(false);
}
- globalSettings.gui.mainDlg.dlgSize = GetClientSize();
+ globalSettings.gui.mainDlg.dlgSize = GetSize();
globalSettings.gui.mainDlg.dlgPos = GetPosition();
//wxGTK: returns full screen size and strange position (65/-4)
@@ -1057,7 +1068,6 @@ XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit()
globalSettings.gui.mainDlg.dlgSize = wxSize();
globalSettings.gui.mainDlg.dlgPos = wxPoint();
}
-
return globalSettings;
}
@@ -1102,7 +1112,7 @@ void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRe
if (auto prov = grid->getDataProvider())
{
std::vector<Grid::ColAttributes> colAttr = grid->getColumnConfig();
- erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
+ eraseIf(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
if (!colAttr.empty())
for (size_t row : grid->getSelectedRows())
{
@@ -1176,18 +1186,14 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const
void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& selectionLeft,
const std::vector<FileSystemObject*>& selectionRight)
{
- std::vector<const FileSystemObject*> rowsLeftTmp;
- std::vector<const FileSystemObject*> rowsRightTmp;
- std::copy_if(selectionLeft .begin(), selectionLeft .end(), std::back_inserter(rowsLeftTmp), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty< LEFT_SIDE>(); });
- std::copy_if(selectionRight.begin(), selectionRight.end(), std::back_inserter(rowsRightTmp), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty<RIGHT_SIDE>(); });
-
- if (rowsLeftTmp.empty() && rowsRightTmp.empty())
- return;
+ if (std::all_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }) &&
+ std::all_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< RIGHT_SIDE>(); }))
+ return;
FocusPreserver fp;
if (showCopyToDialog(this,
- rowsLeftTmp, rowsRightTmp,
+ selectionLeft, selectionRight,
globalCfg_.gui.mainDlg.copyToCfg.lastUsedPath,
globalCfg_.gui.mainDlg.copyToCfg.folderHistory,
globalCfg_.gui.mainDlg.folderHistItemsMax,
@@ -1208,7 +1214,7 @@ void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& sel
guiCfg.mainCfg.automaticRetryDelay); //handle status display and error messages
try
{
- fff::copyToAlternateFolder(rowsLeftTmp, rowsRightTmp,
+ fff::copyToAlternateFolder(selectionLeft, selectionRight,
globalCfg_.gui.mainDlg.copyToCfg.lastUsedPath,
globalCfg_.gui.mainDlg.copyToCfg.keepRelPaths,
globalCfg_.gui.mainDlg.copyToCfg.overwriteIfExists,
@@ -1230,17 +1236,13 @@ void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& sel
void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selectionLeft,
const std::vector<FileSystemObject*>& selectionRight, bool moveToRecycler)
{
- std::vector<FileSystemObject*> rowsLeftTmp = selectionLeft;
- std::vector<FileSystemObject*> rowsRightTmp = selectionRight;
- erase_if(rowsLeftTmp, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); });
- erase_if(rowsRightTmp, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); });
- if (rowsLeftTmp.empty() && rowsRightTmp.empty())
- return;
+ if (std::all_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }) &&
+ std::all_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< RIGHT_SIDE>(); }))
+ return;
FocusPreserver fp;
- //sigh: do senseless vector<FileSystemObject*> -> vector<const FileSystemObject*> conversion:
- if (showDeleteDialog(this, { rowsLeftTmp.begin(), rowsLeftTmp.end() }, { rowsRightTmp.begin(), rowsRightTmp.end() },
+ if (showDeleteDialog(this, selectionLeft, selectionRight,
moveToRecycler) != ReturnSmallDlg::BUTTON_OKAY)
return;
@@ -1259,7 +1261,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec
guiCfg.mainCfg.automaticRetryDelay); //handle status display and error messages
try
{
- deleteFromGridAndHD(rowsLeftTmp, rowsRightTmp,
+ deleteFromGridAndHD(selectionLeft, selectionRight,
folderCmp_,
extractDirectionCfg(getConfig().mainCfg),
moveToRecycler,
@@ -1675,7 +1677,8 @@ void MainDialog::enableAllElements()
m_panelConfig ->Enable();
m_panelViewFilter ->Enable();
- Refresh(); //at least wxWidgets on macOS fails to do this after enabling
+ Refresh(); //at least wxWidgets on macOS fails to do this after enabling
+ auiMgr_.Update(); //
}
@@ -2315,8 +2318,8 @@ void MainDialog::onMainGridContextRim(bool leftSide)
std::vector<FileSystemObject*> nonEmptySelectionLeft = selectionLeft;
std::vector<FileSystemObject*> nonEmptySelectionRight = selectionRight;
- erase_if(nonEmptySelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); });
- erase_if(nonEmptySelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); });
+ eraseIf(nonEmptySelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); });
+ eraseIf(nonEmptySelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); });
menu.addSeparator();
@@ -2733,7 +2736,7 @@ void MainDialog::cfgHistoryRemoveObsolete(const std::vector<Zstring>& filePaths)
void MainDialog::updateUnsavedCfgStatus()
{
- const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
+ const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
const bool haveUnsavedCfg = lastSavedCfg_ != getConfig();
@@ -2780,7 +2783,7 @@ void MainDialog::updateUnsavedCfgStatus()
void MainDialog::OnConfigSave(wxCommandEvent& event)
{
- const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
+ const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
//if we work on a single named configuration document: save directly if changed
//else: always show file dialog
@@ -2834,7 +2837,7 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save
}
else
{
- const Zstring defaultFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstr("SyncSettings.ffs_gui");
+ const Zstring defaultFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstr("SyncSettings.ffs_gui");
auto defaultFolder = utfTo<wxString>(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE));
auto defaultFileName = utfTo<wxString>(afterLast (defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL));
@@ -2873,7 +2876,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate)
{
//essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "m_bpButtonViewTypeSyncAction" is negligible
- const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
+ const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
//prepare batch config: reuse existing batch-specific settings from file if available
BatchExclusiveConfig batchExCfg;
@@ -2957,7 +2960,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
{
if (lastSavedCfg_ != getConfig())
{
- const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
+ const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
//notify user about changed settings
if (globalCfg_.confirmDlgs.popupOnConfigChange)
@@ -3015,7 +3018,7 @@ bool MainDialog::saveOldConfig() //return false on user abort
void MainDialog::OnConfigLoad(wxCommandEvent& event)
{
- const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
+ const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
wxFileDialog filePicker(this,
wxString(),
@@ -3683,7 +3686,7 @@ void MainDialog::OnCompare(wxCommandEvent& event)
const auto& guiCfg = getConfig();
const std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now();
- const std::map<AbstractPath, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps;
+ const std::map<AfsDevice, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps;
//handle status display and error messages
StatusHandlerTemporaryPanel statusHandler(*this, startTime,
@@ -3740,8 +3743,8 @@ void MainDialog::OnCompare(wxCommandEvent& event)
RequestUserAttention();
//add to folder history after successful comparison only
- folderHistoryLeft_ ->addItem(utfTo<Zstring>(m_folderPathLeft ->GetValue()));
- folderHistoryRight_->addItem(utfTo<Zstring>(m_folderPathRight->GetValue()));
+ folderHistoryLeft_ .ref().addItem(utfTo<Zstring>(m_folderPathLeft ->GetValue()));
+ folderHistoryRight_.ref().addItem(utfTo<Zstring>(m_folderPathRight->GetValue()));
assert(m_buttonCompare->GetId() != wxID_ANY);
if (fp.getFocusId() == m_buttonCompare->GetId())
@@ -3773,7 +3776,8 @@ void MainDialog::updateGui()
m_menuItemExportList->Enable(!folderCmp_.empty()); //a CSV without even folder names confuses users: https://freefilesync.org/forum/viewtopic.php?t=4787
- auiMgr_.Update(); //fix small display distortion, if view filter panel is empty
+ warn_static("still needed???")
+ //auiMgr_.Update(); //fix small display distortion, if view filter panel is empty
}
@@ -3879,13 +3883,13 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
globalCfg_.confirmDlgs.confirmSyncStart = !dontShowAgain;
}
- const std::map<AbstractPath, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps;
+ const std::map<AfsDevice, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps;
std::set<AbstractPath> logFilePathsToKeep;
for (const ConfigFileItem& item : cfggrid::getDataView(*m_gridCfgHistory).get())
logFilePathsToKeep.insert(item.logFilePath);
- const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
+ const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring();
const std::chrono::system_clock::time_point syncStartTime = std::chrono::system_clock::now();
bool exitAfterSync = false;
@@ -3919,18 +3923,18 @@ void MainDialog::OnStartSync(wxCommandEvent& event)
std::unique_ptr<LockHolder> dirLocks;
if (globalCfg_.createLockFile)
{
- std::set<Zstring, LessFilePath> availableDirPaths;
+ std::set<Zstring> folderPathsToLock;
for (auto it = begin(folderCmp_); it != end(folderCmp_); ++it)
{
if (it->isAvailable<LEFT_SIDE>()) //do NOT check directory existence again!
if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<LEFT_SIDE>())) //restrict directory locking to native paths until further
- availableDirPaths.insert(*nativeFolderPath);
+ folderPathsToLock.insert(*nativeFolderPath);
if (it->isAvailable<RIGHT_SIDE>())
if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>()))
- availableDirPaths.insert(*nativeFolderPath);
+ folderPathsToLock.insert(*nativeFolderPath);
}
- dirLocks = std::make_unique<LockHolder>(availableDirPaths, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); //throw AbortProcess
+ dirLocks = std::make_unique<LockHolder>(folderPathsToLock, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); //throw AbortProcess
}
//START SYNCHRONIZATION
@@ -4749,8 +4753,8 @@ void MainDialog::insertAddFolderPair(const std::vector<LocalPairConfig>& newPair
FolderPairPanel* newPair = new FolderPairPanel(m_scrolledWindowFolderPairs, *this);
//init dropdown history
- newPair->m_folderPathLeft ->init(folderHistoryLeft_);
- newPair->m_folderPathRight->init(folderHistoryRight_);
+ newPair->m_folderPathLeft ->init(folderHistoryLeft_ .ptr());
+ newPair->m_folderPathRight->init(folderHistoryRight_.ptr());
newPair->m_bpButtonFolderPairOptions->SetBitmapLabel(getResourceImage(L"button_arrow_down"));
@@ -4907,9 +4911,9 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event)
auto colAttrCenter = m_gridMainC->getColumnConfig();
auto colAttrRight = m_gridMainR->getColumnConfig();
- erase_if(colAttrLeft, [](const Grid::ColAttributes& ca) { return !ca.visible; });
- erase_if(colAttrCenter, [](const Grid::ColAttributes& ca) { return !ca.visible || static_cast<ColumnTypeCenter>(ca.type) == ColumnTypeCenter::CHECKBOX; });
- erase_if(colAttrRight, [](const Grid::ColAttributes& ca) { return !ca.visible; });
+ eraseIf(colAttrLeft, [](const Grid::ColAttributes& ca) { return !ca.visible; });
+ eraseIf(colAttrCenter, [](const Grid::ColAttributes& ca) { return !ca.visible || static_cast<ColumnTypeCenter>(ca.type) == ColumnTypeCenter::CHECKBOX; });
+ eraseIf(colAttrRight, [](const Grid::ColAttributes& ca) { return !ca.visible; });
if (provLeft && provCenter && provRight)
{
diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h
index dbb0d136..fb37e667 100755
--- a/FreeFileSync/Source/ui/main_dlg.h
+++ b/FreeFileSync/Source/ui/main_dlg.h
@@ -336,8 +336,8 @@ private:
time_t manualTimeSpanFrom_ = 0;
time_t manualTimeSpanTo_ = 0; //buffer manual time span selection at session level
- std::shared_ptr<FolderHistory> folderHistoryLeft_ = std::make_shared<FolderHistory>(); //shared by all wxComboBox dropdown controls
- std::shared_ptr<FolderHistory> folderHistoryRight_ = std::make_shared<FolderHistory>(); //always bound!
+ zen::SharedRef<FolderHistory> folderHistoryLeft_ = zen::makeSharedRef<FolderHistory>(); //shared by all wxComboBox dropdown controls
+ zen::SharedRef<FolderHistory> folderHistoryRight_ = zen::makeSharedRef<FolderHistory>(); //always bound!
zen::AsyncGuiQueue guiQueue_; //schedule and run long-running tasks asynchronously, but process results on GUI queue
diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp
index a2cfa0fa..573540b2 100755
--- a/FreeFileSync/Source/ui/progress_indicator.cpp
+++ b/FreeFileSync/Source/ui/progress_indicator.cpp
@@ -922,7 +922,7 @@ SyncProgressDialogImpl<TopLevelDialog>::~SyncProgressDialogImpl()
parentFrame_->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::onParentKeyEvent), nullptr, this);
parentFrame_->SetTitle(parentTitleBackup_); //restore title text
-
+
//make sure main dialog is shown again if still "minimized to systray"! see SyncProgressDialog::closeDirectly()
parentFrame_->Show();
//if (parentFrame_->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize!
@@ -1328,8 +1328,10 @@ void SyncProgressDialogImpl<TopLevelDialog>::showSummary(SyncResult finalStatus,
const double timeDelta = std::chrono::duration<double>(stopWatch_.elapsed() - phaseStart_).count();
//we need to consider "time within current phase" not total "timeElapsed"!
- const wxString overallBytesPerSecond = numeric::isNull(timeDelta) ? std::wstring() : formatFilesizeShort(numeric::round(bytesProcessed / timeDelta)) + _("/sec");
- const wxString overallItemsPerSecond = numeric::isNull(timeDelta) ? std::wstring() : replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsProcessed / timeDelta));
+ const wxString overallBytesPerSecond = numeric::isNull(timeDelta) ? std::wstring() :
+ replaceCpy(_("%x/sec"), L"%x", formatFilesizeShort(numeric::round(bytesProcessed / timeDelta)));
+ const wxString overallItemsPerSecond = numeric::isNull(timeDelta) ? std::wstring() :
+ replaceCpy(_("%x/sec"), L"%x", replaceCpy(_("%x items"), L"%x", formatThreeDigitPrecision(itemsProcessed / timeDelta)));
pnl_.m_panelGraphBytes->setAttributes(pnl_.m_panelGraphBytes->getAttributes().setCornerText(overallBytesPerSecond, Graph2D::CORNER_TOP_LEFT));
pnl_.m_panelGraphItems->setAttributes(pnl_.m_panelGraphItems->getAttributes().setCornerText(overallItemsPerSecond, Graph2D::CORNER_TOP_LEFT));
diff --git a/FreeFileSync/Source/ui/search_grid.cpp b/FreeFileSync/Source/ui/search_grid.cpp
index 50ee3ab0..cd03aa8d 100755
--- a/FreeFileSync/Source/ui/search_grid.cpp
+++ b/FreeFileSync/Source/ui/search_grid.cpp
@@ -69,7 +69,7 @@ ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found
if (auto prov = grid.getDataProvider())
{
std::vector<Grid::ColAttributes> colAttr = grid.getColumnConfig();
- erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
+ eraseIf(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; });
if (!colAttr.empty())
{
const MatchFound<respectCase> matchFound(searchString);
diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp
index 7b71fe1e..f9c5f704 100755
--- a/FreeFileSync/Source/ui/small_dlgs.cpp
+++ b/FreeFileSync/Source/ui/small_dlgs.cpp
@@ -28,7 +28,7 @@
#include "../base/algorithm.h"
#include "../base/synchronization.h"
#include "../base/help_provider.h"
-#include "../base/hard_filter.h"
+#include "../base/path_filter.h"
#include "../base/status_handler.h" //updateUiIsAllowed()
#include "../base/generate_logfile.h"
#include "../base/icon_buffer.h"
@@ -175,8 +175,8 @@ class CopyToDialog : public CopyToDlgGenerated
{
public:
CopyToDialog(wxWindow* parent,
- const std::vector<const FileSystemObject*>& rowsOnLeft,
- const std::vector<const FileSystemObject*>& rowsOnRight,
+ std::span<const FileSystemObject* const> rowsOnLeft,
+ std::span<const FileSystemObject* const> rowsOnRight,
Zstring& lastUsedPath,
const std::shared_ptr<FolderHistory>& folderHistory,
bool& keepRelPaths,
@@ -200,8 +200,8 @@ private:
CopyToDialog::CopyToDialog(wxWindow* parent,
- const std::vector<const FileSystemObject*>& rowsOnLeft,
- const std::vector<const FileSystemObject*>& rowsOnRight,
+ std::span<const FileSystemObject* const> rowsOnLeft,
+ std::span<const FileSystemObject* const> rowsOnRight,
Zstring& lastUsedPath,
const std::shared_ptr<FolderHistory>& folderHistory,
bool& keepRelPaths,
@@ -291,8 +291,8 @@ void CopyToDialog::OnOK(wxCommandEvent& event)
ReturnSmallDlg::ButtonPressed fff::showCopyToDialog(wxWindow* parent,
- const std::vector<const FileSystemObject*>& rowsOnLeft,
- const std::vector<const FileSystemObject*>& rowsOnRight,
+ std::span<const FileSystemObject* const> rowsOnLeft,
+ std::span<const FileSystemObject* const> rowsOnRight,
Zstring& lastUsedPath,
std::vector<Zstring>& folderPathHistory,
size_t historySizeMax,
@@ -315,8 +315,8 @@ class DeleteDialog : public DeleteDlgGenerated
{
public:
DeleteDialog(wxWindow* parent,
- const std::vector<const FileSystemObject*>& rowsOnLeft,
- const std::vector<const FileSystemObject*>& rowsOnRight,
+ std::span<const FileSystemObject* const> rowsOnLeft,
+ std::span<const FileSystemObject* const> rowsOnRight,
bool& useRecycleBin);
private:
@@ -329,8 +329,8 @@ private:
void updateGui();
- const std::vector<const FileSystemObject*>& rowsToDeleteOnLeft_;
- const std::vector<const FileSystemObject*>& rowsToDeleteOnRight_;
+ const std::span<const FileSystemObject* const> rowsToDeleteOnLeft_;
+ const std::span<const FileSystemObject* const> rowsToDeleteOnRight_;
const std::chrono::steady_clock::time_point dlgStartTime_ = std::chrono::steady_clock::now();
//output-only parameters:
@@ -339,8 +339,8 @@ private:
DeleteDialog::DeleteDialog(wxWindow* parent,
- const std::vector<const FileSystemObject*>& rowsOnLeft,
- const std::vector<const FileSystemObject*>& rowsOnRight,
+ std::span<const FileSystemObject* const> rowsOnLeft,
+ std::span<const FileSystemObject* const> rowsOnRight,
bool& useRecycleBin) :
DeleteDlgGenerated(parent),
rowsToDeleteOnLeft_(rowsOnLeft),
@@ -424,8 +424,8 @@ void DeleteDialog::OnOK(wxCommandEvent& event)
ReturnSmallDlg::ButtonPressed fff::showDeleteDialog(wxWindow* parent,
- const std::vector<const FileSystemObject*>& rowsOnLeft,
- const std::vector<const FileSystemObject*>& rowsOnRight,
+ std::span<const FileSystemObject* const> rowsOnLeft,
+ std::span<const FileSystemObject* const> rowsOnRight,
bool& useRecycleBin)
{
DeleteDialog confirmDeletion(parent, rowsOnLeft, rowsOnRight, useRecycleBin);
diff --git a/FreeFileSync/Source/ui/small_dlgs.h b/FreeFileSync/Source/ui/small_dlgs.h
index c5b806ce..c03415f1 100755
--- a/FreeFileSync/Source/ui/small_dlgs.h
+++ b/FreeFileSync/Source/ui/small_dlgs.h
@@ -28,8 +28,8 @@ struct ReturnSmallDlg
void showAboutDialog(wxWindow* parent);
ReturnSmallDlg::ButtonPressed showCopyToDialog(wxWindow* parent,
- const std::vector<const FileSystemObject*>& rowsOnLeft,
- const std::vector<const FileSystemObject*>& rowsOnRight,
+ std::span<const FileSystemObject* const> rowsOnLeft,
+ std::span<const FileSystemObject* const> rowsOnRight,
Zstring& lastUsedPath,
std::vector<Zstring>& folderPathHistory,
size_t historySizeMax,
@@ -37,8 +37,8 @@ ReturnSmallDlg::ButtonPressed showCopyToDialog(wxWindow* parent,
bool& overwriteIfExists);
ReturnSmallDlg::ButtonPressed showDeleteDialog(wxWindow* parent,
- const std::vector<const FileSystemObject*>& rowsOnLeft,
- const std::vector<const FileSystemObject*>& rowsOnRight,
+ std::span<const FileSystemObject* const> rowsOnLeft,
+ std::span<const FileSystemObject* const> rowsOnRight,
bool& useRecycleBin);
ReturnSmallDlg::ButtonPressed showSyncConfirmationDlg(wxWindow* parent,
diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp
index e3b6a7bc..728c9bbe 100755
--- a/FreeFileSync/Source/ui/sync_cfg.cpp
+++ b/FreeFileSync/Source/ui/sync_cfg.cpp
@@ -90,8 +90,8 @@ private:
CompareVariant localCmpVar_ = CompareVariant::TIME_SIZE;
- std::set<AbstractPath> devicePathsForEdit_; //helper data for deviceParallelOps
- std::map<AbstractPath, size_t> deviceParallelOps_; //
+ std::set<AfsDevice> devicesForEdit_; //helper data for deviceParallelOps
+ std::map<AfsDevice, size_t> deviceParallelOps_; //
//------------- filter panel --------------------------
void OnHelpShowExamples(wxHyperlinkEvent& event) override { displayHelpEntry(L"exclude-items", this); }
@@ -306,7 +306,7 @@ commandHistItemsMax_(commandHistItemsMax)
m_staticTextCompVarDescription->SetMinSize(wxSize(fastFromDIP(CFG_DESCRIPTION_WIDTH_DIP), -1));
- m_scrolledWindowPerf->SetMinSize(wxSize(fastFromDIP(200), -1));
+ m_scrolledWindowPerf->SetMinSize(wxSize(fastFromDIP(220), -1));
m_bitmapPerf->SetBitmap(perfPanelActive_ ? getResourceImage(L"speed") : greyScale(getResourceImage(L"speed")));
m_panelPerfHeader ->Enable(perfPanelActive_);
m_staticTextPerfParallelOps->Enable(perfPanelActive_);
@@ -1170,12 +1170,12 @@ MiscSyncConfig ConfigDialog::getMiscSyncOptions() const
// - don't touch items corresponding to paths not currently used
// - don't store parallel ops == 1
miscCfg.deviceParallelOps = deviceParallelOps_;
- assert(fgSizerPerf->GetItemCount() == 2 * devicePathsForEdit_.size());
+ assert(fgSizerPerf->GetItemCount() == 2 * devicesForEdit_.size());
int i = 0;
- for (const AbstractPath& devPath : devicePathsForEdit_)
+ for (const AfsDevice& afsDevice : devicesForEdit_)
{
wxSpinCtrl* spinCtrlParallelOps = dynamic_cast<wxSpinCtrl*>(fgSizerPerf->GetItem(i * 2)->GetWindow());
- setDeviceParallelOps(miscCfg.deviceParallelOps, devPath, spinCtrlParallelOps->GetValue());
+ setDeviceParallelOps(miscCfg.deviceParallelOps, afsDevice, spinCtrlParallelOps->GetValue());
++i;
}
//----------------------------------------------------------------------------
@@ -1204,7 +1204,7 @@ void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg)
deviceParallelOps_ = miscCfg.deviceParallelOps;
assert(fgSizerPerf->GetItemCount() % 2 == 0);
- const int rowsToCreate = static_cast<int>(devicePathsForEdit_.size()) - static_cast<int>(fgSizerPerf->GetItemCount() / 2);
+ const int rowsToCreate = static_cast<int>(devicesForEdit_.size()) - static_cast<int>(fgSizerPerf->GetItemCount() / 2);
if (rowsToCreate >= 0)
for (int i = 0; i < rowsToCreate; ++i)
{
@@ -1220,16 +1220,16 @@ void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg)
else
for (int i = 0; i < -rowsToCreate * 2; ++i)
fgSizerPerf->GetItem(size_t(0))->GetWindow()->Destroy();
- assert(fgSizerPerf->GetItemCount() == 2 * devicePathsForEdit_.size());
+ assert(fgSizerPerf->GetItemCount() == 2 * devicesForEdit_.size());
int i = 0;
- for (const AbstractPath& devPath : devicePathsForEdit_)
+ for (const AfsDevice& afsDevice : devicesForEdit_)
{
wxSpinCtrl* spinCtrlParallelOps = dynamic_cast<wxSpinCtrl*> (fgSizerPerf->GetItem(i * 2 )->GetWindow());
wxStaticText* staticTextDevice = dynamic_cast<wxStaticText*>(fgSizerPerf->GetItem(i * 2 + 1)->GetWindow());
- spinCtrlParallelOps->SetValue(static_cast<int>(getDeviceParallelOps(deviceParallelOps_, devPath)));
- staticTextDevice->SetLabel(AFS::getDisplayPath(devPath));
+ spinCtrlParallelOps->SetValue(static_cast<int>(getDeviceParallelOps(deviceParallelOps_, afsDevice)));
+ staticTextDevice->SetLabel(AFS::getDisplayPath(AbstractPath(afsDevice, AfsPath())));
++i;
}
m_panelComparisonSettings->Layout(); //*after* setting text labels
@@ -1277,7 +1277,7 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow)
{
assert(selectedPairIndexToShow_ == EMPTY_PAIR_INDEX_SELECTED);
assert(newPairIndexToShow == -1 || makeUnsigned(newPairIndexToShow) < localPairCfg_.size());
- numeric::clamp(newPairIndexToShow, -1, static_cast<int>(localPairCfg_.size()) - 1);
+ newPairIndexToShow = std::clamp(newPairIndexToShow, -1, static_cast<int>(localPairCfg_.size()) - 1);
selectedPairIndexToShow_ = newPairIndexToShow;
m_listBoxFolderPair->SetSelection(newPairIndexToShow + 1);
@@ -1314,12 +1314,12 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow)
//update the devices list for "parallel file operations" before calling setMiscSyncOptions():
// => should be enough to do this when selecting the main config
// => to be "perfect" we'd have to update already when the user drags & drops a different versioning folder
- devicePathsForEdit_.clear();
+ devicesForEdit_.clear();
auto addDevicePath = [&](const Zstring& folderPathPhrase)
{
- const AbstractPath rootPath = AFS::getRootPath(createAbstractPath(folderPathPhrase));
- if (!AFS::isNullPath(rootPath))
- devicePathsForEdit_.insert(rootPath);
+ const AfsDevice& afsDevice = createAbstractPath(folderPathPhrase).afsDevice;
+ if (!AFS::isNullDevice(afsDevice))
+ devicesForEdit_.insert(afsDevice);
};
for (const LocalPairConfig& fpCfg : localPairCfg_)
{
diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h
index e660c3a0..34c4acf2 100755
--- a/FreeFileSync/Source/ui/sync_cfg.h
+++ b/FreeFileSync/Source/ui/sync_cfg.h
@@ -31,7 +31,7 @@ enum class SyncConfigPanel
struct MiscSyncConfig
{
- std::map<AbstractPath, size_t> deviceParallelOps;
+ std::map<AfsDevice, size_t> deviceParallelOps;
bool ignoreErrors = false;
size_t automaticRetryCount = 0;
std::chrono::seconds automaticRetryDelay{0};
diff --git a/FreeFileSync/Source/ui/tray_icon.cpp b/FreeFileSync/Source/ui/tray_icon.cpp
index 4508f7a6..22d91f66 100755
--- a/FreeFileSync/Source/ui/tray_icon.cpp
+++ b/FreeFileSync/Source/ui/tray_icon.cpp
@@ -80,7 +80,7 @@ wxIcon FfsTrayIcon::ProgressIconGenerator::get(double fraction)
return wxIcon();
const int pixelCount = logo_.GetWidth() * logo_.GetHeight();
- const int startFillPixel = numeric::clampCpy<int>(numeric::round(fraction * pixelCount), 0, pixelCount);
+ const int startFillPixel = std::clamp<int>(numeric::round(fraction * pixelCount), 0, pixelCount);
if (startPixBuf_ != startFillPixel)
{
diff --git a/FreeFileSync/Source/ui/tree_grid.cpp b/FreeFileSync/Source/ui/tree_grid.cpp
index bf3833b5..8d5b71a1 100755
--- a/FreeFileSync/Source/ui/tree_grid.cpp
+++ b/FreeFileSync/Source/ui/tree_grid.cpp
@@ -600,7 +600,7 @@ void TreeView::setData(FolderComparison& newData)
folderCmp_ = newData;
//remove truly empty folder pairs as early as this: we want to distinguish single/multiple folder pair cases by looking at "folderCmp"
- erase_if(folderCmp_, [](const std::shared_ptr<BaseFolderPair>& baseObj)
+ eraseIf(folderCmp_, [](const std::shared_ptr<BaseFolderPair>& baseObj)
{
return AFS::isNullPath(baseObj->getAbstractPath< LEFT_SIDE>()) &&
AFS::isNullPath(baseObj->getAbstractPath<RIGHT_SIDE>());
diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h
index 0a978b6c..0074ae18 100755
--- a/FreeFileSync/Source/version/version.h
+++ b/FreeFileSync/Source/version/version.h
@@ -3,7 +3,7 @@
namespace fff
{
-const char ffsVersion[] = "10.5"; //internal linkage!
+const char ffsVersion[] = "10.6"; //internal linkage!
const char FFS_VERSION_SEPARATOR = '.';
}
diff --git a/wx+/async_task.h b/wx+/async_task.h
index df4c3ec6..074f5337 100755
--- a/wx+/async_task.h
+++ b/wx+/async_task.h
@@ -83,9 +83,9 @@ public:
inRecursion_ = true;
ZEN_ON_SCOPE_EXIT(inRecursion_ = false);
- std::vector<std::unique_ptr<Task>> readyTasks; //Reentrancy; access to AsyncTasks::add is not protected! => evaluate outside erase_if
+ std::vector<std::unique_ptr<Task>> readyTasks; //Reentrancy; access to AsyncTasks::add is not protected! => evaluate outside eraseIf
- erase_if(tasks_, [&](std::unique_ptr<Task>& task)
+ eraseIf(tasks_, [&](std::unique_ptr<Task>& task)
{
if (task->resultReady())
{
diff --git a/wx+/graph.cpp b/wx+/graph.cpp
index e00bed86..9cacd1bf 100755
--- a/wx+/graph.cpp
+++ b/wx+/graph.cpp
@@ -91,7 +91,7 @@ public:
int realToScreenRound(double realPos) const //returns -1 and screenSize + 1 if out of bounds!
{
//catch large double values: if double is larger than what int can represent => undefined behavior!
- numeric::clamp(realPos, outOfBoundsLow_, outOfBoundsHigh_);
+ realPos = std::clamp(realPos, outOfBoundsLow_, outOfBoundsHigh_);
return numeric::round(realToScreen(realPos));
}
@@ -724,10 +724,10 @@ void Graph2D::render(wxDC& dc) const
const wxPoint screenCurrent = activeSel_->refCurrentPos() - graphAreaOrigin;
//normalize positions: a mouse selection is symmetric and *not* an half-open range!
- double screenFromX = numeric::clampCpy(screenStart .x, 0, graphArea.width - 1);
- double screenFromY = numeric::clampCpy(screenStart .y, 0, graphArea.height - 1);
- double screenToX = numeric::clampCpy(screenCurrent.x, 0, graphArea.width - 1);
- double screenToY = numeric::clampCpy(screenCurrent.y, 0, graphArea.height - 1);
+ double screenFromX = std::clamp(screenStart .x, 0, graphArea.width - 1);
+ double screenFromY = std::clamp(screenStart .y, 0, graphArea.height - 1);
+ double screenToX = std::clamp(screenCurrent.x, 0, graphArea.width - 1);
+ double screenToY = std::clamp(screenCurrent.y, 0, graphArea.height - 1);
widen(&screenFromX, &screenToX); //use full pixel range for selection!
widen(&screenFromY, &screenToY);
@@ -782,10 +782,10 @@ void Graph2D::render(wxDC& dc) const
shrink(&screenFromX, &screenToX);
shrink(&screenFromY, &screenToY);
- numeric::clamp(screenFromX, 0.0, graphArea.width - 1.0);
- numeric::clamp(screenFromY, 0.0, graphArea.height - 1.0);
- numeric::clamp(screenToX, 0.0, graphArea.width - 1.0);
- numeric::clamp(screenToY, 0.0, graphArea.height - 1.0);
+ screenFromX = std::clamp(screenFromX, 0.0, graphArea.width - 1.0);
+ screenFromY = std::clamp(screenFromY, 0.0, graphArea.height - 1.0);
+ screenToX = std::clamp(screenToX, 0.0, graphArea.width - 1.0);
+ screenToY = std::clamp(screenToY, 0.0, graphArea.height - 1.0);
const wxPoint pixelFrom = wxPoint(numeric::round(screenFromX),
numeric::round(screenFromY)) + graphAreaOrigin;
diff --git a/wx+/grid.cpp b/wx+/grid.cpp
index 65311ffa..3c19c246 100755
--- a/wx+/grid.cpp
+++ b/wx+/grid.cpp
@@ -171,7 +171,6 @@ wxSize GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring&
if (high > 1)
for (;;)
{
- const size_t middle = (low + high) / 2; //=> never 0 when "high - low > 1"
if (high - low <= 1)
{
if (low == 0)
@@ -181,6 +180,7 @@ wxSize GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring&
}
break;
}
+ const size_t middle = (low + high) / 2; //=> never 0 when "high - low > 1"
const std::wstring& candidate = getUnicodeSubstring(text, 0, middle) + ELLIPSIS;
const wxSize extentCand = dc.GetTextExtent(candidate); //perf: most expensive call of this routine!
@@ -1193,7 +1193,7 @@ private:
//select current row *after* scrolling
wxPoint clientPosTrimmed = clientPos;
- numeric::clamp(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window!
+ clientPosTrimmed.y = std::clamp(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window!
const wxPoint absPos = wnd_.refParent().CalcUnscrolledPosition(clientPosTrimmed);
const ptrdiff_t newRow = wnd_.rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range
@@ -1357,8 +1357,8 @@ void Grid::updateWindowSizes(bool updateScrollbar)
{
ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y;
ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ;
- numeric::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1);
- numeric::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1);
+ yFrom = std::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1);
+ yTo = std::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1);
const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom);
const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo);
@@ -1465,8 +1465,8 @@ wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size)
{
ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y;
ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ;
- numeric::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1);
- numeric::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1);
+ yFrom = std::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1);
+ yTo = std::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1);
const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom);
const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo);
@@ -1499,7 +1499,7 @@ void Grid::onKeyDown(wxKeyEvent& event)
{
if (rowCount > 0)
{
- numeric::clamp<ptrdiff_t>(row, 0, rowCount - 1);
+ row = std::clamp<ptrdiff_t>(row, 0, rowCount - 1);
setGridCursor(row, GridEventPolicy::ALLOW);
}
};
@@ -1508,7 +1508,7 @@ void Grid::onKeyDown(wxKeyEvent& event)
{
if (rowCount > 0)
{
- numeric::clamp<ptrdiff_t>(row, 0, rowCount - 1);
+ row = std::clamp<ptrdiff_t>(row, 0, rowCount - 1);
selectWithCursor(row); //emits GridSelectEvent
}
};
@@ -2018,8 +2018,8 @@ void Grid::selectRange(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const
auto rowLast = std::max(rowFrom, rowTo) + 1;
const size_t rowCount = getRowCount();
- numeric::clamp<ptrdiff_t>(rowFirst, 0, rowCount);
- numeric::clamp<ptrdiff_t>(rowLast, 0, rowCount);
+ rowFirst = std::clamp<ptrdiff_t>(rowFirst, 0, rowCount);
+ rowLast = std::clamp<ptrdiff_t>(rowLast, 0, rowCount);
selection_.selectRange(rowFirst, rowLast, positive);
mainWin_->Refresh();
diff --git a/wx+/grid.h b/wx+/grid.h
index ccf7ad64..102396c3 100755
--- a/wx+/grid.h
+++ b/wx+/grid.h
@@ -271,8 +271,8 @@ private:
{
if (rowFirst <= rowLast)
{
- numeric::clamp<size_t>(rowFirst, 0, selected_.size());
- numeric::clamp<size_t>(rowLast, 0, selected_.size());
+ rowFirst = std::clamp<size_t>(rowFirst, 0, selected_.size());
+ rowLast = std::clamp<size_t>(rowLast, 0, selected_.size());
std::fill(selected_.begin() + rowFirst, selected_.begin() + rowLast, positive);
}
diff --git a/wx+/image_holder.h b/wx+/image_holder.h
index aada581f..b11ae451 100755
--- a/wx+/image_holder.h
+++ b/wx+/image_holder.h
@@ -39,9 +39,9 @@ struct ImageHolder //prepare conversion to wxImage as much as possible while sta
unsigned char* releaseRgb () { return rgb_ .release(); }
unsigned char* releaseAlpha() { return alpha_.release(); }
+private:
struct CLibFree { void operator()(unsigned char* p) const { ::free(p); } }; //use malloc/free to allow direct move into wxImage!
-private:
int width_ = 0;
int height_ = 0;
std::unique_ptr<unsigned char, CLibFree> rgb_; //optional
diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp
index d09a188b..5bc8006f 100755
--- a/wx+/image_resources.cpp
+++ b/wx+/image_resources.cpp
@@ -214,7 +214,7 @@ void GlobalBitmaps::init(const Zstring& filePath)
//do NOT rely on wxConvLocal! On failure shows unhelpful popup "Cannot convert from the charset 'Unknown encoding (-1)'!"
//do we need xBRZ scaling for high quality DPI images?
- const int hqScale = numeric::clampCpy<int>(std::ceil(fastFromDIP(1000) / 1000.0), 1, xbrz::SCALE_FACTOR_MAX);
+ const int hqScale = std::clamp<int>(std::ceil(fastFromDIP(1000) / 1000.0), 1, xbrz::SCALE_FACTOR_MAX);
//even for 125% DPI scaling, "2xBRZ + bilinear downscale" gives a better result than mere "125% bilinear upscale"!
if (hqScale > 1)
dpiScaler_ = std::make_unique<DpiParallelScaler>(hqScale);
diff --git a/zen/basic_math.h b/zen/basic_math.h
index 0d08f6a6..75f5d3b8 100755
--- a/zen/basic_math.h
+++ b/zen/basic_math.h
@@ -21,14 +21,8 @@ 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);
-//std::clamp() available with C++17
-
template <class T, class InputIterator> //precondition: range must be sorted!
auto nearMatch(const T& val, InputIterator first, InputIterator last);
@@ -106,51 +100,6 @@ int sign(T value) //returns one of {-1, 0, 1}
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
diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp
index 2bb3fd26..f5ed0488 100755
--- a/zen/dir_watcher.cpp
+++ b/zen/dir_watcher.cpp
@@ -25,7 +25,7 @@ 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"
+ std::map<int, Zstring> watchedPaths; //watch descriptor and (sub-)directory paths -> owned by "notifDescr"
};
@@ -90,7 +90,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError
throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)), formatSystemError(L"inotify_add_watch", ec));
}
- pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath));
+ pimpl_->watchedPaths.emplace(wd, subDirPath);
}
}
@@ -130,12 +130,12 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void()
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())
+ auto it = pimpl_->watchedPaths.find(evt.wd);
+ if (it != pimpl_->watchedPaths.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 itemPath = it->second + evt.name;
+ const Zstring itemPath = appendSeparator(it->second) + evt.name;
if ((evt.mask & IN_CREATE) ||
(evt.mask & IN_MOVED_TO))
diff --git a/zen/file_access.cpp b/zen/file_access.cpp
index 88b70b14..82c78760 100755
--- a/zen/file_access.cpp
+++ b/zen/file_access.cpp
@@ -50,32 +50,32 @@ std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath)
return {};
};
- if (startsWith(itemPath, "/"))
- {
- if (startsWith(itemPath, "/media/"))
- {
- //Ubuntu: e.g. /media/zenju/DEVICE_NAME
- if (const char* username = ::getenv("USER"))
- if (startsWith(itemPath, std::string("/media/") + username + "/"))
- return doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+ std::optional<PathComponents> pc; //"/media/zenju/" and "/Volumes/" should not fail to parse
- //Ubuntu: e.g. /media/cdrom0
- return doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- }
+ if (!pc && startsWith(itemPath, "/mnt/")) //e.g. /mnt/DEVICE_NAME
+ pc = doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- if (startsWith(itemPath, "/run/media/")) //Suse: e.g. /run/media/zenju/DEVICE_NAME
- if (const char* username = ::getenv("USER"))
- if (startsWith(itemPath, std::string("/run/media/") + username + "/"))
- return doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+ if (!pc && startsWith(itemPath, "/media/")) //Ubuntu: e.g. /media/zenju/DEVICE_NAME
+ if (const char* username = ::getenv("USER"))
+ if (startsWith(itemPath, std::string("/media/") + username + "/"))
+ pc = doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- return doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/);
- }
+ if (!pc && startsWith(itemPath, "/run/media/")) //Centos, Suse: e.g. /run/media/zenju/DEVICE_NAME
+ if (const char* username = ::getenv("USER"))
+ if (startsWith(itemPath, std::string("/run/media/") + username + "/"))
+ pc = doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
- //we do NOT support relative paths!
- return {};
-}
+ if (!pc && startsWith(itemPath, "/run/user/")) //Ubuntu, e.g.: /run/user/1000/gvfs/smb-share:server=192.168.62.145,share=folder
+ if (startsWith(itemPath, "/run/user/" + numberTo<std::string>(::getuid()) + "/gvfs/")) //::getuid() never fails
+ pc = doParse(6 /*sepCountVolumeRoot*/, false /*rootWithSep*/);
+ if (!pc && startsWith(itemPath, "/"))
+ pc = doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/);
+
+ return pc;
+}
+
std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath)
{
@@ -108,49 +108,40 @@ ItemType zen::getItemType(const Zstring& itemPath) //throw FileError
}
-PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError
+std::optional<ItemType> zen::itemStillExists(const Zstring& itemPath) //throw FileError
{
- const std::optional<Zstring> parentPath = getParentFolderPath(itemPath);
try
{
- return { getItemType(itemPath), itemPath, {} }; //throw FileError
+ return getItemType(itemPath); //throw FileError
}
- catch (FileError&)
+ catch (const FileError& e) //not existing or access error
{
+ const std::optional<Zstring> parentPath = getParentFolderPath(itemPath);
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 ps = getPathStatus(*parentPath); //throw FileError
- if (ps.relPath.empty() &&
- ps.existingType != ItemType::FILE) //obscure, but possible (and not an error)
- 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); });
- }
- 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)
-
- ps.relPath.push_back(itemName);
- return ps;
-}
+ const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL);
+ assert(!itemName.empty());
-std::optional<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError
-{
- const PathStatus ps = getPathStatus(itemPath); //throw FileError
- if (ps.relPath.empty())
- return ps.existingType;
- return {};
+ const std::optional<ItemType> parentType = itemStillExists(*parentPath); //throw FileError
+ if (parentType && *parentType != ItemType::FILE /*obscure, but possible (and not an error)*/)
+ try
+ {
+ traverseFolder(*parentPath,
+ [&](const FileInfo& fi) { if (fi.itemName == itemName) throw ItemType::FILE; },
+ [&](const FolderInfo& fi) { if (fi.itemName == itemName) throw ItemType::FOLDER; },
+ [&](const SymlinkInfo& si) { if (si.itemName == itemName) throw ItemType::SYMLINK; },
+ [](const std::wstring& errorMsg) { throw FileError(errorMsg); });
+ }
+ catch (const ItemType&) //finding the item after getItemType() previously failed is exceptional
+ {
+ throw e; //yes, slicing
+ }
+ return {};
+ }
}
@@ -174,16 +165,6 @@ bool zen::dirAvailable(const Zstring& dirPath) //noexcept
}
-bool zen::itemNotExisting(const Zstring& itemPath)
-{
- try
- {
- return !getItemTypeIfExists(itemPath); //throw FileError
- }
- catch (FileError&) { return false; }
-}
-
-
namespace
{
}
@@ -209,13 +190,13 @@ uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0
}
-VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError
+FileId /*optional*/ zen::getFileId(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;
+ return extractFileId(fileInfo);
}
@@ -381,7 +362,7 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr
const Zstring parentPathSrc = beforeLast(pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
const Zstring parentPathTrg = beforeLast(pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE);
//some (broken) devices may fail to rename case directly:
- if (equalLocalPath(parentPathSrc, parentPathTrg))
+ if (equalNativePath(parentPathSrc, parentPathTrg))
{
if (fileNameSrc == fileNameTrg)
return; //non-sensical request
@@ -567,8 +548,18 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget
void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError
{
- if (!getParentFolderPath(dirPath)) //device root
- return static_cast<void>(/*ItemType =*/ getItemType(dirPath)); //throw FileError
+ const std::optional<Zstring> parentPath = getParentFolderPath(dirPath);
+ if (!parentPath) //device root
+ return;
+
+ try //generally we expect that path already exists (see: ffs_paths.cpp) => check first
+ {
+ if (getItemType(dirPath) != ItemType::FILE) //throw FileError
+ return;
+ }
+ catch (FileError&) {} //not yet existing or access error? let's find out...
+
+ createDirectoryIfMissingRecursion(*parentPath); //throw FileError
try
{
@@ -576,18 +567,15 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File
}
catch (FileError&)
{
- const PathStatus ps = getPathStatus(dirPath); //throw FileError
- if (ps.existingType == ItemType::FILE)
- throw;
+ try
+ {
+ if (getItemType(dirPath) != ItemType::FILE) //throw FileError
+ return; //already existing => possible, if createDirectoryIfMissingRecursion() is run in parallel
+ }
+ catch (FileError&) {} //not yet existing or access error
+ //catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } -> details needed???
- //ps.relPath.size() == 1 => same createDirectory() call from above? Maybe parent folder was created by parallel thread shortly after failure!
- Zstring intermediatePath = ps.existingPath;
- for (const Zstring& itemName : ps.relPath)
- try
- {
- createDirectory(intermediatePath = appendSeparator(intermediatePath) + itemName); //throw FileError, ErrorTargetExisting
- }
- catch (ErrorTargetExisting&) {} //possible, if createDirectoryIfMissingRecursion() is run in parallel
+ throw;
}
}
diff --git a/zen/file_access.h b/zen/file_access.h
index 916f23f5..514d798e 100755
--- a/zen/file_access.h
+++ b/zen/file_access.h
@@ -30,8 +30,6 @@ std::optional<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
{
@@ -40,17 +38,12 @@ enum class ItemType
SYMLINK,
};
//(hopefully) fast: does not distinguish between error/not existing
-ItemType getItemType (const Zstring& itemPath); //throw FileError
+ItemType getItemType(const Zstring& itemPath); //throw FileError
//execute potentially SLOW folder traversal but distinguish error/not existing
-std::optional<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
+// assumes: - base path still exists
+// - all child item path parts must correspond to folder traversal
+// => we can conclude whether an item is *not* existing anymore by doing a *case-sensitive* name search => potentially SLOW!
+std::optional<ItemType> itemStillExists(const Zstring& itemPath); //throw FileError
enum class ProcSymlink
{
@@ -62,7 +55,8 @@ void setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl); /
//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
+FileId /*optional*/ getFileId(const Zstring& itemPath); //throw FileError
+
//get per-user directory designated for temporary files:
Zstring getTempFolderPath(); //throw FileError
diff --git a/zen/file_io.cpp b/zen/file_io.cpp
index df47e4c5..80fb3153 100755
--- a/zen/file_io.cpp
+++ b/zen/file_io.cpp
@@ -128,7 +128,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, E
return bytesRead; //"zero indicates end of file"
}
-
+
size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream!
{
/*
diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h
index 16d87c53..bebf5a05 100755
--- a/zen/legacy_compiler.h
+++ b/zen/legacy_compiler.h
@@ -7,11 +7,47 @@
#ifndef LEGACY_COMPILER_H_839567308565656789
#define LEGACY_COMPILER_H_839567308565656789
+//#include <span> //requires C++20
+
+
namespace std
{
//https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html
//https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations
+
+
+//requires C++20! until then, this should suffice...
+template <class T>
+class span
+{
+public:
+ template <class Iterator>
+ span(Iterator first, Iterator last) : size_(last - first), data_(first != last ? &*first : nullptr) {}
+
+ template <class Container>
+ span(Container& cont) : span(cont.begin(), cont.end()) {}
+
+ using iterator = T*;
+ using const_iterator = const T*;
+
+ iterator begin() { return data_; }
+ iterator end () { return data_ + size_; }
+
+ const_iterator begin() const { return data_; }
+ const_iterator end () const { return data_ + size_; }
+
+ const_iterator cbegin() const { return begin(); }
+ const_iterator cend () const { return end (); }
+
+ T* data() const { return data_; }
+ size_t size() const { return size_; }
+ bool empty() const { return size_ == 0; }
+
+private:
+ const size_t size_;
+ T* const data_;
+};
}
#endif //LEGACY_COMPILER_H_839567308565656789
diff --git a/zen/recycler.cpp b/zen/recycler.cpp
index 8b4389a7..8d34f262 100755
--- a/zen/recycler.cpp
+++ b/zen/recycler.cpp
@@ -27,7 +27,7 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError
if (!::g_file_trash(file, nullptr, &error))
{
- const std::optional<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError
+ const std::optional<ItemType> type = itemStillExists(itemPath); //throw FileError
if (!type)
return false;
diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h
index 232e17da..e3dbd55f 100755
--- a/zen/ring_buffer.h
+++ b/zen/ring_buffer.h
@@ -8,10 +8,7 @@
#define RING_BUFFER_H_01238467085684139453534
#include <cassert>
-#include <vector>
-#include <stdexcept>
#include "scope_guard.h"
-#include "string_tools.h"
namespace zen
@@ -40,13 +37,24 @@ public:
~RingBuffer() { clear(); }
- reference front() { return getBufPtr()[bufStart_]; }
- const_reference front() const { return getBufPtr()[bufStart_]; }
+ reference front() { checkInvariants(); assert(!empty()); return getBufPtr()[bufStart_]; }
+ const_reference front() const { checkInvariants(); assert(!empty()); return getBufPtr()[bufStart_]; }
+
+ reference back() { checkInvariants(); assert(!empty()); return getBufPtr()[getBufPos(size_ - 1)]; }
+ const_reference back() const { checkInvariants(); assert(!empty()); return getBufPtr()[getBufPos(size_ - 1)]; }
+
+ template <class U>
+ void push_front(U&& value)
+ {
+ reserve(size_ + 1); //throw ?
+ ::new (getBufPtr() + getBufPos(capacity_ - 1)) T(std::forward<U>(value)); //throw ?
+ ++size_;
+ bufStart_ = getBufPos(capacity_ - 1);
+ }
template <class U>
void push_back(U&& value)
{
- checkInvariants();
reserve(size_ + 1); //throw ?
::new (getBufPtr() + getBufPos(size_)) T(std::forward<U>(value)); //throw ?
++size_;
@@ -54,15 +62,21 @@ public:
void pop_front()
{
- checkInvariants();
- if (empty())
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
-
front().~T();
- ++bufStart_;
--size_;
- if (size_ == 0 || bufStart_ == capacity_)
+ if (size_ == 0)
+ bufStart_ = 0;
+ else
+ bufStart_ = getBufPos(1);
+ }
+
+ void pop_back()
+ {
+ back().~T();
+ --size_;
+
+ if (size_ == 0)
bufStart_ = 0;
}
@@ -80,8 +94,6 @@ public:
template <class Iterator>
void insert_back(Iterator first, Iterator last) //throw ? (strong exception-safety!)
{
- checkInvariants();
-
const size_t len = last - first;
reserve(size_ + len); //throw ?
@@ -100,10 +112,8 @@ public:
void extract_front(Iterator first, Iterator last) //throw ? strongly exception-safe! (but only basic exception safety for [first, last) range)
{
checkInvariants();
-
const size_t len = last - first;
- if (size_ < len)
- throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
+ assert(size_ >= len);
const size_t frontSize = std::min(len, capacity_ - bufStart_);
@@ -113,13 +123,12 @@ public:
std::destroy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize);
std::destroy(getBufPtr(), getBufPtr() + len - frontSize);
- bufStart_ += len;
- size_ -= len;
+ size_ -= len;
if (size_ == 0)
bufStart_ = 0;
- else if (bufStart_ >= capacity_)
- bufStart_ -= capacity_;
+ else
+ bufStart_ = getBufPos(len);
}
void swap(RingBuffer& other)
@@ -132,6 +141,8 @@ public:
void reserve(size_t minSize) //throw ? (strong exception-safety!)
{
+ checkInvariants();
+
if (minSize > capacity_)
{
const size_t newCapacity = std::max(minSize + minSize / 2, minSize); //no minimum capacity: just like std::vector<> implementation
@@ -205,7 +216,7 @@ private:
struct FreeStoreDelete { void operator()(std::byte* p) const { ::operator delete (p); } };
- T* getBufPtr() { return reinterpret_cast<T*>(rawMem_.get()); }
+ /**/ T* getBufPtr() { return reinterpret_cast<T*>(rawMem_.get()); }
const T* getBufPtr() const { return reinterpret_cast<T*>(rawMem_.get()); }
//unlike pure std::uninitialized_move, this one allows for strong exception-safety!
diff --git a/zen/serialize.h b/zen/serialize.h
index d34b61b2..8b4c58ea 100755
--- a/zen/serialize.h
+++ b/zen/serialize.h
@@ -36,20 +36,20 @@ public:
using iterator = std::vector<std::byte>::iterator;
using const_iterator = std::vector<std::byte>::const_iterator;
- iterator begin() { return buffer_->begin(); }
- iterator end () { return buffer_->end (); }
+ iterator begin() { return buffer_.ref().begin(); }
+ iterator end () { return buffer_.ref().end (); }
- const_iterator begin() const { return buffer_->begin(); }
- const_iterator end () const { return buffer_->end (); }
+ const_iterator begin() const { return buffer_.ref().begin(); }
+ const_iterator end () const { return buffer_.ref().end (); }
- void resize(size_t len) { buffer_->resize(len); }
- size_t size() const { return buffer_->size(); }
- bool empty() const { return buffer_->empty(); }
+ void resize(size_t len) { buffer_.ref().resize(len); }
+ size_t size() const { return buffer_.ref().size(); }
+ bool empty() const { return buffer_.ref().empty(); }
- inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer_ == *rhs.buffer_; }
+ inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return lhs.buffer_.ref() == rhs.buffer_.ref(); }
private:
- std::shared_ptr<std::vector<std::byte>> buffer_ = std::make_shared<std::vector<std::byte>>(); //always bound!
+ SharedRef<std::vector<std::byte>> buffer_ = makeSharedRef<std::vector<std::byte>>();
//perf: shared_ptr indirection irrelevant: less than 1% slower!
};
diff --git a/zen/stl_tools.h b/zen/stl_tools.h
index c3a9bf8f..d8bda888 100755
--- a/zen/stl_tools.h
+++ b/zen/stl_tools.h
@@ -11,10 +11,11 @@
#include <map>
#include <vector>
#include <memory>
+#include <cassert>
#include <algorithm>
#include <optional>
#include "string_traits.h"
-#include "build_info.h"
+//#include "build_info.h"
//enhancements for <algorithm>
@@ -22,13 +23,13 @@ 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);
+void eraseIf(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);
+void eraseIf(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);
+void eraseIf(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p);
//append STL containers
template <class T, class Alloc, class C>
@@ -48,14 +49,14 @@ void removeDuplicates(std::vector<T, Alloc>& v, CompLess less);
//binary search returning an iterator
template <class Iterator, class T, class CompLess>
-Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less);
+Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less);
template <class BidirectionalIterator, class T>
-BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value);
+BidirectionalIterator findLast(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,
+BidirectionalIterator1 searchLast(BidirectionalIterator1 first1, BidirectionalIterator1 last1,
BidirectionalIterator2 first2, BidirectionalIterator2 last2);
template <class Num, class ByteIterator> Num hashBytes (ByteIterator first, ByteIterator last);
@@ -74,17 +75,49 @@ struct StringHash
};
-//why, oh wy is there no std::optional<T>::get()???
+//why, oh why is there no std::optional<T>::get()???
template <class T> inline T* get( std::optional<T>& opt) { return opt ? &*opt : nullptr; }
template <class T> inline const T* get(const std::optional<T>& opt) { return opt ? &*opt : nullptr; }
+//===========================================================================
+template <class T> class SharedRef;
+template <class T, class... Args> SharedRef<T> makeSharedRef(Args&& ... args);
+
+template <class T>
+class SharedRef //why is there no std::shared_ref???
+{
+public:
+ SharedRef() = delete; //no suprise memory allocations => always construct with makeSharedRef()
+
+ template <class U>
+ SharedRef(const SharedRef<U>& other) : ref_(other.ref_) {}
+
+ /**/ T& ref() { return *ref_; };
+ const T& ref() const { return *ref_; };
+
+ std::shared_ptr<T> ptr() { return ref_; };
+
+private:
+ explicit SharedRef(std::shared_ptr<T>&& ptr) : ref_(std::move(ptr)) { assert(ref_); }
+
+ template <class U, class... Args> friend SharedRef<U> makeSharedRef(Args&& ... args);
+ template <class U> friend class SharedRef;
+
+ std::shared_ptr<T> ref_; //always bound
+};
+
+template <class T, class... Args> inline
+SharedRef<T> makeSharedRef(Args&&... args) { return SharedRef<T>(std::make_shared<T>(std::forward<Args>(args)...)); }
+//===========================================================================
+
+
//######################## implementation ########################
template <class T, class Alloc, class Predicate> inline
-void erase_if(std::vector<T, Alloc>& v, Predicate p)
+void eraseIf(std::vector<T, Alloc>& v, Predicate p)
{
v.erase(std::remove_if(v.begin(), v.end(), p), v.end());
}
@@ -93,7 +126,7 @@ void erase_if(std::vector<T, Alloc>& v, Predicate p)
namespace impl
{
template <class S, class Predicate> inline
-void set_or_map_erase_if(S& s, Predicate p)
+void setOrMapEraseIf(S& s, Predicate p)
{
for (auto it = s.begin(); it != s.end();)
if (p(*it))
@@ -105,11 +138,11 @@ void set_or_map_erase_if(S& s, Predicate p)
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!!!
+void eraseIf(std::set<T, LessType, Alloc>& s, Predicate p) { impl::setOrMapEraseIf(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); }
+void eraseIf(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p) { impl::setOrMapEraseIf(m, p); }
template <class T, class Alloc, class C> inline
@@ -147,7 +180,7 @@ void removeDuplicates(std::vector<T, Alloc>& v)
template <class Iterator, class T, class CompLess> inline
-Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less)
+Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less)
{
static_assert(std::is_same_v<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>);
@@ -160,7 +193,7 @@ Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess l
template <class BidirectionalIterator, class T> inline
-BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value)
+BidirectionalIterator findLast(const BidirectionalIterator first, const BidirectionalIterator last, const T& value)
{
for (BidirectionalIterator it = last; it != first;) //reverse iteration: 1. check 2. decrement 3. evaluate
{
@@ -174,7 +207,7 @@ BidirectionalIterator find_last(const BidirectionalIterator first, const Bidirec
template <class BidirectionalIterator1, class BidirectionalIterator2> inline
-BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
+BidirectionalIterator1 searchLast(const BidirectionalIterator1 first1, BidirectionalIterator1 last1,
const BidirectionalIterator2 first2, const BidirectionalIterator2 last2)
{
const BidirectionalIterator1 itNotFound = last1;
diff --git a/zen/string_base.h b/zen/string_base.h
index 9632eba4..91a6d5bd 100755
--- a/zen/string_base.h
+++ b/zen/string_base.h
@@ -410,7 +410,7 @@ 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);
+ const Char* it = findLast(begin(), currEnd, ch);
return it == currEnd ? npos : it - begin();
}
@@ -422,7 +422,7 @@ size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const
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,
+ const Char* it = searchLast(begin(), currEnd,
str, str + strLen);
return it == currEnd ? npos : it - begin();
}
diff --git a/zen/string_tools.h b/zen/string_tools.h
index 657c70d5..8579a460 100755
--- a/zen/string_tools.h
+++ b/zen/string_tools.h
@@ -104,26 +104,19 @@ template <class T, class S> T copyStringTo(S&& str);
//---------------------- implementation ----------------------
-template <> inline
-bool isWhiteSpace(char c)
-{
- assert(c != 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>(c) < 128 &&
- std::isspace(static_cast<unsigned char>(c)) != 0;
-}
-
-template <> inline
-bool isWhiteSpace(wchar_t c)
+template <class Char> inline
+bool isWhiteSpace(Char c)
{
+ static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
assert(c != 0); //std C++ does not consider 0 as white space
- return std::iswspace(c) != 0;
+ return c == static_cast<Char>(' ') || (static_cast<Char>('\t') <= c && c <= static_cast<Char>('\r'));
+ //following std::isspace() for default locale but without the interface insanity:
+ // - std::isspace() takes an int, but expects an unsigned char
+ // - some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
}
-
template <class Char> inline
-bool isDigit(Char c) //similar to implmenetation of std::isdigit()!
+bool isDigit(Char c) //similar to implementation of std::isdigit()!
{
static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>);
return static_cast<Char>('0') <= c && c <= static_cast<Char>('9');
@@ -306,7 +299,7 @@ S afterLast(const S& str, const T& term, FailureReturnVal rv)
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
- const auto* it = search_last(strFirst, strLast,
+ const auto* it = searchLast(strFirst, strLast,
termFirst, termFirst + termLen);
if (it == strLast)
return rv == IF_MISSING_RETURN_ALL ? str : S();
@@ -327,7 +320,7 @@ S beforeLast(const S& str, const T& term, FailureReturnVal rv)
const auto* const strLast = strFirst + strLength(str);
const auto* const termFirst = strBegin(term);
- const auto* it = search_last(strFirst, strLast,
+ const auto* it = searchLast(strFirst, strLast,
termFirst, termFirst + termLen);
if (it == strLast)
return rv == IF_MISSING_RETURN_ALL ? str : S();
@@ -725,7 +718,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i
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
+ break; //assert(std::all_of(iter, last, isWhiteSpace<CharType>)); -> this is NO assert situation
}
return number;
}
diff --git a/zen/string_traits.h b/zen/string_traits.h
index cd7dbf1b..93cfd81c 100755
--- a/zen/string_traits.h
+++ b/zen/string_traits.h
@@ -53,7 +53,7 @@ public:
private:
const size_t len_;
- Char* str_;
+ Char* const str_;
};
diff --git a/zen/thread.h b/zen/thread.h
index 7f3d216c..809bc771 100755
--- a/zen/thread.h
+++ b/zen/thread.h
@@ -90,10 +90,10 @@ bool isReady(const std::future<T>& f) { return f.wait_for(std::chrono::seconds(0
//wait until first job is successful or all failed: substitute until std::when_any is available
template <class T>
-class GetFirstResult
+class AsyncFirstResult
{
public:
- GetFirstResult();
+ AsyncFirstResult();
template <class Fun>
void addJob(Fun&& f); //f must return a std::optional<T> containing a value if successful
@@ -161,12 +161,15 @@ public:
ThreadGroup& operator=(ThreadGroup&& tmp) noexcept { swap(tmp); return *this; } //noexcept *required* to support move for reallocations in std::vector and std::swap!!!
//context of controlling OR worker thread, non-blocking:
- void run(Function&& wi /*should throw ThreadInterruption when needed*/)
+ void run(Function&& wi /*should throw ThreadInterruption when needed*/, bool insertFront = false)
{
{
std::lock_guard<std::mutex> dummy(workLoad_->lock);
- workLoad_->tasks.push_back(std::move(wi));
+ if (insertFront)
+ workLoad_->tasks.push_front(std::move(wi));
+ else
+ workLoad_->tasks.push_back(std::move(wi));
const size_t tasksPending = ++(workLoad_->tasksPending);
if (worker_.size() < std::min(tasksPending, threadCountMax_))
@@ -318,7 +321,7 @@ bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration&
template <class T>
-class GetFirstResult<T>::AsyncResult
+class AsyncFirstResult<T>::AsyncResult
{
public:
//context: worker threads
@@ -361,12 +364,12 @@ private:
template <class T> inline
-GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {}
+AsyncFirstResult<T>::AsyncFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {}
template <class T>
template <class Fun> inline
-void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> containing a value on success
+void AsyncFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> containing a value on success
{
std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); });
++jobsTotal_;
@@ -376,11 +379,11 @@ void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> conta
template <class T>
template <class Duration> inline
-bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
+bool AsyncFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); }
template <class T> inline
-std::optional<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); }
+std::optional<T> AsyncFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); }
//------------------------------------------------------------------------------------------
diff --git a/zen/zstring.cpp b/zen/zstring.cpp
index 68609030..f8a34045 100755
--- a/zen/zstring.cpp
+++ b/zen/zstring.cpp
@@ -77,22 +77,19 @@ Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const
{
if (oldTerm.empty())
return str;
-
- Zstring strU = str;
- Zstring oldU = oldTerm;
-
- for (Zchar& c : strU) c = asciiToUpper(c); //can't use makeUpperCopy(): input/output sizes may differ!
- for (Zchar& c : oldU) c = asciiToUpper(c); //
Zstring output;
for (size_t pos = 0;;)
{
- const size_t posFound = strU.find(oldU, pos);
- if (posFound == Zstring::npos)
+ const size_t posFound = std::search(str.begin() + pos, str.end(), //can't use makeUpperCopy(): input/output sizes may differ!
+ oldTerm.begin(), oldTerm.end(),
+ [](Zchar charL, Zchar charR) { return asciiToUpper(charL) == asciiToUpper(charR); }) - str.begin();
+
+ if (posFound == str.size())
{
if (pos == 0) //optimize "oldTerm not found": return ref-counted copy
- return str;
+ return str;
output.append(str.begin() + pos, str.end());
return output;
}
@@ -126,7 +123,7 @@ OS X (UTF8 char)
________________________
time per call | function
*/
-int compareLocalPath(const Zstring& lhs, const Zstring& rhs)
+int compareNativePath(const Zstring& lhs, const Zstring& rhs)
{
assert(lhs.find(Zchar('\0')) == Zstring::npos); //don't expect embedded nulls!
assert(rhs.find(Zchar('\0')) == Zstring::npos); //
@@ -250,8 +247,3 @@ int compareNatural(const Zstring& lhs, const Zstring& rhs)
}
}
-
-
-warn_static("clean up implementation of these two:")
-//template <> inline bool isWhiteSpace(char c)
-//template <> inline bool isWhiteSpace(wchar_t c)
diff --git a/zen/zstring.h b/zen/zstring.h
index 20cf968d..9fecdce3 100755
--- a/zen/zstring.h
+++ b/zen/zstring.h
@@ -35,24 +35,18 @@ Zstring getUnicodeNormalForm(const Zstring& str);
Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const Zstring& newTerm);
+struct LessUnicodeNormal { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return getUnicodeNormalForm(lhs) < getUnicodeNormalForm(rhs);} };
+
//------------------------------------------------------------------------------------------
-//inline
-//int compareNoCase(const Zstring& lhs, const Zstring& rhs)
-//{
-// return zen::compareString(makeUpperCopy(lhs), makeUpperCopy(rhs));
-// //avoid eager optimization bugs: e.g. "if (isAsciiString()) compareAsciiNoCase()" might model a different order!
-//}
inline bool equalNoCase(const Zstring& lhs, const Zstring& rhs) { return makeUpperCopy(lhs) == makeUpperCopy(rhs); }
struct ZstringNoCase //use as STL container key: avoid needless upper-case conversions during std::map<>::find()
{
- ZstringNoCase(const Zstring& str) : upperCase(makeUpperCopy(str)) {}
- Zstring upperCase;
+ ZstringNoCase(const Zstring& str) : upperCase(makeUpperCopy(str)) {}
+ Zstring upperCase;
};
-inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { return lhs.upperCase < rhs.upperCase; }
-
-//struct LessNoCase { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareNoCase(lhs, rhs) < 0; } };
+inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { return lhs.upperCase < rhs.upperCase; }
//------------------------------------------------------------------------------------------
@@ -60,11 +54,11 @@ inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { retu
// Windows: igore case
// Linux: byte-wise comparison
// macOS: igore case + Unicode normalization forms
-int compareLocalPath(const Zstring& lhs, const Zstring& rhs);
+int compareNativePath(const Zstring& lhs, const Zstring& rhs);
-inline bool equalLocalPath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs) == 0; }
+inline bool equalNativePath(const Zstring& lhs, const Zstring& rhs) { return compareNativePath(lhs, rhs) == 0; }
-struct LessLocalPath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareLocalPath(lhs, rhs) < 0; } };
+struct LessNativePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareNativePath(lhs, rhs) < 0; } };
//------------------------------------------------------------------------------------------
int compareNatural(const Zstring& lhs, const Zstring& rhs);
@@ -73,11 +67,7 @@ struct LessNaturalSort { bool operator()(const Zstring& lhs, const Zstring rhs)
//------------------------------------------------------------------------------------------
warn_static("get rid:")
-inline int compareFilePath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs); }
-
-inline bool equalFilePath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs) == 0; }
-
-struct LessFilePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareLocalPath(lhs, rhs) < 0; } };
+inline bool equalFilePath(const Zstring& lhs, const Zstring& rhs) { return compareNativePath(lhs, rhs) == 0; }
//------------------------------------------------------------------------------------------
@@ -92,18 +82,50 @@ Zstring appendSeparator(Zstring path) //support rvalue references!
inline
+Zstring appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep)
+{
+ using namespace zen;
+
+ assert(!startsWith(relPath, pathSep) && !endsWith(relPath, pathSep));
+ if (relPath.empty())
+ return basePath;
+ if (basePath.empty())
+ return relPath;
+
+ if (startsWith(relPath, pathSep))
+ {
+ if (relPath.size() == 1)
+ return basePath;
+
+ if (endsWith(basePath, pathSep))
+ return basePath + (relPath.c_str() + 1);
+ }
+ else if (!endsWith(basePath, pathSep))
+ {
+ Zstring output = basePath;
+ output.reserve(basePath.size() + 1 + relPath.size()); //append all three strings using a single memory allocation
+ return std::move(output) + pathSep + relPath; //
+ }
+
+ return basePath + relPath;
+}
+
+inline Zstring nativeAppendPaths(const Zstring& basePath, const Zstring& relPath) { return appendPaths(basePath, relPath, FILE_NAME_SEPARATOR); }
+
+
+inline
Zstring getFileExtension(const Zstring& filePath)
{
//const Zstring fileName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL);
//return afterLast(fileName, Zstr('.'), zen::IF_MISSING_RETURN_NONE);
- auto it = zen::find_last(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR);
+ auto it = zen::findLast(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR);
if (it == filePath.end())
it = filePath.begin();
else
++it;
- auto it2 = zen::find_last(it, filePath.end(), Zstr('.'));
+ auto it2 = zen::findLast(it, filePath.end(), Zstr('.'));
if (it2 != filePath.end())
++it2;
diff --git a/zenXml/zenxml/parser.h b/zenXml/zenxml/parser.h
index a90d163a..70bf6654 100755
--- a/zenXml/zenxml/parser.h
+++ b/zenXml/zenxml/parser.h
@@ -316,7 +316,7 @@ public:
Token getNextToken() //throw XmlParsingError
{
//skip whitespace
- pos_ = std::find_if(pos_, stream_.end(), std::not_fn(isWhiteSpace<char>));
+ pos_ = std::find_if_not(pos_, stream_.end(), isWhiteSpace<char>);
if (pos_ == stream_.end())
return Token::TK_END;
diff --git a/zenXml/zenxml/xml.h b/zenXml/zenxml/xml.h
index 5eddc462..15d635bd 100755
--- a/zenXml/zenxml/xml.h
+++ b/zenXml/zenxml/xml.h
@@ -277,12 +277,12 @@ public:
{
bool success = readStruc(*refList_[refIndex_], value);
if (!success)
- log_->notifyConversionError(getNameFormatted());
+ log_.ref().notifyConversionError(getNameFormatted());
return success;
}
else
{
- log_->notifyMissingElement(getNameFormatted());
+ log_.ref().notifyMissingElement(getNameFormatted());
return false;
}
}
@@ -312,12 +312,12 @@ public:
{
bool success = refList_[refIndex_]->getAttribute(name, value);
if (!success)
- log_->notifyMissingAttribute(getNameFormatted(), utfTo<std::string>(name));
+ log_.ref().notifyMissingAttribute(getNameFormatted(), utfTo<std::string>(name));
return success;
}
else
{
- log_->notifyMissingElement(getNameFormatted());
+ log_.ref().notifyMissingElement(getNameFormatted());
return false;
}
}
@@ -357,7 +357,7 @@ public:
However be aware that the chain of connected proxy instances will be broken once you call XmlIn::get() to retrieve the underlying pointer.
Errors that occur when working with this pointer are not logged by the original set of related instances.
*/
- bool haveErrors() const { return !log_->elementList().empty(); }
+ bool haveErrors() const { return !log_.ref().elementList().empty(); }
///Get a list of XML element and attribute names which failed to convert to user data.
/**
@@ -368,13 +368,13 @@ public:
std::vector<String> getErrorsAs() const
{
std::vector<String> output;
- const auto& elements = log_->elementList();
+ const auto& elements = log_.ref().elementList();
std::transform(elements.begin(), elements.end(), std::back_inserter(output), [](const std::string& str) { return utfTo<String>(str); });
return output;
}
private:
- XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const std::shared_ptr<ErrorLog>& sharedlog) :
+ XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const SharedRef<ErrorLog>& sharedlog) :
refList_(siblingList), formattedName_(elementNameFmt), log_(sharedlog)
{ assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); }
@@ -423,7 +423,7 @@ private:
std::vector<const XmlElement*> refList_; //all sibling elements with same name (all pointers bound!)
size_t refIndex_ = 0; //this sibling's index in refList_
std::string formattedName_; //contains full and formatted element name if (and only if) refList_ is empty
- std::shared_ptr<ErrorLog> log_{ std::make_shared<ErrorLog>() }; //always bound
+ mutable SharedRef<ErrorLog> log_ = makeSharedRef<ErrorLog>();
};
bgstack15