From 076498028ff511afd88d93e7b0bf1d1a81093b3d Mon Sep 17 00:00:00 2001 From: B Stack Date: Tue, 13 Nov 2018 06:58:56 -0500 Subject: 10.6 --- Changelog.txt | 24 +- FreeFileSync/Build/Languages/arabic.lng | 184 ++++++----- FreeFileSync/Build/Languages/bulgarian.lng | 192 ++++++----- FreeFileSync/Build/Languages/chinese_simple.lng | 174 ++++++---- .../Build/Languages/chinese_traditional.lng | 196 ++++++----- FreeFileSync/Build/Languages/croatian.lng | 178 +++++----- FreeFileSync/Build/Languages/czech.lng | 178 +++++----- FreeFileSync/Build/Languages/danish.lng | 176 ++++++---- FreeFileSync/Build/Languages/dutch.lng | 184 ++++++----- FreeFileSync/Build/Languages/english_uk.lng | 186 ++++++----- FreeFileSync/Build/Languages/french.lng | 176 ++++++---- FreeFileSync/Build/Languages/german.lng | 102 +++--- FreeFileSync/Build/Languages/greek.lng | 180 +++++----- FreeFileSync/Build/Languages/hebrew.lng | 186 ++++++----- FreeFileSync/Build/Languages/hindi.lng | 178 +++++----- FreeFileSync/Build/Languages/hungarian.lng | 194 ++++++----- FreeFileSync/Build/Languages/italian.lng | 176 ++++++---- FreeFileSync/Build/Languages/japanese.lng | 178 +++++----- FreeFileSync/Build/Languages/korean.lng | 176 ++++++---- FreeFileSync/Build/Languages/lithuanian.lng | 178 +++++----- FreeFileSync/Build/Languages/norwegian.lng | 186 ++++++----- FreeFileSync/Build/Languages/polish.lng | 180 +++++----- FreeFileSync/Build/Languages/portuguese.lng | 176 ++++++---- FreeFileSync/Build/Languages/portuguese_br.lng | 182 +++++----- FreeFileSync/Build/Languages/romanian.lng | 178 +++++----- FreeFileSync/Build/Languages/russian.lng | 182 +++++----- FreeFileSync/Build/Languages/slovak.lng | 208 +++++++----- FreeFileSync/Build/Languages/slovenian.lng | 180 +++++----- FreeFileSync/Build/Languages/spanish.lng | 176 ++++++---- FreeFileSync/Build/Languages/swedish.lng | 176 ++++++---- FreeFileSync/Build/Languages/turkish.lng | 170 ++++++---- FreeFileSync/Build/Languages/ukrainian.lng | 178 +++++----- FreeFileSync/Build/styles.gtk_rc | 4 +- FreeFileSync/Source/Makefile | 16 +- FreeFileSync/Source/RealTimeSync/Makefile | 12 +- FreeFileSync/Source/RealTimeSync/application.cpp | 5 +- .../Source/RealTimeSync/folder_selector2.cpp | 3 +- FreeFileSync/Source/RealTimeSync/main_dlg.cpp | 18 +- FreeFileSync/Source/RealTimeSync/monitor.cpp | 16 +- FreeFileSync/Source/RealTimeSync/tray_menu.cpp | 2 +- FreeFileSync/Source/RealTimeSync/xml_proc.cpp | 10 +- FreeFileSync/Source/base/algorithm.cpp | 325 +++++++++--------- FreeFileSync/Source/base/algorithm.h | 12 +- FreeFileSync/Source/base/application.cpp | 83 ++--- FreeFileSync/Source/base/comparison.cpp | 193 +++++------ FreeFileSync/Source/base/comparison.h | 2 +- FreeFileSync/Source/base/db_file.cpp | 136 ++++---- FreeFileSync/Source/base/db_file.h | 8 +- FreeFileSync/Source/base/dir_exist_async.h | 74 ++++- FreeFileSync/Source/base/dir_lock.cpp | 27 +- FreeFileSync/Source/base/dir_lock.h | 2 +- FreeFileSync/Source/base/ffs_paths.cpp | 73 +++- FreeFileSync/Source/base/ffs_paths.h | 5 + FreeFileSync/Source/base/file_hierarchy.cpp | 8 +- FreeFileSync/Source/base/file_hierarchy.h | 34 +- FreeFileSync/Source/base/generate_logfile.cpp | 40 +-- FreeFileSync/Source/base/generate_logfile.h | 2 - FreeFileSync/Source/base/localization.cpp | 93 +++++- FreeFileSync/Source/base/lock_holder.h | 16 +- FreeFileSync/Source/base/norm_filter.h | 6 +- FreeFileSync/Source/base/parallel_scan.cpp | 31 +- FreeFileSync/Source/base/parallel_scan.h | 14 +- FreeFileSync/Source/base/parse_lng.h | 2 +- FreeFileSync/Source/base/parse_plural.h | 4 +- FreeFileSync/Source/base/path_filter.cpp | 366 +++++++++++++++++++++ FreeFileSync/Source/base/path_filter.h | 239 ++++++++++++++ FreeFileSync/Source/base/perf_check.cpp | 4 +- FreeFileSync/Source/base/process_xml.cpp | 8 +- FreeFileSync/Source/base/resolve_path.cpp | 18 +- FreeFileSync/Source/base/resolve_path.h | 2 +- FreeFileSync/Source/base/status_handler_impl.h | 28 +- FreeFileSync/Source/base/structures.cpp | 34 +- FreeFileSync/Source/base/structures.h | 13 +- FreeFileSync/Source/base/synchronization.cpp | 53 ++- FreeFileSync/Source/base/synchronization.h | 2 +- FreeFileSync/Source/base/versioning.cpp | 101 +++--- FreeFileSync/Source/base/versioning.h | 4 +- FreeFileSync/Source/fs/abstract.cpp | 181 +++++----- FreeFileSync/Source/fs/abstract.h | 203 +++++------- FreeFileSync/Source/fs/concrete_impl.h | 8 +- FreeFileSync/Source/fs/native.cpp | 55 ++-- FreeFileSync/Source/ui/batch_status_handler.cpp | 17 +- FreeFileSync/Source/ui/cfg_grid.cpp | 8 +- FreeFileSync/Source/ui/cfg_grid.h | 2 +- FreeFileSync/Source/ui/command_box.cpp | 4 +- FreeFileSync/Source/ui/file_view.cpp | 2 +- FreeFileSync/Source/ui/folder_history_box.cpp | 4 +- FreeFileSync/Source/ui/folder_history_box.h | 4 +- FreeFileSync/Source/ui/folder_selector.cpp | 2 +- FreeFileSync/Source/ui/gui_generated.cpp | 18 +- FreeFileSync/Source/ui/gui_status_handler.cpp | 27 +- FreeFileSync/Source/ui/log_panel.cpp | 2 +- FreeFileSync/Source/ui/main_dlg.cpp | 148 +++++---- FreeFileSync/Source/ui/main_dlg.h | 4 +- FreeFileSync/Source/ui/progress_indicator.cpp | 8 +- FreeFileSync/Source/ui/search_grid.cpp | 2 +- FreeFileSync/Source/ui/small_dlgs.cpp | 30 +- FreeFileSync/Source/ui/small_dlgs.h | 8 +- FreeFileSync/Source/ui/sync_cfg.cpp | 32 +- FreeFileSync/Source/ui/sync_cfg.h | 2 +- FreeFileSync/Source/ui/tray_icon.cpp | 2 +- FreeFileSync/Source/ui/tree_grid.cpp | 2 +- FreeFileSync/Source/version/version.h | 2 +- wx+/async_task.h | 4 +- wx+/graph.cpp | 18 +- wx+/grid.cpp | 20 +- wx+/grid.h | 4 +- wx+/image_holder.h | 2 +- wx+/image_resources.cpp | 2 +- zen/basic_math.h | 51 --- zen/dir_watcher.cpp | 10 +- zen/file_access.cpp | 142 ++++---- zen/file_access.h | 20 +- zen/file_io.cpp | 2 +- zen/legacy_compiler.h | 36 ++ zen/recycler.cpp | 2 +- zen/ring_buffer.h | 55 ++-- zen/serialize.h | 18 +- zen/stl_tools.h | 63 +++- zen/string_base.h | 4 +- zen/string_tools.h | 29 +- zen/string_traits.h | 2 +- zen/thread.h | 21 +- zen/zstring.cpp | 22 +- zen/zstring.h | 64 ++-- zenXml/zenxml/parser.h | 2 +- zenXml/zenxml/xml.h | 16 +- 127 files changed, 5361 insertions(+), 3741 deletions(-) create mode 100755 FreeFileSync/Source/base/path_filter.cpp create mode 100755 FreeFileSync/Source/base/path_filter.h 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 @@ Items differ in attributes only العناصر مختلفة في السمات فقط +The name %x is used by more than one item in the folder. +الاسم %x تم استخدامه اكثر من مرة فى نفس المجلد. + Resolving symbolic link %x جاري حل المسار الرمزي %x @@ -191,9 +194,6 @@ File time tolerance التفاوت في وقت الملف -Folder access timeout -مهلة وصول المجلد - Run with background priority تشغيل مع أولوية في الخلفية @@ -342,8 +342,11 @@ Update attributes on right تحديث السمات على اليسار -Warning -تحذير +Errors: +أخطاء: + +Warnings: +تحذيرات: Items processed: معالجة العناصر: @@ -354,6 +357,9 @@ Total time: مجموع الوقت: +Warning +تحذير + Stopped توقف @@ -363,6 +369,21 @@ Error parsing file %x, row %y, column %z. حدث خطأ أثناء تحليل الملف %x، الصف %y، و العمود %z. +Services +خدمات + +Show All +اظهر الكل + +Hide Others +اخفاء الاخرين + +Hide %x +اخفاء %x + +Quit %x +الغاء %x + Cannot set directory locks for the following folders: لا يمكن وضع قفل المسار للمجلدات الأتية: @@ -382,11 +403,11 @@ Cannot read directory %x. لا يمكن قراءة الدليل %x. -/sec -\ثانية +%x/sec +%x ثانية -%x items/sec -%x عنصر\الثانية +%x items +%x عناصر Show in Explorer إظهار في المستكشف @@ -535,11 +556,11 @@ Generating database... إنشاء قاعدة بيانات... -Searching for excess file versions: -البحث عن اصدارات الملف الزائدة: +Searching for old file versions: +البحث عن اصدارات قديمة للملف: -Removing excess file versions: -إزالة اصدارات الملف الزائدة: +Removing old file versions: +ازالة اصدارات قديمة للملف: Unable to create time stamp for versioning: تعذر إنشاء بصمة زمنية من أجل المفاضلة الزمنية: @@ -597,6 +618,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. تعذر نقل %x إلى سلة المحذوفات. +Unable to access %x. +لا يمكن الوصول إلى %x. + +Authentication completed. +نجحت المصادقة. + +Authentication failed. +فشلت المصادقة. + +You may close this page now and continue with FreeFileSync. +يمكنك غلق هذه الصفحة والاستمرار باستخدام FreeFileSync. + +The server returned an error: +الخادم ارسل الخطأ: + +Cannot determine free disk space for %x. +لا يمكن تحديد مساحة القرص الحرة لـ %x. + Cannot find %x. لا يمكن العثور على %x. @@ -609,18 +648,12 @@ Actual: %y bytes Cannot delete symbolic link %x. لا يمكن حذف الرابط الرمزي %x. -Cannot determine free disk space for %x. -لا يمكن تحديد مساحة القرص الحرة لـ %x. - Incorrect command line: سطر أوامر خاطئ: The server does not support authentication via %x. لا يدعم الخادم المصادقة عبر %x. -Unable to access %x. -لا يمكن الوصول إلى %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -653,28 +686,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. فشل فتح قناة SFTP رقم %x. - -1 byte -%x bytes - - -‎0 byte -‎1 byte -‎2 bytes -‎%x bytes -‎%x bytes -‎%x bytes - - -%x MB -‎%x MB - -%x KB -‎%x KB - -%x GB -‎%x GB - Drag && drop سحب و إفلات @@ -788,6 +799,28 @@ The command is triggered if: &Retry إ&عادة المحاولة + +1 byte +%x bytes + + +‎0 byte +‎1 byte +‎2 bytes +‎%x bytes +‎%x bytes +‎%x bytes + + +%x MB +‎%x MB + +%x KB +‎%x KB + +%x GB +‎%x GB + Loading... تحميل... @@ -937,7 +970,7 @@ The command is triggered if: &حفظ كمهمة دفعية... Show &log - +أظهر &السجل Start &comparison بدأ الم&قارنة @@ -999,9 +1032,6 @@ The command is triggered if: Access online storage الوصول إلى التخزين عبر الإنترنت -Swap sides -مبادلة الجانبين - Close search bar إغلاق شريط البحث @@ -1026,6 +1056,9 @@ The command is triggered if: View type: عرض النوع: +Save as default +حفظ كافتراضي + Select view: اختيار نمط العرض: @@ -1083,6 +1116,15 @@ The command is triggered if: Handle daylight saving time تعامل مع التوقيت الصيفي +Ignore errors +تجاهل الأخطاء + +Retry count: +تعداد محاولات الإعادة: + +Delay (in seconds): +التأخير (بالثواني): + Performance improvements: تحسينات الأداء: @@ -1157,17 +1199,11 @@ The command is triggered if: Last x days: اخر x أيام: -Ignore errors -تجاهل الأخطاء +&Override default log path: +&تجاوز المسار الافتراضى للسجل: -Retry count: -تعداد محاولات الإعادة: - -Delay (in seconds): -التأخير (بالثواني): - -Run a command after synchronization: -تشغيل أمر بعد المزامنة: +Run a command: +تشغيل الأمر: OK موافق @@ -1217,6 +1253,9 @@ The command is triggered if: Directory on server: المسار على الخادم: +Access timeout (in seconds): +أقصى زمن للوصول(ثانية): + SFTP channels per connection: قنوات SFTP لكل اتصال: @@ -1295,12 +1334,6 @@ The command is triggered if: Stop synchronization at first error إحباط المزامنة عند أول خطأ -Save log: -حفظ السجل: - -Limit number of log files: -حد عدد ملفات السجل: - How can I schedule a batch job? كيف يمكنني جدولة مهمة دفعية؟ @@ -1337,8 +1370,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again إعادة إظهار جميع التنبهات و نوافذ الحوار التي تم إخفاؤها -Remove old log files after x days: - +Default log path: +المسار الافتراضى للسجل: + +&Delete logs after x days: +&ازالة السجلات بعد x يوم: Customize context menu: تخصيص القائمة المحلية: @@ -1349,8 +1385,8 @@ This guarantees a consistent state even in case of a serious error. &Default الا&فتراضي -Feedback and suggestions are welcome -التعليقات و الاقتراحات موضع ترحيب +Feedback and suggestions are welcome: +ردود الفعل والاقتراحات مرحب بها: Home page الصفحة الرئيسية @@ -1376,8 +1412,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: الرماز المصدري مكتوب بلغة C++‎ باستخدام: -Published under the GNU General Public License -نشر تحت رخصة GNU General Public License +Published under the GNU General Public License: +نشر باستخدام رخصة جنو العمومية العامة: Many thanks for localization: شكرا جزيلا للترجمة: @@ -1434,7 +1470,7 @@ This guarantees a consistent state even in case of a serious error. معلومات No log entries - +لا يوجد سجلات Select all اختيار الجميع @@ -1460,6 +1496,9 @@ This guarantees a consistent state even in case of a serious error. Overview نظرة عامة +Swap sides +مبادلة الجانبين + Show "%x" إظهار "%x" @@ -1650,9 +1689,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files إظهار الملفات التي تم فلترتها أو استبعادها بشكل مؤقت -Save as default -حفظ كافتراضي - Filter عامل الفلترة @@ -2018,12 +2054,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. فشل تصفح سلة المهملات من أجل الملف %x. -The following XML elements could not be read: -عناصر XML التالية لا يمكن قراءتها: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -ملف التكوين %x غير مكتمل. سيتم إعادة تعيين العناصر المفقودة إلى قيمها الافتراضية. - Prepare installation الاستعداد للتثبيت 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 @@ Размер: Content comparison was skipped for excluded files. -Сравняването на съдържанието е прескочено за изключените файлове. +Сравняването на съдържание се прескача за изключените файлове. Items differ in attributes only Елементите се различават само по атрибути +The name %x is used by more than one item in the folder. +Името %x се използва от повече от един елемент в папката. + Resolving symbolic link %x Проследява символна връзка %x @@ -187,9 +190,6 @@ File time tolerance Толеранс на файловото време -Folder access timeout -Време за достъп до папката - Run with background priority Изпълнявай с фонов приоритет @@ -334,8 +334,11 @@ Update attributes on right Актуализирай атрибутите на десния елемент -Warning -Предупреждение +Errors: +Грешки: + +Warnings: +Предупреждения: Items processed: Обработени елементи: @@ -346,6 +349,9 @@ Total time: Общо време: +Warning +Предупреждение + Stopped Спряно @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Грешка при анализ на файл %x, ред %y, колона %z. +Services +Услуги + +Show All +Покажи всички + +Hide Others +Скрий другите + +Hide %x +Скрий %x + +Quit %x +Край на %x + Cannot set directory locks for the following folders: Не може да заключи директориите за следните папки: @@ -370,17 +391,17 @@ Cannot read directory %x. Не може да прочете директория %x. -/sec -/сек. +%x/sec +%x/сек. -%x items/sec -%x елем./сек. +%x items +%x елемента Show in Explorer Покажи в Експлорера Open with default application -Отвори с приложение по подразбиране +Отвори с подразбираното приложение Browse directory Преглед на директорията @@ -523,11 +544,11 @@ Generating database... Създава база данни... -Searching for excess file versions: -Търси излишни файлови версии: +Searching for old file versions: +Търси стари версии на файлове: -Removing excess file versions: -Премахва излишни файлови версии: +Removing old file versions: +Премахва стари версии на файлове: Unable to create time stamp for versioning: Не може да маркира времето на версиите: @@ -585,6 +606,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. Не може да премести %x в кошчето. +Unable to access %x. +Няма достъп до %x. + +Authentication completed. +Аутентификацията приключи. + +Authentication failed. +Аутентификацията неуспешна. + +You may close this page now and continue with FreeFileSync. +Може да затворите тази страница и да продължите с FreeFileSync. + +The server returned an error: +Сървърът връща грешка: + +Cannot determine free disk space for %x. +Не може да определи свободното дисково пространство за %x. + Cannot find %x. Не е намерен %x. @@ -597,18 +636,12 @@ Actual: %y bytes Cannot delete symbolic link %x. Не може да изтрие символната връзка %x. -Cannot determine free disk space for %x. -Не може да определи свободното дисково пространство за %x. - Incorrect command line: Невалиден команден ред: The server does not support authentication via %x. Сървърът не поддържа идентификация чрез %x. -Unable to access %x. -Няма достъп до %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. Не може да отвори SFTP-канал номер %x. - -1 byte -%x bytes - - -1 байт -%x байта - - -%x MB -%x МБ - -%x KB -%x КБ - -%x GB -%x ГБ - Drag && drop Влачете и пуснете @@ -764,6 +779,24 @@ The command is triggered if: &Retry &Повтори + +1 byte +%x bytes + + +1 байт +%x байта + + +%x MB +%x МБ + +%x KB +%x КБ + +%x GB +%x ГБ + Loading... Зареждане... @@ -909,7 +942,7 @@ The command is triggered if: Запази &като пакетна задача... Show &log - +Покажи &log Start &comparison Почни &сравняване @@ -921,10 +954,10 @@ The command is triggered if: Настройки на &филтъра S&ynchronization settings -Настройки на с&инхронизиране +Настройки на с&инхронизация Start &synchronization -Почни &синхронизиране +Почни &синхронизация &Actions &Действия @@ -951,7 +984,7 @@ The command is triggered if: &Провери за обновления сега Check &automatically once a week -Провери автоматично &ежеседмично +Проверявай автоматично &ежеседмично Cancel Отказ @@ -969,10 +1002,7 @@ The command is triggered if: Отнеми двойка папки Access online storage -Достъп до онлайн-запазване - -Swap sides -Размени страните +Достъп до онлайн-съхранение Close search bar Затвори полето за търсене @@ -998,8 +1028,11 @@ The command is triggered if: View type: Тип изглед: +Save as default +Запази като подразбирано + Select view: -Избери изглед: +Изберете изглед: Statistics: Статистика: @@ -1055,6 +1088,15 @@ The command is triggered if: Handle daylight saving time Отчети лятното време +Ignore errors +Игнорирай грешките + +Retry count: +Брой повторения: + +Delay (in seconds): +Задръжка (сек.): + Performance improvements: Подобряване на производителността: @@ -1129,17 +1171,11 @@ The command is triggered if: Last x days: Последните x дни: -Ignore errors -Игнорирай грешките - -Retry count: -Брой повторения: - -Delay (in seconds): -Задръжка (сек.): +&Override default log path: +&Замени подразбирания log-път: -Run a command after synchronization: -След синхронизация изпълни команда: +Run a command: +Изпълни команда: OK ОК @@ -1189,6 +1225,9 @@ The command is triggered if: Directory on server: Директория на сървъра: +Access timeout (in seconds): +Време за достъп (в секунди): + SFTP channels per connection: SFTP-канали на връзка: @@ -1267,12 +1306,6 @@ The command is triggered if: Stop synchronization at first error Спри синхронизацията при първа грешка -Save log: -Запази протокол: - -Limit number of log files: -Ограничи броя протоколни файлове: - How can I schedule a batch job? Как да планирам пакетна задача? @@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Покажи всички постоянно скрити диалози и предупреждения отново -Remove old log files after x days: - +Default log path: +Подразбиран log-път: + +&Delete logs after x days: +&Изтрий протоколите след x дни: Customize context menu: Настрой контекстното меню: @@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. &Default &По подразбиране -Feedback and suggestions are welcome -Забележки и предложения са добре дошли +Feedback and suggestions are welcome: +Отзиви и предложения са добре дошли: Home page Домашна страница @@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: Изходния код е написан на C++ със: -Published under the GNU General Public License -Публикува се по лиценза GNU General Public License +Published under the GNU General Public License: +Публикуван под GNU General Public License: Many thanks for localization: Благодарност за локализацията: @@ -1358,7 +1394,7 @@ This guarantees a consistent state even in case of a serious error. Активирайте дарителско издание на FreeFileSync по един от следните методи: 1. Activate via internet now: -1. Активиране по Интернет: +1. Активирайте по Интернет сега: Activate online Активирайте онлайн @@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error. Информация No log entries - +Няма log-записи Select all Маркирай всичко @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. Overview Преглед +Swap sides +Размени страните + Show "%x" Покажи "%x" @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files Покажи филтрираните или временно изключени файлове -Save as default -Запази като подразбирано - Filter Филтър @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. Неуспешна проверка на кошчето за папка %x. -The following XML elements could not be read: -Не могат да се прочетат следните XML-елементи: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Конфигурационният файл %x е непълен. Ще бъдат зададени подразбирани стойности за липсващите елементи. - Prepare installation Подготовка за инсталиране 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 @@ Items differ in attributes only 项目仅是文件属性不同 +The name %x is used by more than one item in the folder. +%x 此名称在文件夹中被一个以上的项目所使用. + Resolving symbolic link %x 正在解决符号连接 %x @@ -186,9 +189,6 @@ File time tolerance 文件时间容差 -Folder access timeout -文件夹访问超时 - Run with background priority 以后台优先级运行 @@ -332,8 +332,11 @@ Update attributes on right 更新右侧的文件属性 -Warning -警告 +Errors: +错误: + +Warnings: +警告: Items processed: 已处理的项目: @@ -344,6 +347,9 @@ Total time: 总共时间: +Warning +警告 + Stopped 已停止 @@ -353,6 +359,21 @@ Error parsing file %x, row %y, column %z. 当分析文件 %x , 行 %y, 列 %z 时出错. +Services +服务 + +Show All +显示所有 + +Hide Others +隐藏其他 + +Hide %x +隐藏 %x + +Quit %x +退出 %x + Cannot set directory locks for the following folders: 无法为如下文件夹设置 目录锁定: @@ -367,11 +388,11 @@ Cannot read directory %x. 无法读取目录 %x. -/sec -/秒 +%x/sec +%x/秒 -%x items/sec -%x 个项目/秒 +%x items +%x 个项目 Show in Explorer 在Explorer中显示 @@ -520,11 +541,11 @@ Generating database... 正在生成数据库... -Searching for excess file versions: -正在搜索多余的文件版本: +Searching for old file versions: +正在搜索旧的文件版本: -Removing excess file versions: -正在删除多余的文件版本: +Removing old file versions: +正在移除旧的文件版本: Unable to create time stamp for versioning: 无法为历史版本创建时间戳: @@ -582,6 +603,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. 无法将 %x 移动到回收站. +Unable to access %x. +无法访问 %x. + +Authentication completed. +验证完成. + +Authentication failed. +验证失败. + +You may close this page now and continue with FreeFileSync. +你可以现在关闭本页面并继续使用 FreeFileSync. + +The server returned an error: +服务器返回一个错误: + +Cannot determine free disk space for %x. +无法确定 %x 上的可用磁盘空间. + Cannot find %x. 无法找到 %x. @@ -594,18 +633,12 @@ Actual: %y bytes Cannot delete symbolic link %x. 无法删除符号链接 %x. -Cannot determine free disk space for %x. -无法确定 %x 上的可用磁盘空间. - Incorrect command line: 不正确的命令行: The server does not support authentication via %x. 此服务器并不支持通过 %x 进行认证. -Unable to access %x. -无法访问 %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -628,23 +661,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. 打开SFTP通道号 %x 时失败. - -1 byte -%x bytes - - -%x 字节 - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop 拖放 @@ -758,6 +774,23 @@ The command is triggered if: &Retry 重试(&R) + +1 byte +%x bytes + + +%x 字节 + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... 正在载入... @@ -902,7 +935,7 @@ The command is triggered if: 另存为批处理作业(&B)... Show &log - +显示日志(&L) Start &comparison 开始比较(&C) @@ -964,9 +997,6 @@ The command is triggered if: Access online storage 访问在线存储空间 -Swap sides -两侧互换 - Close search bar 关闭搜索条 @@ -991,6 +1021,9 @@ The command is triggered if: View type: 视图类型: +Save as default +保存为默认值 + Select view: 选择视图: @@ -1048,6 +1081,15 @@ The command is triggered if: Handle daylight saving time 处理夏令时 +Ignore errors +忽略错误 + +Retry count: +重试计数: + +Delay (in seconds): +延时 (秒): + Performance improvements: 性能改进: @@ -1122,17 +1164,11 @@ The command is triggered if: Last x days: 最近 x 天: -Ignore errors -忽略错误 - -Retry count: -重试计数: - -Delay (in seconds): -延时 (秒): +&Override default log path: +覆盖默认日志路径(&O): -Run a command after synchronization: -同步之后运行一个命令: +Run a command: +执行一个命令: OK 确定 @@ -1182,6 +1218,9 @@ The command is triggered if: Directory on server: 服务器上的目录: +Access timeout (in seconds): +访问超时(秒): + SFTP channels per connection: 每个连接的SFTP通道数: @@ -1260,12 +1299,6 @@ The command is triggered if: Stop synchronization at first error 在遇到第一个错误时停止同步 -Save log: -保存日志: - -Limit number of log files: -限制日志文件数量: - How can I schedule a batch job? 我如何计划一个批处理作业? @@ -1299,8 +1332,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again 重新显示所有被永久性隐藏的 对话框和警告信息 -Remove old log files after x days: - +Default log path: +默认日志路径: + +&Delete logs after x days: +在 x 天后删除日志(&D): Customize context menu: 自定义右键菜单: @@ -1311,8 +1347,8 @@ This guarantees a consistent state even in case of a serious error. &Default 默认(&D) -Feedback and suggestions are welcome -欢迎反馈意见和提出建议 +Feedback and suggestions are welcome: +欢迎提出反馈意见和建议: Home page 主页 @@ -1338,8 +1374,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: 源代码使用如下工具由C++写成: -Published under the GNU General Public License -在GNU通用公共许可下发布 +Published under the GNU General Public License: +根据GNU通用公共许可证发布: Many thanks for localization: 非常感谢以下本地化翻译者: @@ -1396,7 +1432,7 @@ This guarantees a consistent state even in case of a serious error. 信息 No log entries - +没有日志条目 Select all 选择全部 @@ -1422,6 +1458,9 @@ This guarantees a consistent state even in case of a serious error. Overview 摘要 +Swap sides +两侧互换 + Show "%x" 显示 "%x" @@ -1592,9 +1631,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files 显示已被过滤或被临时排除的文件 -Save as default -保存为默认值 - Filter 过滤器 @@ -1935,12 +1971,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. 检查文件夹 %x 的回收站失败. -The following XML elements could not be read: -下列的XML元素无法被读取: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -配置文件 %x 不完整. 丢失的元素将被设置为它们的默认值. - Prepare installation 准备安装 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 @@ Items differ in attributes only 只有項目的屬性不同 +The name %x is used by more than one item in the folder. +名稱 %x 已被資料夾中的多個項目使用。 + Resolving symbolic link %x 正在解析符號連結 %x @@ -186,9 +189,6 @@ File time tolerance 檔案時間容差範圍 -Folder access timeout -資料夾存取超時 - Run with background priority 背景優先執行 @@ -226,7 +226,7 @@ 資料庫檔案 %x 不相容。 Initial synchronization: -初始化同步: +初始同步: Database file %x does not yet exist. 資料庫檔案 %x 不存在。 @@ -253,7 +253,7 @@ 正在搜尋資料夾 %x… Timeout while searching for folder %x. -搜尋資料夾 %x 時超時。 +搜尋資料夾 %x 時逾時。 Cannot get process information. 無法取得處理訊息。 @@ -332,8 +332,11 @@ Update attributes on right 更新右邊的屬性 -Warning -警告 +Errors: +錯誤: + +Warnings: +警告: Items processed: 已處理項目: @@ -344,15 +347,33 @@ Total time: 全部時間: +Warning +警告 + Stopped 已停止 Cleaning up log files: -清除日誌檔: +清除紀錄檔: Error parsing file %x, row %y, column %z. 解析 %x 檔案,第 %y 列,第 %z 行出現錯誤。 +Services +服務 + +Show All +顯示全部 + +Hide Others +隱藏其他 + +Hide %x +隱藏 %x + +Quit %x +結束 %x + Cannot set directory locks for the following folders: 無法為以下資料夾設定目錄鎖定: @@ -361,17 +382,17 @@ %x threads -%x 執行緒 +%x 個執行緒 Cannot read directory %x. 無法讀取目錄 %x。 -/sec -(秒) +%x/sec +%x/秒 -%x items/sec -%x 個項目(秒) +%x items +%x 項目 Show in Explorer 在資源管理器中顯示 @@ -520,11 +541,11 @@ Generating database... 正在產生資料庫… -Searching for excess file versions: -正在搜尋多餘的檔案版本: +Searching for old file versions: +正在搜尋舊檔版本: -Removing excess file versions: -正在刪除多餘的檔案版本: +Removing old file versions: +正在移除舊檔版本: Unable to create time stamp for versioning: 無法建立時間戳記的版本控制: @@ -582,6 +603,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. 無法將 %x 移動到資源回收筒。 +Unable to access %x. +無法存取 %x。 + +Authentication completed. +認證完成。 + +Authentication failed. +認證失敗。 + +You may close this page now and continue with FreeFileSync. +您可以立即關閉此頁面並繼續使用FreeFileSync。 + +The server returned an error: +伺服器傳回錯誤: + +Cannot determine free disk space for %x. +無法確定 %x 的可用磁碟空間。 + Cannot find %x. 找不到 %x。 @@ -594,24 +633,18 @@ Actual: %y bytes Cannot delete symbolic link %x. 無法刪除符號連結 %x。 -Cannot determine free disk space for %x. -無法確定 %x 的可用磁碟空間。 - Incorrect command line: 不正確的命令列: The server does not support authentication via %x. -伺服器不支援透過 %x 進行身份驗證。 - -Unable to access %x. -無法存取 %x。 +伺服器不支援透過 %x 進行認證。 Operation timed out after 1 second. Operation timed out after %x seconds. -在 %x 秒後操作超時。 +在 %x 秒後操作逾時。 @@ -628,23 +661,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. 無法開啟SFTP通道號 %x。 - -1 byte -%x bytes - - -%x 位元組 - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop 拖放 @@ -758,6 +774,23 @@ The command is triggered if: &Retry 重試(&R) + +1 byte +%x bytes + + +%x 位元組 + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... 正在載入… @@ -824,7 +857,7 @@ The command is triggered if: 上次同步 Log -日誌 +紀錄 Folder 資料夾 @@ -902,7 +935,7 @@ The command is triggered if: 另存為批次工作(&B)… Show &log - +顯示紀錄(&L) Start &comparison 開始比對(&C) @@ -964,9 +997,6 @@ The command is triggered if: Access online storage 存取線上儲存空間 -Swap sides -兩邊交換 - Close search bar 關閉搜尋欄位 @@ -991,6 +1021,9 @@ The command is triggered if: View type: 檢視類型: +Save as default +儲存為預設值 + Select view: 選擇視圖: @@ -1028,7 +1061,7 @@ The command is triggered if: 包含符號連結(&S): &Follow -跟隨(&F) +追隨(&F) &Direct 直接(&D) @@ -1048,6 +1081,15 @@ The command is triggered if: Handle daylight saving time 處理日光節約時間 +Ignore errors +忽略錯誤 + +Retry count: +重試次數: + +Delay (in seconds): +延遲(以秒為單位): + Performance improvements: 效能改善: @@ -1122,17 +1164,11 @@ The command is triggered if: Last x days: 最近 x 天: -Ignore errors -忽略錯誤 - -Retry count: -重試次數: - -Delay (in seconds): -延遲(以秒為單位): +&Override default log path: +覆蓋預設紀錄路徑(&O): -Run a command after synchronization: -同步後執行一個命令: +Run a command: +執行命令: OK 確定 @@ -1159,7 +1195,7 @@ The command is triggered if: 外顯式SSL/TLS(&E) Authentication: -身份驗證: +認證: &Password 密碼(&P): @@ -1182,6 +1218,9 @@ The command is triggered if: Directory on server: 在伺服器上的目錄: +Access timeout (in seconds): +存取逾時(以秒為單位): + SFTP channels per connection: 每個連接SFTP通道數: @@ -1260,12 +1299,6 @@ The command is triggered if: Stop synchronization at first error 在第一個錯誤時停止同步 -Save log: -儲存日誌: - -Limit number of log files: -限制日誌檔數量: - How can I schedule a batch job? 如何排程批次工作? @@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again 再次顯示所有永久 隱藏的對話框和警告訊息 -Remove old log files after x days: - +Default log path: +預設紀錄路徑: + +&Delete logs after x days: +在 x 天後刪除紀錄(&D): Customize context menu: 自訂內容功能表: @@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error. &Default 預設(&D) -Feedback and suggestions are welcome -歡迎反映意見和建議 +Feedback and suggestions are welcome: +歡迎回饋和建議: Home page 主頁 @@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: 使用C++編寫的原始碼: -Published under the GNU General Public License -在GNU通用公共許可證下發佈 +Published under the GNU General Public License: +根據GNU通用公共授權條款發佈: Many thanks for localization: 非常感謝在地化翻譯人員: @@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error. 訊息 No log entries - +沒有紀錄 Select all 全選 @@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error. Overview 摘要 +Swap sides +兩邊交換 + Show "%x" 顯示「%x」 @@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files 顯示已篩選或暫時排除的檔案 -Save as default -儲存為預設值 - Filter 篩選器 @@ -1644,7 +1680,7 @@ This guarantees a consistent state even in case of a serious error. 進度 Thank you, %x, for your donation and support! -%x, 感謝您的贊助與支持! +%x,感謝您的贊助與支持! Connections 連線數 @@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. 檢查資源回收筒的資料夾 %x 失敗。 -The following XML elements could not be read: -無法讀取下列XML元件: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -配置檔案 %x 不完整。缺少的元件將設定為其本身預設值。 - Prepare installation 準備安裝 @@ -2014,7 +2044,7 @@ This guarantees a consistent state even in case of a serious error. 使用FreeFileSync進行編輯 Instead of an ad, here's an animal. -這是一隻動物,而不是廣告。 +這是一隻動物,不是廣告。 The FreeFileSync portable version cannot install into a subfolder of %x. FreeFileSync可攜式版本無法安裝到 %x 的子資料夾。 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 @@ Items differ in attributes only Stavke se razlikuju samo u atributima +The name %x is used by more than one item in the folder. +Naziv %x se koristi za više od jedne stavke u mapi. + Resolving symbolic link %x Rješavam simboličku vezu %x @@ -188,9 +191,6 @@ File time tolerance Tolerancija vremena datoteke -Folder access timeout -Istek vremena pristupa mapi - Run with background priority Pokreni s pozadinskim prioritetom @@ -336,8 +336,11 @@ Update attributes on right Osvježi atribute desno -Warning -Upozorenje +Errors: +Pogreške: + +Warnings: +Upozorenja: Items processed: Obrađene stavke: @@ -348,6 +351,9 @@ Total time: Ukupno vrijeme: +Warning +Upozorenje + Stopped Zaustavljeno @@ -357,6 +363,21 @@ Error parsing file %x, row %y, column %z. Greška u analizi datoteke %x, red %y, stupac %z. +Services +Servisi + +Show All +Prikaži sve + +Hide Others +Sakrij ostale + +Hide %x +Sakrij %x + +Quit %x +Napusti %x + Cannot set directory locks for the following folders: Nije moguće zaključavanje slijedećih foldera: @@ -373,11 +394,11 @@ Cannot read directory %x. Nije moguće učitati mapu %x. -/sec -/sek +%x/sec +%x/sek -%x items/sec -%x stavki/sek +%x items +%x stavki Show in Explorer Prikaži u Exploreru @@ -526,11 +547,11 @@ Generating database... Izrađivanje baze podataka... -Searching for excess file versions: -Traženje višestrukih verzija datoteka: +Searching for old file versions: +Pretraživanje starih verzija datoteka: -Removing excess file versions: -Uklanjanje višestrukih verzija datoteka: +Removing old file versions: +Uklanjanje starih verzija datoteka: Unable to create time stamp for versioning: Nije moguća izrada vremenske oznake za označavanje: @@ -588,6 +609,24 @@ Stvarno: %y bajta Unable to move %x to the recycle bin. Nije moguće premjestiti %x u koš za smeće. +Unable to access %x. +Nije moguće pristupiti %x. + +Authentication completed. +Provjera autentičnosti dovršena. + +Authentication failed. +Provjera autentičnosti nije uspjela. + +You may close this page now and continue with FreeFileSync. +Sada možete zatvoriti ovu stranicu i nastaviti sa FreeFileSync. + +The server returned an error: +Pogreška poslužitelja: + +Cannot determine free disk space for %x. +Nije moguće izračunati slobodan prostor diska za %x. + Cannot find %x. Nije moguće pronaći %x. @@ -600,18 +639,12 @@ Stvarno: %y bajta Cannot delete symbolic link %x. Nije moguće brisanje simbolične poveznice %x. -Cannot determine free disk space for %x. -Nije moguće izračunati slobodan prostor diska za %x. - Incorrect command line: Netočna naredbena linija: The server does not support authentication via %x. Server ne podržava provjeru autentičnosti preko %x. -Unable to access %x. -Nije moguće pristupiti %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -638,25 +671,6 @@ Stvarno: %y bajta Failed to open SFTP channel number %x. Nije uspjelo povezivanje na SFTP kanal broj %x. - -1 byte -%x bytes - - -%x bajt -%x bajta -%x bajtova - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Povuci && ispusti @@ -770,6 +784,25 @@ Naredba će biti pokrenuta ako se: &Retry &Ponovi + +1 byte +%x bytes + + +%x bajt +%x bajta +%x bajtova + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Učitavam... @@ -916,7 +949,7 @@ Naredba će biti pokrenuta ako se: Spremi kao &Slijedni zadatak... Show &log - +Prikaži &zapisnik Start &comparison Započni &usporedbu @@ -978,9 +1011,6 @@ Naredba će biti pokrenuta ako se: Access online storage Pristupi online pohrani -Swap sides -Zamjeni strane - Close search bar Zatvori traku pretraživanja @@ -1005,6 +1035,9 @@ Naredba će biti pokrenuta ako se: View type: Vrsta prikaza: +Save as default +Spremi kao standardno + Select view: Odaberite prikaz: @@ -1062,6 +1095,15 @@ Naredba će biti pokrenuta ako se: Handle daylight saving time Upravljaj ljetnim računanjem vremena +Ignore errors +Zanemariti pogreške + +Retry count: +Broj pokušaja: + +Delay (in seconds): +Odgoda (u sekundama): + Performance improvements: Poboljšanje perfomansi: @@ -1136,17 +1178,11 @@ Naredba će biti pokrenuta ako se: Last x days: Zadnjih nekoliko dana: -Ignore errors -Zanemariti pogreške - -Retry count: -Broj pokušaja: - -Delay (in seconds): -Odgoda (u sekundama): +&Override default log path: +&Prepiši zadanu putanju zapisnika: -Run a command after synchronization: -Pokreni naredbu nakon sinkronizacije: +Run a command: +Izvrši naredbu: OK U redu @@ -1196,6 +1232,9 @@ Naredba će biti pokrenuta ako se: Directory on server: Mapa na serveru: +Access timeout (in seconds): +Prekid pristupa nakon (u sekundama): + SFTP channels per connection: SFTP kanala po konekciji: @@ -1274,12 +1313,6 @@ Naredba će biti pokrenuta ako se: Stop synchronization at first error Zaustavi sinkronizaciju pri prvoj pogrešci -Save log: -Spremi izvještaj: - -Limit number of log files: -Limitiranje broja log datoteka: - How can I schedule a batch job? Kako zakazati slijedni zadatak? @@ -1316,8 +1349,11 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Show all permanently hidden dialogs and warning messages again Prikaži sve trajno skrivene prozore i poruke upozorenja ponovno -Remove old log files after x days: - +Default log path: +Zadana putanja zapisnika: + +&Delete logs after x days: +&Izbrisati zapisnike nakon x dana: Customize context menu: Prilagodite kontekstni izbornik: @@ -1328,8 +1364,8 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. &Default &Zadano -Feedback and suggestions are welcome -Povratne informacije i prijedlozi su dobrodošli +Feedback and suggestions are welcome: +Povratne informacije i prijedlozi su dobrodošli: Home page Web stranica @@ -1355,8 +1391,8 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Source code written in C++ using: Izvorni kod napisan u C++ uz korištenje: -Published under the GNU General Public License -Objavljeno pod licencom GNU General Public +Published under the GNU General Public License: +Objavljeno pod GNU General Public Licencom: Many thanks for localization: Velike zahvale idu: @@ -1413,7 +1449,7 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Info No log entries - +Nema izvještaja Select all Odaberi sve @@ -1439,6 +1475,9 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Overview Pregled +Swap sides +Zamjeni strane + Show "%x" Prikaži "%x" @@ -1617,9 +1656,6 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Show filtered or temporarily excluded files Prikaži filtrirane ili privremeno odvojene datoteke -Save as default -Spremi kao standardno - Filter Filtriranje @@ -1970,12 +2006,6 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. Checking recycle bin failed for folder %x. Provjeravanje koša za smeće neuspješno za mapu %x. -The following XML elements could not be read: -Slijedeći XML elementi se nisu mogli učitati: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Konfiguracijska datoteka %x je nepotpuna. Nedostajući elementi će biti postavljeni na početne vrijednosti. - Prepare installation Pripremam instalaciju 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 @@ Items differ in attributes only Položky se liší pouze ve vlastnostech +The name %x is used by more than one item in the folder. +Název %x je použit více jak jednou položkou ve složce. + Resolving symbolic link %x Hledání odkazu symbolického zástupce %x @@ -188,9 +191,6 @@ File time tolerance Časová tolerance -Folder access timeout -Časový limit přístupu ke složce - Run with background priority Spuštění s prioritou na pozadí @@ -336,8 +336,11 @@ Update attributes on right Nastavit vlastnosti vpravo -Warning -Varování +Errors: +Chyb: + +Warnings: +Varování: Items processed: Zpracováno položek: @@ -348,6 +351,9 @@ Total time: Celkový čas: +Warning +Varování + Stopped Zastaveno @@ -357,6 +363,21 @@ Error parsing file %x, row %y, column %z. Chyba zpracování souboru %x: na řádku %y ve sloupci %z. +Services +Služby + +Show All +Zobrazit vše + +Hide Others +Skrýt ostatní + +Hide %x +Skrýt %x + +Quit %x +Ukončit %x + Cannot set directory locks for the following folders: Nelze uzamčít následující adresáře: @@ -373,11 +394,11 @@ Cannot read directory %x. Nelze číst adresář %x. -/sec -/s +%x/sec +%x/s -%x items/sec -%x položek/s +%x items +%x položek Show in Explorer Zobrazit v Průzkumníkovi @@ -526,11 +547,11 @@ Generating database... Vytváření databáze... -Searching for excess file versions: -Hledání nadbytečných verzí: +Searching for old file versions: +Hledání starších verzí: -Removing excess file versions: -Odstraňování nadbytečných verzí: +Removing old file versions: +Odstraňování starších verzí: Unable to create time stamp for versioning: Nelze vytvořit časové značky verzování: @@ -588,6 +609,24 @@ Aktuálně: %y b Unable to move %x to the recycle bin. Není možné přesunout %x do Koše. +Unable to access %x. +Nepodařil se přístup k %x. + +Authentication completed. +Přístup povolen. + +Authentication failed. +Přístup odmítnut. + +You may close this page now and continue with FreeFileSync. +Nyní můžete tuto stránku zavřít a pokračovat s FreeFileSync. + +The server returned an error: +Server vrátil chybu: + +Cannot determine free disk space for %x. +Nelze zjistit volné místo na disku %x. + Cannot find %x. Nelze najít %x. @@ -600,18 +639,12 @@ Aktuálně: %y b Cannot delete symbolic link %x. Nelze smazat symbolický odkaz %x. -Cannot determine free disk space for %x. -Nelze zjistit volné místo na disku %x. - Incorrect command line: Neplatný příkaz: The server does not support authentication via %x. Server nepodporuje ověření pomocí %x. -Unable to access %x. -Nepodařil se přístup k %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -638,25 +671,6 @@ Aktuálně: %y b Failed to open SFTP channel number %x. Nelze otevřít SFTP kanál %x. - -1 byte -%x bytes - - -%x B -%x B -%x B - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Přetáhni sem && pusť @@ -770,6 +784,25 @@ Příkaz je spuštěn když: &Retry &Opakovat + +1 byte +%x bytes + + +%x B +%x B +%x B + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Načítání... @@ -916,7 +949,7 @@ Příkaz je spuštěn když: Uložit jako &dávku... Show &log - +&Zobrazit žurnál Start &comparison Začít &porovnání @@ -978,9 +1011,6 @@ Příkaz je spuštěn když: Access online storage Přístup k uložišti -Swap sides -Změna stran - Close search bar Zavřít hledání @@ -1005,6 +1035,9 @@ Příkaz je spuštěn když: View type: Typ zobrazení: +Save as default +Uložit jako výchozí + Select view: Výběr zobrazení: @@ -1062,6 +1095,15 @@ Příkaz je spuštěn když: Handle daylight saving time Používat letní čas +Ignore errors +Přeskočit chyby + +Retry count: +Počet opakování: + +Delay (in seconds): +Prodleva (v sekundách): + Performance improvements: Vylepšení rychlosti: @@ -1136,17 +1178,11 @@ Příkaz je spuštěn když: Last x days: Posledních dní: -Ignore errors -Přeskočit chyby - -Retry count: -Počet opakování: - -Delay (in seconds): -Prodleva (v sekundách): +&Override default log path: +&Nahradit výchozí cestu žurnálu: -Run a command after synchronization: -Po dokončení synchronizace spustit příkaz: +Run a command: +Spustit příkaz: OK OK @@ -1196,6 +1232,9 @@ Příkaz je spuštěn když: Directory on server: Adresář na serveru: +Access timeout (in seconds): +Časový limit přístupu (v sekundách): + SFTP channels per connection: SFTP kanálů na spojení: @@ -1274,12 +1313,6 @@ Příkaz je spuštěn když: Stop synchronization at first error Ukončit synchronizaci při první chybě -Save log: -Uložit žurnál: - -Limit number of log files: -Omezit množství žurnálů: - How can I schedule a batch job? Jak nastavit spouštění dávky? @@ -1313,8 +1346,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Zobrazit znovu všechny trvale skryté dialogy a varovná hlášení -Remove old log files after x days: - +Default log path: +Výchozí cesta žurnálu: + +&Delete logs after x days: +&Smazat žurnály po (dnech): Customize context menu: Přizpůsobit kontextovou nabídku: @@ -1325,8 +1361,8 @@ This guarantees a consistent state even in case of a serious error. &Default &Předdefinované -Feedback and suggestions are welcome -Komentáře a náměty jsou vždy vítány +Feedback and suggestions are welcome: +Komentáře a náměty jsou vždy vítány: Home page Navštivte @@ -1352,8 +1388,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: Zdrojový kód byl napsán kompletně v C++ pomocí: -Published under the GNU General Public License -Vydáno pod GNU General Public License (GPL) +Published under the GNU General Public License: +Vydáno pod GNU General Public License (GPL): Many thanks for localization: Poděkování za překlad FreeFileSync: @@ -1410,7 +1446,7 @@ This guarantees a consistent state even in case of a serious error. Info No log entries - +Žádné záznamy Select all Vybrat vše @@ -1436,6 +1472,9 @@ This guarantees a consistent state even in case of a serious error. Overview Přehled +Swap sides +Změna stran + Show "%x" Zobrazit "%x" @@ -1614,9 +1653,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files Zobrazit filtrované nebo dočasně vynechané soubory -Save as default -Uložit jako výchozí - Filter Filtr @@ -1967,12 +2003,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. Selhala kontrola Koše pro složku %x. -The following XML elements could not be read: -Nelze načíst následující XML elementy: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Konfigurační soubor %x je nekompletní. Chybějící položky budou nahrazeny výchozími hodnotami. - Prepare installation Příprava instalace 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 @@ Items differ in attributes only Enhederne har kun attributter til forskel +The name %x is used by more than one item in the folder. +Navnet %x bruges af flere emner i mappen. + Resolving symbolic link %x Løser symbolsk link %x @@ -187,9 +190,6 @@ File time tolerance Tidstolerance -Folder access timeout -Tidsgrænse mappeadgang - Run with background priority Kør med baggrundsprioritet @@ -334,8 +334,11 @@ Update attributes on right Opdater attributter mod højre -Warning -Advarsel +Errors: +Fejl: + +Warnings: +Advarsler: Items processed: Emner behandlet: @@ -346,6 +349,9 @@ Total time: Samlet tid: +Warning +Advarsel + Stopped Afbrudt @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Behandlingsfejl i filen %x, række %y, kolonne %z. +Services +Tjenester + +Show All +Vis alle + +Hide Others +Skjul andre + +Hide %x +Skjul %x + +Quit %x +Luk %x + Cannot set directory locks for the following folders: Kan ikke sætte mappelåse for følgende mapper: @@ -370,11 +391,11 @@ Cannot read directory %x. Kan ikke læse mappen %x. -/sec -/sek +%x/sec +%x/sek -%x items/sec -%x emner/sek +%x items +%x emner Show in Explorer Åben filplacering @@ -523,11 +544,11 @@ Generating database... Opretter database... -Searching for excess file versions: -Finder overskredne filversioner: +Searching for old file versions: +Søger efter gamle filversioner: -Removing excess file versions: -Fjerner overskredne filversioner: +Removing old file versions: +Fjerner gamle filversioner: Unable to create time stamp for versioning: Kan ikke oprette tidsstempel til versionering: @@ -585,6 +606,24 @@ Aktuel: %y byte Unable to move %x to the recycle bin. Kunne ikke flytte %x til papirkurv. +Unable to access %x. +Kan ikke tilgå %x. + +Authentication completed. +Godkendelse gennemført. + +Authentication failed. +Godkendelse fejlede. + +You may close this page now and continue with FreeFileSync. +Du kan lukke siden og returnere til FreeFileSync. + +The server returned an error: +Serverfejl: + +Cannot determine free disk space for %x. +Kan ikke definere ledig plads på %x. + Cannot find %x. Kan ikke finde %x. @@ -597,18 +636,12 @@ Aktuel: %y byte Cannot delete symbolic link %x. Kan ikke slette det symbolske link %x. -Cannot determine free disk space for %x. -Kan ikke definere ledig plads på %x. - Incorrect command line: Ugyldig kommando: The server does not support authentication via %x. Serveren støtter ikke godkendelse via %x. -Unable to access %x. -Kan ikke tilgå %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Aktuel: %y byte Failed to open SFTP channel number %x. Fejl ved åbning af SFTP kanal nr. %x. - -1 byte -%x bytes - - -1 byte -%x byte - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Træk emner hertil @@ -764,6 +779,24 @@ Kommandoen udføres hvis: &Retry &Prøv igen + +1 byte +%x bytes + + +1 byte +%x byte + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Indlæser... @@ -909,7 +942,7 @@ Kommandoen udføres hvis: Gem som &batchfil... Show &log - +Vis &log Start &comparison Start &analyse @@ -971,9 +1004,6 @@ Kommandoen udføres hvis: Access online storage Åben onlinelager -Swap sides -Byt side - Close search bar Luk søgelinie @@ -998,6 +1028,9 @@ Kommandoen udføres hvis: View type: Visning: +Save as default +Gem som standard + Select view: Vælg visning: @@ -1055,6 +1088,15 @@ Kommandoen udføres hvis: Handle daylight saving time Tag hensyn til sommertid +Ignore errors +Ignorér fejl + +Retry count: +Antal forsøg: + +Delay (in seconds): +Forsinkelse (sek): + Performance improvements: Ydelsesforbedring: @@ -1129,17 +1171,11 @@ Kommandoen udføres hvis: Last x days: Sidste x dage: -Ignore errors -Ignorér fejl - -Retry count: -Antal forsøg: - -Delay (in seconds): -Forsinkelse (sek): +&Override default log path: +&Tilpas logplacering: -Run a command after synchronization: -Kør kommando efter synkronisering: +Run a command: +Kør kommando: OK OK @@ -1189,6 +1225,9 @@ Kommandoen udføres hvis: Directory on server: Servermappe: +Access timeout (in seconds): +Timeout (sekunder): + SFTP channels per connection: SFTP kanaler pr. forbindelse: @@ -1267,12 +1306,6 @@ Kommandoen udføres hvis: Stop synchronization at first error Stop synkronisering ved første fejl -Save log: -Gem log: - -Limit number of log files: -Begræns antal logfiler: - How can I schedule a batch job? Hvordan oprettes en batchfil? @@ -1309,8 +1342,11 @@ Sikrer processen ved alvorlige fejl. Show all permanently hidden dialogs and warning messages again Vis skjulte advarsler og beskeder igen -Remove old log files after x days: - +Default log path: +Logplacering: + +&Delete logs after x days: +&Slet logs efter x dage: Customize context menu: Tilpas kontekstmenu: @@ -1321,8 +1357,8 @@ Sikrer processen ved alvorlige fejl. &Default S&tandard -Feedback and suggestions are welcome -Kritik og forslag er meget velkomne +Feedback and suggestions are welcome: +Kritik og foreslag er meget velkomne: Home page Hjemmeside @@ -1348,8 +1384,8 @@ Sikrer processen ved alvorlige fejl. Source code written in C++ using: Kildekoden er skrevet i C++ med hjælp fra: -Published under the GNU General Public License -Udgivet under GNU General Public Licence +Published under the GNU General Public License: +Udgivet under GNU General Public License: Many thanks for localization: Tak for oversættelse til: @@ -1406,7 +1442,7 @@ Sikrer processen ved alvorlige fejl. Info No log entries - +Ingen logemner Select all Vælg alt @@ -1432,6 +1468,9 @@ Sikrer processen ved alvorlige fejl. Overview Oversigt +Swap sides +Byt side + Show "%x" Vis "%x" @@ -1606,9 +1645,6 @@ Sikrer processen ved alvorlige fejl. Show filtered or temporarily excluded files Vis filtrerede eller midlertidigt ekskluderede filer -Save as default -Gem som standard - Filter Filter @@ -1954,12 +1990,6 @@ Sikrer processen ved alvorlige fejl. Checking recycle bin failed for folder %x. Check papirkurv fejlede for mappen %x. -The following XML elements could not be read: -Kunne ikke læse følgende XML emner: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Indstillingsfilen %x er ikke komplet. Manglende elementer sættes til standard. - Prepare installation Forbereder installering 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 @@ globaal configuratiebestand: Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. -Een willekeurig aantal FreeFileSync. ffs_gui en/of "ffs_batch" configuratiebestanden. +Een willekeurig aantal FreeFileSync "ffs_gui" en/of "ffs_batch" configuratiebestanden. Any number of alternative directory pairs for at most one config file. Een willekeurig aantal alternatieve mapparen voor maximaal een configuratiebestand. @@ -160,6 +160,9 @@ Items differ in attributes only De items verschillen alleen in de kenmerken +The name %x is used by more than one item in the folder. +De naam %x wordt gebruikt door meer dan één item in de map. + Resolving symbolic link %x Symbolische link %x oplossen @@ -187,9 +190,6 @@ File time tolerance Tolerantie bestandstijd -Folder access timeout -Time-out map toegang - Run with background priority Uitvoeren met achtergrond prioriteit @@ -334,8 +334,11 @@ Update attributes on right Update kenmerken aan de rechterzijde -Warning -Waarschuwing +Errors: +Fouten: + +Warnings: +Waarschuwingen: Items processed: Items verwerkt: @@ -346,6 +349,9 @@ Total time: Totale tijd: +Warning +Waarschuwing + Stopped Gestopt @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Fout bij het analyseren van bestand %x, rij %y, kolom %z. +Services +Diensten + +Show All +Toon alles + +Hide Others +Verberg anderen + +Hide %x +Verberg %x + +Quit %x +Sluit %x + Cannot set directory locks for the following folders: Kan map vergrendelingen niet instellen voor de volgende mappen: @@ -370,11 +391,11 @@ Cannot read directory %x. Kan de map %x niet lezen. -/sec -/sec +%x/sec +%x/sec -%x items/sec -%x items/sec +%x items +%x items Show in Explorer In Explorer weergeven @@ -523,11 +544,11 @@ Generating database... Genereren database... -Searching for excess file versions: -Zoeken naar overtollige bestandsversies: +Searching for old file versions: +Naar oude bestandsversies zoeken: -Removing excess file versions: -Overmatige bestandsversies verwijderen: +Removing old file versions: +Oude bestandsversies verwijderen: Unable to create time stamp for versioning: Kan geen tijdstempel maken voor versiebeheer: @@ -585,6 +606,24 @@ Werkelijk: %y bytes Unable to move %x to the recycle bin. Niet in staat om %x naar de prullenbak te verplaatsen. +Unable to access %x. +Geen toegang tot %x. + +Authentication completed. +Authenticatie voltooid. + +Authentication failed. +Verificatie mislukt. + +You may close this page now and continue with FreeFileSync. +U kunt deze pagina nu sluiten en doorgaan met FreeFileSync. + +The server returned an error: +De server heeft een fout teruggestuurd: + +Cannot determine free disk space for %x. +Kan de vrije schijfruimte voor %x niet bepalen. + Cannot find %x. Kan %x niet vinden. @@ -597,18 +636,12 @@ Werkelijk: %y bytes Cannot delete symbolic link %x. Kan de symbolische koppeling %x niet verwijderen. -Cannot determine free disk space for %x. -Kan de vrije schijfruimte voor %x niet bepalen. - Incorrect command line: Onjuiste opdrachtregel: The server does not support authentication via %x. De server ondersteunt geen verificatie via %x. -Unable to access %x. -Geen toegang tot %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Werkelijk: %y bytes Failed to open SFTP channel number %x. Fout bij het openen van SFTP kanaalnummer %x. - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Slepen && neerzetten @@ -764,6 +779,24 @@ De opdracht wordt geactiveerd als: &Retry &Probeer opnieuw + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Laden... @@ -867,7 +900,7 @@ De opdracht wordt geactiveerd als: Lokale vergelijking instellingen Local synchronization settings -Lokale synchronisatie-instellingen +Lokale synchronisatie instellingen Local filter Lokaal filter @@ -909,7 +942,7 @@ De opdracht wordt geactiveerd als: Opslaan als &batchverwerking... Show &log - +Bekijk &logbestand Start &comparison Start &vergelijking @@ -971,9 +1004,6 @@ De opdracht wordt geactiveerd als: Access online storage Toegang tot online opslag -Swap sides -Verwisselen van zijden - Close search bar Sluit de zoekbalk @@ -998,6 +1028,9 @@ De opdracht wordt geactiveerd als: View type: Type bekijken: +Save as default +Opslaan als standaard + Select view: Selecteer weergave: @@ -1055,6 +1088,15 @@ De opdracht wordt geactiveerd als: Handle daylight saving time Zomertijd gebruiken +Ignore errors +Negeer fouten + +Retry count: +Probeer opnieuw te tellen: + +Delay (in seconds): +Vertraging (in seconden): + Performance improvements: Prestatieverbeteringen: @@ -1129,17 +1171,11 @@ De opdracht wordt geactiveerd als: Last x days: Laatste x dagen: -Ignore errors -Negeer fouten - -Retry count: -Probeer opnieuw te tellen: - -Delay (in seconds): -Vertraging (in seconden): +&Override default log path: +&Overschrijf standaard logboekpad: -Run a command after synchronization: -Voer een opdracht uit na de synchronisatie: +Run a command: +Voer een opdracht uit: OK OK @@ -1181,7 +1217,7 @@ De opdracht wordt geactiveerd als: Gebruikersnaam: Private key file: -Prive-sleutel bestand: +Prive sleutelbestand: &Show password &Toon wachtwoord @@ -1189,6 +1225,9 @@ De opdracht wordt geactiveerd als: Directory on server: Map op de server: +Access timeout (in seconds): +Toegang timeout (in seconden): + SFTP channels per connection: SFTP kanalen per verbinding: @@ -1267,12 +1306,6 @@ De opdracht wordt geactiveerd als: Stop synchronization at first error Synchronisatie bij eerste fout stoppen -Save log: -Logbestand opslaan: - -Limit number of log files: -Beperk het aantal logbestanden: - How can I schedule a batch job? Hoe kan ik een batchverwerking plannen? @@ -1309,8 +1342,11 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Show all permanently hidden dialogs and warning messages again Alle permanent verborgen dialogen en waarschuwingsberichten opnieuw weergeven -Remove old log files after x days: - +Default log path: +Standaard logboek pad: + +&Delete logs after x days: +&Verwijder logboeken na x dagen: Customize context menu: Contextmenu aanpassen: @@ -1321,8 +1357,8 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. &Default &Standaard -Feedback and suggestions are welcome -Terugkoppeling en suggesties zijn welkom +Feedback and suggestions are welcome: +Terugkoppeling en suggesties zijn welkom: Home page Startpagina @@ -1348,8 +1384,8 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Source code written in C++ using: Broncode werd in C++ geschreven met: -Published under the GNU General Public License -Gepubliceerd onder de GNU General Public License +Published under the GNU General Public License: +Gepubliceerd onder de GNU General Public License: Many thanks for localization: Hartelijk dank voor de lokalisatie: @@ -1379,7 +1415,7 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Markeer configuraties die niet meer dan het volgende aantal dagen zijn uitgevoerd: Synchronization Settings -Synchronisatie-instellingen +Synchronisatie instellingen Access Online Storage Toegang Online Opslag @@ -1406,7 +1442,7 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Info No log entries - +Geen vermeldingen in het logboek Select all Alles selecteren @@ -1432,6 +1468,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Overview Overzicht +Swap sides +Verwisselen van zijden + Show "%x" Toon "%x" @@ -1606,9 +1645,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Show filtered or temporarily excluded files Toon gefilterde of tijdelijk uitgesloten bestanden -Save as default -Opslaan als standaard - Filter Filter @@ -1954,12 +1990,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. Checking recycle bin failed for folder %x. Controle van de prullenbak voor map %x is mislukt. -The following XML elements could not be read: -De volgende XML-elementen kunnen niet worden gelezen: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Configuratiebestand %x is onvolledig. De ontbrekende elementen worden ingesteld op hun standaardwaarden. - Prepare installation Installatie voorbereiden 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 @@ n == 1 ? 0 : 1 -No log entries - - -Remove old log files after x days: - - -Show &log - - Both sides have changed since last synchronization. Both sides have changed since last synchronisation. @@ -169,6 +160,9 @@ Items differ in attributes only Items differ in attributes only +The name %x is used by more than one item in the folder. +The name %x is used by more than one item in the folder. + Resolving symbolic link %x Resolving symbolic link %x @@ -196,9 +190,6 @@ File time tolerance File time tolerance -Folder access timeout -Folder access timeout - Run with background priority Run with background priority @@ -343,8 +334,11 @@ Update attributes on right Update attributes on right -Warning -Warning +Errors: +Errors: + +Warnings: +Warnings: Items processed: Elements processed: @@ -355,6 +349,9 @@ Total time: Total time: +Warning +Warning + Stopped Stopped @@ -364,6 +361,21 @@ Error parsing file %x, row %y, column %z. Error parsing file %x, row %y, column %z. +Services +Services + +Show All +Show All + +Hide Others +Hide Others + +Hide %x +Hide %x + +Quit %x +Quit %x + Cannot set directory locks for the following folders: Cannot set directory locks for the following folders: @@ -379,11 +391,11 @@ Cannot read directory %x. Cannot read directory %x. -/sec -/sec +%x/sec +%x/sec -%x items/sec -%x items/sec +%x items +%x items Show in Explorer Show in Explorer @@ -532,11 +544,11 @@ Generating database... Generating database... -Searching for excess file versions: -Searching for excess file versions: +Searching for old file versions: +Searching for old file versions: -Removing excess file versions: -Removing excess file versions: +Removing old file versions: +Removing old file versions: Unable to create time stamp for versioning: Unable to create time stamp for versioning: @@ -594,6 +606,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. Unable to move %x to the recycle bin. +Unable to access %x. +Unable to access %x. + +Authentication completed. +Authentication completed. + +Authentication failed. +Authentication failed. + +You may close this page now and continue with FreeFileSync. +You may close this page now and continue with FreeFileSync. + +The server returned an error: +The server returned an error: + +Cannot determine free disk space for %x. +Cannot determine free disk space for %x. + Cannot find %x. Cannot find %x. @@ -606,18 +636,12 @@ Actual: %y bytes Cannot delete symbolic link %x. Cannot delete symbolic link %x. -Cannot determine free disk space for %x. -Cannot determine free disk space for %x. - Incorrect command line: Incorrect command line: The server does not support authentication via %x. The server does not support authentication via %x. -Unable to access %x. -Unable to access %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -642,24 +666,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. Failed to open SFTP channel number %x. - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Drag && drop @@ -773,6 +779,24 @@ The command is triggered if: &Retry &Retry + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Loading... @@ -917,6 +941,9 @@ The command is triggered if: Save as &batch job... Save as &batch job... +Show &log +Show &log + Start &comparison Start &comparison @@ -977,9 +1004,6 @@ The command is triggered if: Access online storage Access online storage -Swap sides -Swap sides - Close search bar Close search bar @@ -1004,6 +1028,9 @@ The command is triggered if: View type: View type: +Save as default +Save as default + Select view: Select view: @@ -1061,6 +1088,15 @@ The command is triggered if: Handle daylight saving time Handle daylight saving time +Ignore errors +Ignore errors + +Retry count: +Retry count: + +Delay (in seconds): +Delay (in seconds): + Performance improvements: Performance improvements: @@ -1135,17 +1171,11 @@ The command is triggered if: Last x days: Last x days: -Ignore errors -Ignore errors - -Retry count: -Retry count: +&Override default log path: +&Override default log path: -Delay (in seconds): -Delay (in seconds): - -Run a command after synchronization: -Run a command after synchronization: +Run a command: +Run a command: OK OK @@ -1195,6 +1225,9 @@ The command is triggered if: Directory on server: Directory on server: +Access timeout (in seconds): +Access timeout (in seconds): + SFTP channels per connection: SFTP channels per connection: @@ -1273,12 +1306,6 @@ The command is triggered if: Stop synchronization at first error Stop synchronisation at first error -Save log: -Save log: - -Limit number of log files: -Limit number of log files: - How can I schedule a batch job? How can I schedule a batch job? @@ -1315,6 +1342,12 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Show all permanently hidden dialogues and warning messages again +Default log path: +Default log path: + +&Delete logs after x days: +&Delete logs after x days: + Customize context menu: Customise context menu: @@ -1324,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. &Default &Default -Feedback and suggestions are welcome -Feedback and suggestions are welcome +Feedback and suggestions are welcome: +Feedback and suggestions are welcome: Home page Home page @@ -1351,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: Source code written in C++ using: -Published under the GNU General Public License -Published under the GNU General Public Licence +Published under the GNU General Public License: +Published under the GNU General Public License: Many thanks for localization: Many thanks for localisation: @@ -1408,6 +1441,9 @@ This guarantees a consistent state even in case of a serious error. Info Info +No log entries +No log entries + Select all Select all @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. Overview Overview +Swap sides +Swap sides + Show "%x" Show "%x" @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files Show filtered or temporarily excluded files -Save as default -Save as default - Filter Filter @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. Checking recycle bin failed for folder %x. -The following XML elements could not be read: -The following XML elements could not be read: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Configuration file %x is incomplete. The missing elements will be set to their default values. - Prepare installation Prepare installation 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 @@ Items differ in attributes only Seuls les attributs des éléments diffèrent +The name %x is used by more than one item in the folder. +Le nom %x est utilisé par plusieurs éléments du dossier. + Resolving symbolic link %x Résolution du lien symbolique %x @@ -187,9 +190,6 @@ File time tolerance Tolérance horaire -Folder access timeout -Dépassement du délai d'accès au dossier - Run with background priority Exécution avec la priorité de tâche de fond. @@ -334,8 +334,11 @@ Update attributes on right Mise à jour des attributs à droite -Warning -Attention +Errors: +Erreurs : + +Warnings: +Avertissements : Items processed: Élements traités : @@ -346,6 +349,9 @@ Total time: Durée totale : +Warning +Attention + Stopped Arrêté @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Erreur lors de l'analyse du fichier %x, ligne %y, colonne %z. +Services +Services + +Show All +Tout afficher + +Hide Others +Masquer les autres + +Hide %x +Masquer %x + +Quit %x +Quitter %x + Cannot set directory locks for the following folders: Impossible de verrouiller les répertoires des dossiers suivants : @@ -370,11 +391,11 @@ Cannot read directory %x. Impossible de lire le répertoire %x. -/sec -/sec +%x/sec +%x/sec -%x items/sec -%x éléments/sec +%x items +%x éléments Show in Explorer Montrer dans l'explorateur @@ -523,11 +544,11 @@ Generating database... Génération de la base de données ... -Searching for excess file versions: -Recherche des versions de fichiers excédentaires : +Searching for old file versions: +Recherche d'anciennes versions : -Removing excess file versions: -Suppression des versions de fichiers excédentaires : +Removing old file versions: +Suppression d'anciennes versions : Unable to create time stamp for versioning: Impossible de générer l'horodatage pour la gestion des versions : @@ -585,6 +606,24 @@ Trouvé : %y octets Unable to move %x to the recycle bin. Impossible de déplacer %x dans la Corbeille. +Unable to access %x. +Impossible d'accéder à %x. + +Authentication completed. +Identification terminée. + +Authentication failed. +Echec de l'identification. + +You may close this page now and continue with FreeFileSync. +Vous pouvez fermer cette page maintenant et poursuivre avec FreeFileSync. + +The server returned an error: +Le serveur a renvoyé une erreur : + +Cannot determine free disk space for %x. +Impossible de calculer l'espace libre du disque %x. + Cannot find %x. Impossible de trouver %x. @@ -597,18 +636,12 @@ Trouvé : %y octets Cannot delete symbolic link %x. Impossible de supprimer le lien symbolique %x. -Cannot determine free disk space for %x. -Impossible de calculer l'espace libre du disque %x. - Incorrect command line: Ligne de commande incorrecte : The server does not support authentication via %x. Le serveur refuse l'authentification par %x. -Unable to access %x. -Impossible d'accéder à %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Trouvé : %y octets Failed to open SFTP channel number %x. Impossible d'ouvrir le port SFTP %x. - -1 byte -%x bytes - - -%x octet -%x octets - - -%x MB -%x Mo - -%x KB -%x Ko - -%x GB -%x Go - Drag && drop Glisser && Déposer @@ -764,6 +779,24 @@ La commande est déclenchée si : &Retry &Réessayer + +1 byte +%x bytes + + +%x octet +%x octets + + +%x MB +%x Mo + +%x KB +%x Ko + +%x GB +%x Go + Loading... Chargement ... @@ -909,7 +942,7 @@ La commande est déclenchée si : Enregistrer en tant que fichier de &commandes ... Show &log - +Afficher &journal Start &comparison Démarrer la &comparaison @@ -971,9 +1004,6 @@ La commande est déclenchée si : Access online storage Accès au stockage en ligne -Swap sides -Permuter les côtés - Close search bar Fermer la barre de recherche @@ -998,6 +1028,9 @@ La commande est déclenchée si : View type: Type de vue : +Save as default +Sauvegarde par défaut + Select view: Choisir la vue : @@ -1055,6 +1088,15 @@ La commande est déclenchée si : Handle daylight saving time Gérer l'heure d'été +Ignore errors +Ignorer les erreurs + +Retry count: +Nombre de tentatives : + +Delay (in seconds): +Délai (en secondes) : + Performance improvements: Amélioration des performances : @@ -1129,17 +1171,11 @@ La commande est déclenchée si : Last x days: Derniers x jours : -Ignore errors -Ignorer les erreurs - -Retry count: -Nombre de tentatives : - -Delay (in seconds): -Délai (en secondes) : +&Override default log path: +&Remplacer le chemin par défaut du journal : -Run a command after synchronization: -Exécuter une commande après la synchronisation : +Run a command: +Éxecuter une commande : OK OK @@ -1189,6 +1225,9 @@ La commande est déclenchée si : Directory on server: Répertoire sur le serveur : +Access timeout (in seconds): +Délai d'accès (en secondes) : + SFTP channels per connection: Ports SFTP par connexion : @@ -1267,12 +1306,6 @@ La commande est déclenchée si : Stop synchronization at first error Arrêter la synchronisation à la première erreur -Save log: -Sauvegarde du fichier log : - -Limit number of log files: -Limiter le nombre de journaux : - How can I schedule a batch job? Comment planifier un fichier de commandes ? @@ -1309,8 +1342,11 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Show all permanently hidden dialogs and warning messages again Réafficher en permanence les boîtes de dialogue et les avertissements -Remove old log files after x days: - +Default log path: +Chemin du journal par défaut : + +&Delete logs after x days: +&Supprimer les journaux après x jours : Customize context menu: Personnaliser le menu contextuel : @@ -1321,8 +1357,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. &Default &Défaut -Feedback and suggestions are welcome -Vos commentaires et vos suggestions sont les bienvenus +Feedback and suggestions are welcome: +Commentaires et suggestions sont les bienvenus : Home page Page d'accueil @@ -1348,8 +1384,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Source code written in C++ using: Code source écrit en C++ utilisant : -Published under the GNU General Public License -Publié sous licence GNU General Public License +Published under the GNU General Public License: +Publié sous licence publique générale GNU : Many thanks for localization: Un grand merci pour la traduction à : @@ -1406,7 +1442,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Info No log entries - +Aucune entrée journal Select all Tout sélectionner @@ -1432,6 +1468,9 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Overview Aperçu +Swap sides +Permuter les côtés + Show "%x" Afficher "%x" @@ -1606,9 +1645,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Show filtered or temporarily excluded files Afficher les fichiers filtrés ou temporairement exclus -Save as default -Sauvegarde par défaut - Filter Filtre @@ -1954,12 +1990,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. Checking recycle bin failed for folder %x. Échec de la vérification de la Corbeille pour le dossier %x. -The following XML elements could not be read: -Les éléments XML suivants ne peuvent être lus : - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Le fichier de configuration %x est incomplet. Les éléments manquants sont fixés à leur valeur par défaut. - Prepare installation Préparation de l'installation 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 @@ n == 1 ? 0 : 1 -Access timeout (in seconds): -Zeitlimit für Zugriff(in Sekunden): - -The server returned an error: -Der Server hat einen Fehler zurückgegeben: - -You may close this page now and continue with FreeFileSync. -Sie können diese Seite nun schließen und mit FreeFileSync fortfahren. - -Authentication failed. -Authentifizierung fehlgeschlagen. - -Authentication completed. -Authentifizierung abgeschlossen. - Both sides have changed since last synchronization. Beide Seiten wurden seit der letzten Synchronisation verändert. @@ -175,6 +160,9 @@ Items differ in attributes only Die Elemente unterscheiden sich nur in Attributen +The name %x is used by more than one item in the folder. +Der Name %x wird von mehr als einem Element im Ordner verwendet. + Resolving symbolic link %x Folge der symbolischen Verknüpfung %x @@ -346,8 +334,11 @@ Update attributes on right Aktualisiere Attribute des rechten Elements -Warning -Warnung +Errors: +Fehler: + +Warnings: +Warnungen: Items processed: Verarbeitete Elemente: @@ -358,6 +349,9 @@ Total time: Gesamtzeit: +Warning +Warnung + Stopped Gestoppt @@ -367,6 +361,21 @@ Error parsing file %x, row %y, column %z. Fehler beim Auswerten der Datei %x, Zeile %y, Spalte %z. +Services +Dienste + +Show All +Alle einblenden + +Hide Others +Andere ausblenden + +Hide %x +%x ausblenden + +Quit %x +%x beenden + Cannot set directory locks for the following folders: Die Verzeichnissperren können für die folgenden Ordner nicht gesetzt werden: @@ -382,11 +391,11 @@ Cannot read directory %x. Das Verzeichnis %x kann nicht gelesen werden. -/sec -/sek +%x/sec +%x/sek -%x items/sec -%x Elemente/sek +%x items +%x Elemente Show in Explorer Im Explorer anzeigen @@ -535,11 +544,11 @@ Generating database... Erzeuge Synchronisationsdatenbank... -Searching for excess file versions: -Suche überzählige Dateiversionen: +Searching for old file versions: +Suche alte Dateiversionen: -Removing excess file versions: -Entferne überzählige Dateiversionen: +Removing old file versions: +Entferne alte Dateiversionen: Unable to create time stamp for versioning: Der Zeitstempel für die Versionierung kann nicht erstellt werden: @@ -600,6 +609,18 @@ Tatsächlich: %y bytes Unable to access %x. Auf %x kann nicht zugegriffen werden. +Authentication completed. +Authentifizierung abgeschlossen. + +Authentication failed. +Authentifizierung fehlgeschlagen. + +You may close this page now and continue with FreeFileSync. +Sie können diese Seite nun schließen und mit FreeFileSync fortfahren. + +The server returned an error: +Der Server hat einen Fehler zurückgegeben: + Cannot determine free disk space for %x. Der freie Speicherplatz für %x konnte nicht ermittelt werden. @@ -983,9 +1004,6 @@ Die Befehlszeile wird ausgelöst, wenn: Access online storage Auf Onlinespeicher zugreifen -Swap sides -Seiten vertauschen - Close search bar Suchleiste schließen @@ -1010,6 +1028,9 @@ Die Befehlszeile wird ausgelöst, wenn: View type: Ansichtstyp: +Save as default +Als Standard speichern + Select view: Ansicht wählen: @@ -1153,8 +1174,8 @@ Die Befehlszeile wird ausgelöst, wenn: &Override default log path: &Standardprotokollpfad überschreiben: -Run a command after synchronization: -Befehl nach Synchronisation ausführen: +Run a command: +Befehl ausführen: OK OK @@ -1204,6 +1225,9 @@ Die Befehlszeile wird ausgelöst, wenn: Directory on server: Verzeichnis auf Server: +Access timeout (in seconds): +Zeitlimit für Zugriff(in Sekunden): + SFTP channels per connection: SFTP Kanäle je Verbindung: @@ -1321,8 +1345,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. Default log path: Standardprotokollpfad: -Remove old log files after x days: -Alte Protokolldateien nach x Tagen entfernen: +&Delete logs after x days: +&Protokolldateien nach x Tagen löschen: Customize context menu: Kontextmenü anpassen: @@ -1333,8 +1357,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. &Default &Standard -Feedback and suggestions are welcome -Feedback und Vorschläge sind willkommen +Feedback and suggestions are welcome: +Hinweise und Vorschläge sind willkommen: Home page Homepage @@ -1360,8 +1384,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. Source code written in C++ using: Der Quellcode wurde in C++ geschrieben mit: -Published under the GNU General Public License -Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz +Published under the GNU General Public License: +Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz: Many thanks for localization: Vielen Dank für die Lokalisation: @@ -1444,6 +1468,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. Overview Übersicht +Swap sides +Seiten vertauschen + Show "%x" Zeige "%x" @@ -1618,9 +1645,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. Show filtered or temporarily excluded files Gefilterte oder temporär ausgeschlossene Dateien zeigen -Save as default -Als Standard speichern - Filter Filter 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 @@ Items differ in attributes only Τα στοιχεία διαφέρουν μόνο ως προς τα χαρακτηριστικά τους +The name %x is used by more than one item in the folder. +Το όνομα %x χρησιμοποιείται για περισσότερα από ένα αρχεία στον φάκελο. + Resolving symbolic link %x Επίλυση του συμβολικού συνδέσμου %x @@ -187,9 +190,6 @@ File time tolerance Ανοχή στην ημερομηνία αρχείου -Folder access timeout -Χρονικό όριο πρόσβασης στο φάκελο - Run with background priority Εκτέλεση με προτεραιότητα παρασκηνίου @@ -334,8 +334,11 @@ Update attributes on right Ενημέρωση των χαρακτηριστικών στα δεξιά -Warning -Προειδοποίηση +Errors: +Σφάλματα: + +Warnings: +Προειδοποιήσεις: Items processed: Επεξεργάστηκαν στοιχεία: @@ -346,6 +349,9 @@ Total time: Συνολική διάρκεια: +Warning +Προειδοποίηση + Stopped Διακοπή @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Σφάλμα κατά την ανάλυση του αρχείου %x, γραμμή %y, στήλη %z. +Services +Υπηρεσίες + +Show All +Εμφάνιση Όλων + +Hide Others +Απόκρυψη Άλλων + +Hide %x +Απόκρυψη %x + +Quit %x +Έξοδος από %x + Cannot set directory locks for the following folders: Δεν μπορούν να κλειδωθούν οι ακόλουθοι υποκατάλογοι: @@ -370,11 +391,11 @@ Cannot read directory %x. Δεν μπορεί να γίνει ανάγνωση του καταλόγου %x. -/sec -/δευτερόλεπτο +%x/sec +%x/δευτερόλεπτο -%x items/sec -%x στοιχεία/δευτερόλεπτο +%x items +%x στοιχεία Show in Explorer Εμφάνιση στην Εξερεύνηση @@ -523,11 +544,11 @@ Generating database... Δημιουργία βάσης δεδομένων... -Searching for excess file versions: -Αναζήτηση υπεράριθμων παλιών εκδόσεων: +Searching for old file versions: +Αναζήτηση παλιών εκδόσεων των αρχείων: -Removing excess file versions: -Αφαίρεση υπεράριθμων παλιών εκδόσεων: +Removing old file versions: +Διαγραφή παλιών εκδόσεων των αρχείων: Unable to create time stamp for versioning: Δεν ήταν δυνατή η δημιουργία χρονικής σήμανσης για τη διατήρηση παλιών εκδόσεων: @@ -585,6 +606,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. Δεν ήταν δυνατή η μεταφορά του %x στον κάδο ανακύκλωσης. +Unable to access %x. +Δεν είναι δυνατή η πρόσβαση στο %x. + +Authentication completed. +Ολοκληρώθηκε η επαλήθευση ταυτότητας. + +Authentication failed. +Η επαλήθευση ταυτότητας απέτυχε. + +You may close this page now and continue with FreeFileSync. +Τώρα μπορείτε να κλείσετε αυτή τη σελίδα και να συνεχίσετε το FreeFileSync. + +The server returned an error: +Ο διακομιστής επέστρεψε το λάθος: + +Cannot determine free disk space for %x. +Δεν μπορεί να υπολογιστεί ο ελεύθερος χώρος του δίσκου %x. + Cannot find %x. Το %x δεν μπορεί να βρεθεί. @@ -597,17 +636,11 @@ Actual: %y bytes Cannot delete symbolic link %x. Δεν μπορεί να διαγραφεί ο συμβολικός σύνδεσμος %x. -Cannot determine free disk space for %x. -Δεν μπορεί να υπολογιστεί ο ελεύθερος χώρος του δίσκου %x. - Incorrect command line: Εσφαλμένη γραμμή εντολών: The server does not support authentication via %x. -Ο εξυπηρετητής δεν υποστηρίζει πιστοποίηση μέσω %x. - -Unable to access %x. -Δεν είναι δυνατή η πρόσβαση στο %x. +Ο εξυπηρετητής δεν υποστηρίζει επαλήθευση ταυτότητας μέσω %x. Operation timed out after 1 second. @@ -633,24 +666,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. Αποτυχία ανοίγματος του SFTP καναλιού με αριθμό %x. - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Μεταφορά && Απόθεση @@ -764,6 +779,24 @@ The command is triggered if: &Retry &Επανάληψη + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Φόρτωση... @@ -909,7 +942,7 @@ The command is triggered if: Αποθήκευση ως δέσ&μη ενεργειών... Show &log - +Εμφάνιση αρχείου &καταγραφής Start &comparison Έ&ναρξη σύγκρισης @@ -971,9 +1004,6 @@ The command is triggered if: Access online storage Πρόσβαση σε online χώρο αποθήκευσης -Swap sides -Ανταλλαγή πλευρών - Close search bar Κλείσιμο γραμμής αναζήτησης @@ -998,6 +1028,9 @@ The command is triggered if: View type: Τύπος εμφάνισης: +Save as default +Αποθήκευση ως προεπιλογής + Select view: Επιλογή εμφάνισης: @@ -1055,6 +1088,15 @@ The command is triggered if: Handle daylight saving time Διαχείριση θερινής ώρας +Ignore errors +Αγνόηση σφαλμάτων + +Retry count: +Αριθμός προσπαθειών: + +Delay (in seconds): +Καθυστέρηση (σε δευτερόλεπτα): + Performance improvements: Βελτιώσεις της απόδοσης: @@ -1129,17 +1171,11 @@ The command is triggered if: Last x days: Τις τελευταίες x μέρες: -Ignore errors -Αγνόηση σφαλμάτων - -Retry count: -Αριθμός προσπαθειών: - -Delay (in seconds): -Καθυστέρηση (σε δευτερόλεπτα): +&Override default log path: +&Παράκαμψη προεπιλεγμένης διαδρομής για το αρχείο καταγραφής: -Run a command after synchronization: -Εκτέλεση εντολής μετά το συγχρονισμό: +Run a command: +Εκτέλεση της εντολής: OK OK @@ -1166,7 +1202,7 @@ The command is triggered if: &Ρητά SSL/TLS Authentication: -Πιστοποίηση: +Επαλήθευση ταυτότητας: &Password &Κωδικός πρόσβασης @@ -1189,6 +1225,9 @@ The command is triggered if: Directory on server: Υποκατάλογος στον διακομιστή: +Access timeout (in seconds): +Χρονικό όριο σύνδεσης (σε δευτερόλεπτα): + SFTP channels per connection: Αριθμός καναλιών SFTP ανά σύνδεση: @@ -1267,12 +1306,6 @@ The command is triggered if: Stop synchronization at first error Διακοπή του συγχρονισμού με το πρώτο σφάλμα -Save log: -Αποθήκευση αρχείου καταγραφής: - -Limit number of log files: -Περιορισμός αριθμού αρχείων καταγραφής: - How can I schedule a batch job? Πώς μπορώ να προγραμματίσω μια εργασία με δέσμη ενεργειών; @@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Επανεμφάνιση όλων των μόνιμα κρυμμένων ειδοποιήσεων και προειδοποιητικών μηνυμάτων -Remove old log files after x days: - +Default log path: +Προεπιλεγμένη διαδρομή για το αρχείο καταγραφής: + +&Delete logs after x days: +&Διαγραφή των αρχείων καταγραφής μετά από x ημέρες: Customize context menu: Προσαρμογή μενού περιβάλλοντος: @@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. &Default &Προεπιλογή -Feedback and suggestions are welcome -Τα σχόλια και οι προτάσεις σας είναι ευπρόσδεκτα +Feedback and suggestions are welcome: +Σχόλια και προτάσεις είναι ευπρόσδεκτα: Home page Αρχική σελίδα @@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: Ο πηγαίος κώδικας γράφτηκε σε C++ χρησιμοποιώντας τα: -Published under the GNU General Public License -Διανέμεται υπό την Γενική Άδεια Δημόσιας Χρήσης GNU +Published under the GNU General Public License: +Δημοσιευμένο υπό την Γενική Άδεια Δημόσιας Χρήσης GNU: Many thanks for localization: Πολλές ευχαριστίες για τις μεταφράσεις: @@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error. Πληροφορίες No log entries - +Καμία καταγραφή Select all Επιλογή όλων @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. Overview Σύνοψη +Swap sides +Ανταλλαγή πλευρών + Show "%x" Εμφάνιση της γραμμής "%x" @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files Εμφάνιση φιλτραρισμένων ή προσωρινά εξαιρεθέντων αρχείων -Save as default -Αποθήκευση ως προεπιλογής - Filter Φίλτρο @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. Ο έλεγχος του κάδου ανακύκλωσης απέτυχε για τον υποκατάλογο %x. -The following XML elements could not be read: -Τα ακόλουθα στοιχεία XML δεν ήταν δυνατό να αναγνωσθούν: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Το αρχείο παραμέτρων %x είναι ατελές. Τα στοιχεία που λείπουν θα αντικατασταθούν με τις προεπιλεγμένες τιμές τους. - Prepare installation Προετοιμασία εγκατάστασης 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 @@ n == 1 ? 0 : 1 -No log entries - - -Remove old log files after x days: - - -Show &log - - Both sides have changed since last synchronization. שני הצדדים שונו מאז הסנכרון האחרון. @@ -169,6 +160,9 @@ Items differ in attributes only פריטים שונים בתכונות בלבד +The name %x is used by more than one item in the folder. +בשם %x נעשה שימוש יותר מפעם אחת בתיקייה. + Resolving symbolic link %x פותר קישור סימבולי %x @@ -196,9 +190,6 @@ File time tolerance אפיצות זמן קובץ -Folder access timeout -זמן קצוב לגישה לתיייה - Run with background priority בצע עם עדיפות רקע @@ -343,8 +334,11 @@ Update attributes on right עדכן תכונות בצד שמאל -Warning -אזהרה +Errors: +שגיאות: + +Warnings: +אזהרות: Items processed: אלמנטים עובדו: @@ -355,6 +349,9 @@ Total time: זמן כולל: +Warning +אזהרה + Stopped נעצר @@ -364,6 +361,21 @@ Error parsing file %x, row %y, column %z. שגיאה בפענוח קובץ %x, שורה %y, טור %z. +Services +שרותים + +Show All +הראה הכל + +Hide Others +מסתיר אחרים + +Hide %x +מסתיר %x + +Quit %x +יוצא %x + Cannot set directory locks for the following folders: אין אפשרות להגדיר נעילות מחיצה עבור התיקיות הבאות: @@ -379,11 +391,11 @@ Cannot read directory %x. לא ניתן לקרוא מחיצה %x. -/sec -/שנ +%x/sec +%x /שניות -%x items/sec -%x פריטים לשניה +%x items +%x פריטים Show in Explorer הראה בסייר הקבצים @@ -532,11 +544,11 @@ Generating database... מייצר מסד נתונים... -Searching for excess file versions: -מחפש גרסאות קובץ עודפות: +Searching for old file versions: +מחפש גרסאות קובץ ישנות: -Removing excess file versions: -מסיר גרסאות קובץ עודפות: +Removing old file versions: +מסיר גרסאות קובץ ישנות: Unable to create time stamp for versioning: לא ניתן ליצור תג זמן לגרסאות: @@ -594,6 +606,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. לא יכול להעביר את %x לסל המחזור. +Unable to access %x. +לא ניתן לגשת אל %x. + +Authentication completed. +האימות הושלם. + +Authentication failed. +האימות נכשל. + +You may close this page now and continue with FreeFileSync. +אתה יכול לסגור דף זה עכשיו ולהמשיך עם FreeFileSync. + +The server returned an error: +השרת החזיר שגיאה: + +Cannot determine free disk space for %x. +לא ניתן לקבוע שטח דיסק פנוי עבור %x. + Cannot find %x. לא מוצא %x. @@ -606,18 +636,12 @@ Actual: %y bytes Cannot delete symbolic link %x. לא ניתן למחוק קישור סימבולי %x. -Cannot determine free disk space for %x. -לא ניתן לקבוע שטח דיסק פנוי עבור %x. - Incorrect command line: שורת פקודה לא תקינה: The server does not support authentication via %x. השרת אינו תומך באימות באמצעות %x. -Unable to access %x. -לא ניתן לגשת אל %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -642,24 +666,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. נכשלה פתיחת ערוץ SFTP מספר %x. - -1 byte -%x bytes - - -בית 1 -%x בתים - - -%x MB -%x מגה בייט - -%x KB -%x קילו בייט - -%x GB -%x גיגה בייט - Drag && drop גרור ושחרר @@ -773,6 +779,24 @@ The command is triggered if: &Retry &נסה שנית + +1 byte +%x bytes + + +בית 1 +%x בתים + + +%x MB +%x מגה בייט + +%x KB +%x קילו בייט + +%x GB +%x גיגה בייט + Loading... טוען... @@ -917,6 +941,9 @@ The command is triggered if: Save as &batch job... שמור כעבודת &אצווה... +Show &log +הצג &יומנים: + Start &comparison התחל &והשווה @@ -977,9 +1004,6 @@ The command is triggered if: Access online storage גש לאחסון מכוון -Swap sides -החלף צדדים - Close search bar סגור סרגל חיפוש @@ -1004,6 +1028,9 @@ The command is triggered if: View type: הצג סוג: +Save as default +שמור כברירת מחדל + Select view: בחר תצוגה: @@ -1061,6 +1088,15 @@ The command is triggered if: Handle daylight saving time התמודד עם שעון קיץ +Ignore errors +התעלם משגיאות + +Retry count: +מונה נסיונות חוזרים: + +Delay (in seconds): +השהייה (בשניות): + Performance improvements: שיפורים בביצועים: @@ -1135,17 +1171,11 @@ The command is triggered if: Last x days: x ימים אחרונים: -Ignore errors -התעלם משגיאות - -Retry count: -מונה נסיונות חוזרים: +&Override default log path: +&דרוס נתיב ברירת מחדל של יומן: -Delay (in seconds): -השהייה (בשניות): - -Run a command after synchronization: -הרץ פקודה לאחר סינכרון: +Run a command: +הפעל פקודה: OK אשר @@ -1195,6 +1225,9 @@ The command is triggered if: Directory on server: מחיצה על שרת: +Access timeout (in seconds): +זמן קצוב לתפוגה (בשניות): + SFTP channels per connection: ערוצי SFTP לחיבור: @@ -1273,12 +1306,6 @@ The command is triggered if: Stop synchronization at first error עצור סנכרון עם הופעת שגיאה ראשונה -Save log: -שמור יומן: - -Limit number of log files: -הגבל מספר של קבצי יומן: - How can I schedule a batch job? כיצד לתזמן משימת אצווה? @@ -1315,6 +1342,12 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again הראה שוב את כל הדיאלוגים והודאות האזהרה המוסתרים באופן קבוע +Default log path: +נתיב יומן ברירת מחדל: + +&Delete logs after x days: +&מחק יומנים לאחר x ימים: + Customize context menu: התאמה אישית של תפריט הקשר: @@ -1324,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. &Default &ברירת מחדל -Feedback and suggestions are welcome -משוב והצעות יתקבלו בברכה +Feedback and suggestions are welcome: +משוב והצעות יתקבלו בברכה: Home page דף הבית @@ -1351,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: קוד מקור נכתב ב- C++‎ באמצעות: -Published under the GNU General Public License -מפורסם במסגרת GNU General Public License +Published under the GNU General Public License: +פורסם תחת הרישיון הציבורי הכללי של GNU: Many thanks for localization: תודות עבור תרגום שפות: @@ -1408,6 +1441,9 @@ This guarantees a consistent state even in case of a serious error. Info מידע +No log entries +אין רשומות יומן + Select all בחר הכל @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. Overview מבט כללי +Swap sides +החלף צדדים + Show "%x" הראה "%x" @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files הראה קבצים שסוננו או לא נכללו זמנית -Save as default -שמור כברירת מחדל - Filter מסנן @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. בודק כשלון בתא מחזור עבור תיקייה %x. -The following XML elements could not be read: -לא ניתן לקרוא את רכיבי XML הבאים: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -קובץ תצורה %x אינו שלם. הרכיבים החסרים יוגדרו עם ערכי ברירת המחדל. - Prepare installation מכין התקנה 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 @@ Items differ in attributes only आइटम्स के केवल गुण ही भिन्न हैं। +The name %x is used by more than one item in the folder. +%x नाम का उपयोग फ़ोल्डर में एक से अधिक आइटम द्वारा किया गया है। + Resolving symbolic link %x %x प्रतीकात्मक कड़ी को हल किया जा रहा है @@ -187,9 +190,6 @@ File time tolerance फ़ाइल समय टॉलरेंस (छूट) -Folder access timeout -निर्देशिका पहुँच काल समापन - Run with background priority पृष्ठभूमि प्राथमिकता के साथ चलाएँ @@ -334,8 +334,11 @@ Update attributes on right दाईं तरफ के गुण अद्यतन करें -Warning -चेतावनी +Errors: +त्रुटियाँ: + +Warnings: +चेतावनियाँ: Items processed: संसाधित आइटम्स: @@ -346,6 +349,9 @@ Total time: कुल समय: +Warning +चेतावनी + Stopped रुका @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. फ़ाइल %x, पंक्ति %y, स्तंभ %z पदच्छेदन में त्रुटि। +Services +सेवाएँ + +Show All +सब दिखाओ + +Hide Others +अन्य छुपाएं + +Hide %x +%x छुपाएं + +Quit %x +%x छोड़ें + Cannot set directory locks for the following folders: निम्न निर्देशिकाओं के लिए निर्देशिका अवरोध सेट नहीं कर सकते: @@ -370,11 +391,11 @@ Cannot read directory %x. निर्देशिका %x को पढ़ा नहीं जा सकता। -/sec -/सेकंड +%x/sec +%x/सेकंड -%x items/sec -%x आइटम्स/सेकंड +%x items +%x आइटम्स Show in Explorer एक्सप्लोरर में दिखाएं @@ -523,11 +544,11 @@ Generating database... डेटाबेस बनाया जा रहा है... -Searching for excess file versions: -अतिरिक्त फ़ाइल संस्करणों की खोज हो रही है: +Searching for old file versions: +पुराने फ़ाइल संस्करणों की खोज हो रही है: -Removing excess file versions: -अतिरिक्त फ़ाइल संस्करणों को हटाया जा रहा है: +Removing old file versions: +पुराने फ़ाइल संस्करणों को हटाया जा रहा है: Unable to create time stamp for versioning: संस्करण के लिए समय मोहर बनाने में असमर्थ: @@ -585,6 +606,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. %x को रीसायकल बिन ले जाने में असमर्थ। +Unable to access %x. +%x तक पहुँचने में असमर्थ। + +Authentication completed. +प्रमाणन पूर्ण। + +Authentication failed. +प्रमाणन विफल। + +You may close this page now and continue with FreeFileSync. +आप अब इस पृष्ठ को बंद कर FreeFileSync जारी रख सकते हैं। + +The server returned an error: +सर्वर ने एक त्रुटि वापस कर दी: + +Cannot determine free disk space for %x. +%x के लिए खाली डिस्क जगह निर्धारित नहीं कर सकते। + Cannot find %x. %x नहीं मिला। @@ -597,18 +636,12 @@ Actual: %y bytes Cannot delete symbolic link %x. प्रतीकात्मक कड़ी %x हटाया नहीं जा सकता। -Cannot determine free disk space for %x. -%x के लिए खाली डिस्क जगह निर्धारित नहीं कर सकते। - Incorrect command line: अनुचित आदेश-पंक्ति: The server does not support authentication via %x. सर्वर पर %x द्वारा प्रमाणन समर्थित नहीं है। -Unable to access %x. -%x तक पहुँचने में असमर्थ। - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. SFTP चैनल संख्या %x को खोलने में विफल। - -1 byte -%x bytes - - -1 बाइट -%x बाइट्स - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop ड्रॅग एण्ड ड्रॉप @@ -764,6 +779,24 @@ The command is triggered if: &Retry पुनः प्रयास (&R) + +1 byte +%x bytes + + +1 बाइट +%x बाइट्स + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... लोड हो रहा है... @@ -909,7 +942,7 @@ The command is triggered if: बॅच जॉब के रूप में सहेजें (&b)... Show &log - +लॉग दिखाएं (&l) Start &comparison तुलना शुरू करें (&c) @@ -971,9 +1004,6 @@ The command is triggered if: Access online storage ऑनलाइन संग्रहण पहुँच प्राप्त करें -Swap sides -पक्ष गमागम करें - Close search bar खोज पट्टी बंद करें @@ -998,6 +1028,9 @@ The command is triggered if: View type: दृश्य प्रकार: +Save as default +डिफ़ॉल्ट के रूप में सहेजें + Select view: दृश्य चुनें: @@ -1055,6 +1088,15 @@ The command is triggered if: Handle daylight saving time दिवालोक बचत समय (डेलाईट सेविंग टाइम) प्रहस्तन करें +Ignore errors +त्रुटियों को अनदेखा करें + +Retry count: +पुनः प्रयास गणनांक: + +Delay (in seconds): +विलंब (सेकंड में): + Performance improvements: प्रदर्शन सुधार: @@ -1129,17 +1171,11 @@ The command is triggered if: Last x days: पिछले x दिन: -Ignore errors -त्रुटियों को अनदेखा करें - -Retry count: -पुनः प्रयास गणनांक: - -Delay (in seconds): -विलंब (सेकंड में): +&Override default log path: +डिफ़ॉल्ट लॉग पथ अधिभूत करें (&O): -Run a command after synchronization: -सिंक्रनाइज़ेशन के बाद कोई आदेश चलाएँ: +Run a command: +आदेश चलाएँ: OK ठीक @@ -1189,6 +1225,9 @@ The command is triggered if: Directory on server: सर्वर पर निर्देशिका: +Access timeout (in seconds): +पहुँच समय बाह्य (सेकंड्स में): + SFTP channels per connection: SFTP चैनल्स प्रति कनेक्शन: @@ -1267,12 +1306,6 @@ The command is triggered if: Stop synchronization at first error पहली त्रुटि पर सिंक्रनाइज़ेशन रोकें -Save log: -लॉग सहेजें: - -Limit number of log files: -लॉग फ़ाइलों की संख्या सीमित करें: - How can I schedule a batch job? मैं कोई बॅच कार्य कैसे अनुसूचित करूँ? @@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again स्थायी रूप से छिपाये संवाद बॉक्सेस और चेतावनी संदेश फिर से दिखाएं -Remove old log files after x days: - +Default log path: +डिफ़ॉल्ट लॉग पथ: + +&Delete logs after x days: +x दिनों के बाद लॉग हटाएं (&D): Customize context menu: प्रासंगिक मेनू अनुकूलित करें: @@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. &Default डिफ़ॉल्ट (&D) -Feedback and suggestions are welcome -प्रतिक्रिया और सुझावों का स्वागत है +Feedback and suggestions are welcome: +प्रतिक्रिया और सुझाव का स्वागत है: Home page मुख पृष्ठ @@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: स्रोत कोड C++ में लिखा गया इनके प्रयोग से: -Published under the GNU General Public License -जीएनयू जनरल पब्लिक लाइसेंस (GNU General Public License) के अंतर्गत प्रकाशित +Published under the GNU General Public License: +जीएनयू जनरल पब्लिक लाइसेंस (GNU General Public License) के तहत प्रकाशित: Many thanks for localization: स्थानीयकरण के लिए अनेक आभार: @@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error. जानकारी No log entries - +कोई लॉग प्रविष्टियां नहीं Select all सभी चुने @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. Overview अवलोकन +Swap sides +पक्ष गमागम करें + Show "%x" "%x" दिखाएं @@ -1553,7 +1592,7 @@ This guarantees a consistent state even in case of a serious error. नहीं सहेजें (&n) Hide configuration -कॉन्फ़िगरेशन छुपाएँ +कॉन्फ़िगरेशन छुपाएं Highlight... हाइलाइट... @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files फ़िल्टर किए या अस्थायी रूप से अपवर्जित फ़ाइल्स दिखाएं -Save as default -डिफ़ॉल्ट के रूप में सहेजें - Filter फ़िल्टर @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. निर्देशिका %x के लिए रीसायकल बिन की जाँच विफल। -The following XML elements could not be read: -निम्न XML तत्वों को पढा नहीं जा सका: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -कॉन्फ़िगरेशन फ़ाइल %x अधूरी है। अनुपस्थित तत्व उनके डिफ़ॉल्ट मानों से सेट होंगे। - Prepare installation स्थापना तैयारी 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 @@ Items differ in attributes only Az elemek csak attribútumaikban különböznek +The name %x is used by more than one item in the folder. +A %x nevet nem csak egy elem viseli a könyvtárban. + Resolving symbolic link %x %x szimbolikus hivatkozás feloldása @@ -187,9 +190,6 @@ File time tolerance Állományok időkülönbségének tűréshatára -Folder access timeout -Könyvtár hozzáférés időtúllépés - Run with background priority Futtatás háttér prioritással @@ -299,7 +299,7 @@ Mindkét oldal azonos Conflict/item cannot be categorized -Az ütközés vagy elem nem kategorizálható +Az ütközés/elem nem kategorizálható Copy new item to left Új elem másolása a bal oldalra @@ -320,10 +320,10 @@ Jobb oldali állomány mozgatása Update left item -Bal oldali elemek frissítése +Bal oldali elem frissítése Update right item -Jobb oldali elemek frissítése +Jobb oldali elem frissítése Do nothing Nincs mit végrehajtani @@ -334,8 +334,11 @@ Update attributes on right Attribútumok frissítése a jobb oldalon -Warning -Figyelmeztetés +Errors: +Hiba: + +Warnings: +Figyelmeztetés: Items processed: Feldolgozott elemek száma: @@ -346,6 +349,9 @@ Total time: Összes időszükséglet: +Warning +Figyelmeztetés + Stopped Leállítva @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Hiba történt a feldolgozás közben: %x állomány, %y sor, %z oszlop. +Services +Szervizek + +Show All +Összest Mutassa + +Hide Others +Rejtse a többit + +Hide %x +Rejt %x + +Quit %x +Leállít %x + Cannot set directory locks for the following folders: Nem lehet beállítani a könyvtárak lockolását a következő könyvtárakhoz: @@ -370,11 +391,11 @@ Cannot read directory %x. %x könyvtár nem olvasható. -/sec -/mp +%x/sec +%x/sec -%x items/sec -%x elem/mp +%x items +%x elem Show in Explorer Mutassa az Intézőben @@ -392,7 +413,7 @@ Sikeresen végrehajtva Completed with warnings -Figyelmeztetések mellett végrehajtva +Figyelmeztetéssel végrehajtva Completed with errors Hibák mellett végrehajtva @@ -401,7 +422,7 @@ Nem elérhető a Kötet Árnyék-másolat szolgáltatás. Please run the 64-bit version of FreeFileSync to create shadow copies on this system. -Árnyék másolatok készítéséhez ezen a rendszeren futtassa a FreeFileSync 64 bites verzióját. +Ezen a rendszeren árnyék másolatok készítéséhez futtassa a FreeFileSync 64 bites verzióját. Cannot determine volume name for %x. %x számára nem lehet a kötet-nevet meghatározni. @@ -488,7 +509,7 @@ Adja meg a célkönyvtárat a verziókövetéshez. The following items have unresolved conflicts and will not be synchronized: -A következő tételek feloldatlan ütközést tartalmaznak, így nem lesznek szinkronizálva: +A következő elemek feloldatlan ütközést tartalmaznak, így nem lesznek szinkronizálva: The following folders are significantly different. Please check that the correct folders are selected for synchronization. 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. @@ -523,11 +544,11 @@ Generating database... Adatbázis generálása... -Searching for excess file versions: -A felesleges fájl-verziók keresése: +Searching for old file versions: +Régi állomány-változatok keresése: -Removing excess file versions: -A felesleges fájl-verziók törlése: +Removing old file versions: +Régi állomány-változatok törlése: Unable to create time stamp for versioning: Nem képes időbélyegzés létrehozására a verzióképzéshez: @@ -585,6 +606,24 @@ Tényleges: %y bájt Unable to move %x to the recycle bin. %x nem helyezhető Lomtárba. +Unable to access %x. +%x nem elérhető. + +Authentication completed. +Hitelesítés befejezve. + +Authentication failed. +Hitelesítés sikertelen. + +You may close this page now and continue with FreeFileSync. +Most bezárhatja ezt az oldalt és folytathatja a FreeFileSync-et. + +The server returned an error: +A szerver hibát adott vissza: + +Cannot determine free disk space for %x. +%x-en nem határozható meg a szabad tárterület. + Cannot find %x. %x nem található. @@ -597,18 +636,12 @@ Tényleges: %y bájt Cannot delete symbolic link %x. %x szimbolikus hivatkozás nem törölhető. -Cannot determine free disk space for %x. -%x-en nem határozható meg a szabad tárterület. - Incorrect command line: Hibás parancssor: The server does not support authentication via %x. A szerver nem támogatja a %x általi autentikációt. -Unable to access %x. -%x nem elérhető. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Tényleges: %y bájt Failed to open SFTP channel number %x. Hiba a %x számú SFTP csatorna megnyitása során. - -1 byte -%x bytes - - -1 bájt -%x bájt - - -%x MB -%x MB - -%x KB -%x kB - -%x GB -%x GB - Drag && drop Húzd && Ejtsd @@ -764,6 +779,24 @@ A parancs végrehajtódik, ha: &Retry &Ismét + +1 byte +%x bytes + + +1 bájt +%x bájt + + +%x MB +%x MB + +%x KB +%x kB + +%x GB +%x GB + Loading... Tölti... @@ -846,7 +879,7 @@ A parancs végrehajtódik, ha: Relatív elérési útvonal Item name -Tétel neve +Elem neve Size Méret @@ -909,7 +942,7 @@ A parancs végrehajtódik, ha: Mentse &kötegelt feladatként... Show &log - +Mutassa a &logokat Start &comparison Kezdje az &összehasonlítást @@ -971,9 +1004,6 @@ A parancs végrehajtódik, ha: Access online storage Online tároló elérése -Swap sides -Cserélje fel az oldalakat - Close search bar Zárja be a keresési sávot @@ -998,6 +1028,9 @@ A parancs végrehajtódik, ha: View type: Nézet típusa: +Save as default +Mentse mint alapértelmezést + Select view: Válasszon nézetet: @@ -1055,6 +1088,15 @@ A parancs végrehajtódik, ha: Handle daylight saving time Kezelje a nyári időszámítás különbségét +Ignore errors +Hagyja figyelmen kívül a hibákat + +Retry count: +Visszatérések száma: + +Delay (in seconds): +Késés (másodpercben): + Performance improvements: Teljesítmény növelése: @@ -1129,17 +1171,11 @@ A parancs végrehajtódik, ha: Last x days: legutóbbi x nap: -Ignore errors -Hagyja figyelmen kívül a hibákat - -Retry count: -Visszatérések száma: - -Delay (in seconds): -Késés (másodpercben): +&Override default log path: +&Felülírja az alapértelmezett log útvonalat: -Run a command after synchronization: -Futtasson egy parancsot a szinkronizálás után: +Run a command: +Futtasson egy parancsot: OK OK @@ -1189,6 +1225,9 @@ A parancs végrehajtódik, ha: Directory on server: Könyvtár az alábbi szerveren: +Access timeout (in seconds): +Hozzáférési várakozási idő (másodpercben): + SFTP channels per connection: SFTP csatornák száma kapcsolatonként: @@ -1223,7 +1262,7 @@ A parancs végrehajtódik, ha: Bájt Items -Tétel +Elem Synchronizing... Szinkronizálás folyamatban... @@ -1267,12 +1306,6 @@ A parancs végrehajtódik, ha: Stop synchronization at first error Állítsa le a szinkronizálást az első hibánál -Save log: -Mentse a következő naplóállományt: - -Limit number of log files: -A log állományok számának korlátja: - How can I schedule a batch job? Hogyan tudok kötegelt feldolgozást ütemezni? @@ -1309,8 +1342,11 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Show all permanently hidden dialogs and warning messages again Mutassa újra az összes ideiglenesen rejtett párbeszédablakot és figyelmeztetést -Remove old log files after x days: - +Default log path: +Alapértelmezett logolási útvonal: + +&Delete logs after x days: +&Törölje a logokat x nap után: Customize context menu: Környezeti menü testreszabása: @@ -1321,8 +1357,8 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. &Default &Alapértelmezett -Feedback and suggestions are welcome -Várjuk a visszajelzéseket és az ötleteket +Feedback and suggestions are welcome: +Visszajelzést és javalatokat szívesen látjuk: Home page Honlap @@ -1337,7 +1373,7 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Ha szereted a FreeFileSync-et: Support with a donation -Támogassa adománnyal +Támogasd adománnyal The auto updater was disabled by the administrator. Az automatikus frissítést a rendszergazda kapcsolta ki. @@ -1348,8 +1384,8 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Source code written in C++ using: A programot C++-ban fejlesztették a következők felhasználásával: -Published under the GNU General Public License -Közzétéve a GNU General Public License alatt +Published under the GNU General Public License: +Közzétéve GNU GPL alatt: Many thanks for localization: Köszönet a lokalizációért: @@ -1406,7 +1442,7 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Információ No log entries - +Nincs log bejegyzés Select all Összeset kiválasztja @@ -1432,6 +1468,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Overview Áttekintés +Swap sides +Cserélje fel az oldalakat + Show "%x" "%x" mutatása @@ -1606,9 +1645,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Show filtered or temporarily excluded files Mutassa a szűrt vagy ideiglenesen kizárt állományokat -Save as default -Mentse mint alapértelmezést - Filter Szűrő @@ -1954,12 +1990,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. Checking recycle bin failed for folder %x. %x könyvtár vonatkozásában a lomtár ellenőrzése meghiúsult. -The following XML elements could not be read: -A következő XML elemek nem olvashatók: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -%x konfigurációs állomány nem teljes. A hiányzó elemeket az alapértelmezés szerinti értékekkel helyettesíti. - Prepare installation Telepítés előkészítése 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 @@ Items differ in attributes only Gli oggetti differiscono solo negli attributi +The name %x is used by more than one item in the folder. +Il nome %x è utilizzato da più di un elemento nella cartella. + Resolving symbolic link %x Risoluzione collegamento %x @@ -187,9 +190,6 @@ File time tolerance File tolleranza temporale -Folder access timeout -Timeout di accesso alle cartelle - Run with background priority Eseguire con priorità in background @@ -334,8 +334,11 @@ Update attributes on right Aggiorna attributi a destra -Warning -Attenzione +Errors: +Errori: + +Warnings: +Avvertenze: Items processed: Oggetti processati: @@ -346,6 +349,9 @@ Total time: Tempo totale: +Warning +Attenzione + Stopped Arrestato @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Errore nel parsing del file %x, riga %y, colonna %z. +Services +Servizi + +Show All +Mostra tutto + +Hide Others +Nascondi gli altri + +Hide %x +Nascondi %x + +Quit %x +Esci da %x + Cannot set directory locks for the following folders: Impossibile impostare i blocchi di directory per le seguenti cartelle: @@ -370,11 +391,11 @@ Cannot read directory %x. Impossibile leggere la directory %x. -/sec -/sec +%x/sec +%x/ sec -%x items/sec -%x oggetti/sec +%x items +%x articoli Show in Explorer Mostra in Esplora Risorse @@ -523,11 +544,11 @@ Generating database... Generazione database... -Searching for excess file versions: -Ricerca delle versioni di file in eccesso: +Searching for old file versions: +Ricerca di vecchie versioni di file: -Removing excess file versions: -Rimozione delle versioni di file in eccesso: +Removing old file versions: +Rimozione delle versioni precedenti dei file: Unable to create time stamp for versioning: Impossibile creare l'impronta per il controllo delle versioni: @@ -585,6 +606,24 @@ Attuale: %y byte Unable to move %x to the recycle bin. Impossibile spostare %x nel cestino. +Unable to access %x. +Impossibile accedere %x. + +Authentication completed. +Autenticazione completata. + +Authentication failed. +Autenticazione fallita. + +You may close this page now and continue with FreeFileSync. +Puoi chiudere questa pagina ora e continuare con FreeFileSync. + +The server returned an error: +Il server ha restituito un errore: + +Cannot determine free disk space for %x. +Impossibile determinare lo spazio libero su disco per %x. + Cannot find %x. Impossibile trovare %x. @@ -597,18 +636,12 @@ Attuale: %y byte Cannot delete symbolic link %x. Impossibile eliminare collegamento %x. -Cannot determine free disk space for %x. -Impossibile determinare lo spazio libero su disco per %x. - Incorrect command line: Linea di comando non corretta: The server does not support authentication via %x. Il server non supporta l'autenticazione tramite %x. -Unable to access %x. -Impossibile accedere %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Attuale: %y byte Failed to open SFTP channel number %x. Impossibile aprire canale SFTP numero %x. - -1 byte -%x bytes - - -1 byte -%x byte - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Trascina e rilascia @@ -764,6 +779,24 @@ Il comando è attivato se: &Retry &Riprova + +1 byte +%x bytes + + +1 byte +%x byte + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Caricamento... @@ -909,7 +942,7 @@ Il comando è attivato se: Salva come &processo batch... Show &log - +Mostrare &log Start &comparison Avvio &confronto @@ -971,9 +1004,6 @@ Il comando è attivato se: Access online storage Accesso all'archiviazione online -Swap sides -Inverti i lati - Close search bar Chiudi barra di ricerca @@ -998,6 +1028,9 @@ Il comando è attivato se: View type: Visualizza tipo: +Save as default +Salva come predefinito + Select view: Seleziona vista: @@ -1055,6 +1088,15 @@ Il comando è attivato se: Handle daylight saving time Maneggiare l'ora legale +Ignore errors +Ignora errori + +Retry count: +Riprova conteggio: + +Delay (in seconds): +Ritardo (in secondi): + Performance improvements: Miglioramenti delle prestazioni: @@ -1129,17 +1171,11 @@ Il comando è attivato se: Last x days: Ultimi x giorni: -Ignore errors -Ignora errori - -Retry count: -Riprova conteggio: - -Delay (in seconds): -Ritardo (in secondi): +&Override default log path: +&Sostituisci il percorso di registrazione predefinito: -Run a command after synchronization: -Esegui un comando dopo la sincronizzazione: +Run a command: +Esegui un comando: OK OK @@ -1189,6 +1225,9 @@ Il comando è attivato se: Directory on server: Directory su server: +Access timeout (in seconds): +Timeout di accesso (in secondi): + SFTP channels per connection: Canali SFTP per il collegamento: @@ -1267,12 +1306,6 @@ Il comando è attivato se: Stop synchronization at first error Interrompere la sincronizzazione al primo errore -Save log: -Salva log: - -Limit number of log files: -Limita il numero di file di registro: - How can I schedule a batch job? Come posso programmare un lavoro batch? @@ -1309,8 +1342,11 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Show all permanently hidden dialogs and warning messages again Mostra di nuovo tutti i dialoghi nascosti in modo permanente e i messaggi di allarme -Remove old log files after x days: - +Default log path: +Percorso di log predefinito: + +&Delete logs after x days: +&Elimina i log dopo x giorni: Customize context menu: Personalizzare menu contestuale: @@ -1321,8 +1357,8 @@ Questo garantisce uno stato consistente anche in caso di errore grave. &Default &Predefinito -Feedback and suggestions are welcome -Ogni commento o suggerimento è ben accetto +Feedback and suggestions are welcome: +Feedback e suggerimenti sono benvenuti: Home page Pagina Iniziale @@ -1348,8 +1384,8 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Source code written in C++ using: Codice sorgente scritto in C++ utilizzando: -Published under the GNU General Public License -Pubblicato con licenza GNU General Public +Published under the GNU General Public License: +Pubblicato sotto la GNU General Public License: Many thanks for localization: Ringraziamenti per la traduzione: @@ -1406,7 +1442,7 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Info No log entries - +Nessuna voce di registro Select all Seleziona tutto @@ -1432,6 +1468,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Overview Anteprima +Swap sides +Inverti i lati + Show "%x" Mostra "%x" @@ -1606,9 +1645,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Show filtered or temporarily excluded files Mostra file filtrati o temporaneamente esclusi -Save as default -Salva come predefinito - Filter Filtro @@ -1954,12 +1990,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave. Checking recycle bin failed for folder %x. Controllo cestino non riuscito per la cartella %x. -The following XML elements could not be read: -I seguenti elementi XML non possono essere letti: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -File di configurazione %x incompleta. Gli elementi mancanti saranno impostati sui valori predefiniti. - Prepare installation Preparare l'installazione 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 @@ 選択された構成を実行しないで編集用に開きます. Path to an alternate GlobalSettings.xml file. - +代替 GlobalSettings.xml ファイルへのパス. Installation files are corrupted. Please reinstall FreeFileSync. インストール ファイルが破損しています、FreeFileSync を再インストールしてください. @@ -159,6 +159,9 @@ Items differ in attributes only 属性のみ異なる項目 +The name %x is used by more than one item in the folder. +名前 %x は、フォルダ内複数の項目で使用されています. + Resolving symbolic link %x シンボリックリンク %x を解決中 @@ -186,9 +189,6 @@ File time tolerance ファイル時間の許容範囲 -Folder access timeout -フォルダ アクセスのタイムアウト - Run with background priority 優先度バックグラウンドで実行 @@ -332,8 +332,11 @@ Update attributes on right 右の属性を更新 -Warning -警告 +Errors: +エラー: + +Warnings: +警告: Items processed: 処理された要素: @@ -344,6 +347,9 @@ Total time: 合計時間: +Warning +警告 + Stopped 停止 @@ -353,6 +359,21 @@ Error parsing file %x, row %y, column %z. ファイル %x の構文解析エラー, 行 %y, 列 %z. +Services +サービス + +Show All +すべて表示 + +Hide Others +他を非表示 + +Hide %x +%x を非表示 + +Quit %x +%x を終了 + Cannot set directory locks for the following folders: 次のフォルダにあるディレクトリはロックできません: @@ -367,11 +388,11 @@ Cannot read directory %x. ディレクトリ %x を読み取れません. -/sec -/秒 +%x/sec +%x/秒 -%x items/sec -%x 項目/秒 +%x items +%x 項目 Show in Explorer エクスプローラで表示 @@ -520,11 +541,11 @@ Generating database... データベースを作成中... -Searching for excess file versions: -ファイルの余剰なバージョンを検索: +Searching for old file versions: +古いファイルバージョンの検索: -Removing excess file versions: -ファイルの余剰なバージョンを除去: +Removing old file versions: +古いファイルバージョンの削除: Unable to create time stamp for versioning: バージョン管理のタイムスタンプを作成できません: @@ -582,6 +603,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. %x をゴミ箱に移動できません. +Unable to access %x. +%x にアクセスできません. + +Authentication completed. +認証が完了しました. + +Authentication failed. +認証に失敗しました. + +You may close this page now and continue with FreeFileSync. +このページを閉じて FreeFileSync. を続行することができます. + +The server returned an error: +サーバはエラーを返しました: + +Cannot determine free disk space for %x. +%x の空きディスク領域を検出できません. + Cannot find %x. %x がみつかりません. @@ -594,18 +633,12 @@ Actual: %y bytes Cannot delete symbolic link %x. シンボリック リンク %x を削除できません. -Cannot determine free disk space for %x. -%x の空きディスク領域を検出できません. - Incorrect command line: 不正なコマンドライン: The server does not support authentication via %x. このサーバは %x による認証に対応していません. -Unable to access %x. -%x にアクセスできません. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -628,23 +661,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. SFTP チャンネル %x を開けません. - -1 byte -%x bytes - - -%x バイト - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop ドラッグ && ドロップ @@ -688,7 +704,7 @@ Actual: %y bytes 3. 'スタート'をクリック. To get started just import a "ffs_batch" file. - +"ffs_batch" をインポート後、すぐに開始. Folders to watch: 監視するフォルダ: @@ -758,6 +774,23 @@ The command is triggered if: &Retry 再試行(&R) + +1 byte +%x bytes + + +%x バイト + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... 読み込み中... @@ -902,7 +935,7 @@ The command is triggered if: 一括ジョブで保存(&B)... Show &log - +ログを表示(&L) Start &comparison 比較を開始(&C) @@ -964,9 +997,6 @@ The command is triggered if: Access online storage オンライン ストレージにアクセス -Swap sides -パネルを入れ替え - Close search bar 検索バーを閉じる @@ -991,6 +1021,9 @@ The command is triggered if: View type: 表示形式: +Save as default +既定として保存 + Select view: 選択表示: @@ -1048,6 +1081,15 @@ The command is triggered if: Handle daylight saving time 夏時間の取り扱い +Ignore errors +エラーを無視 + +Retry count: +再試行回数: + +Delay (in seconds): +遅延 (秒で指定): + Performance improvements: パフォーマンス向上: @@ -1122,17 +1164,11 @@ The command is triggered if: Last x days: x 日以降: -Ignore errors -エラーを無視 - -Retry count: -再試行回数: - -Delay (in seconds): -遅延 (秒で指定): +&Override default log path: +既定のログ保存パスに上書き(&Q): -Run a command after synchronization: -同期処理後に実行するコマンド: +Run a command: +コマンドを実行: OK OK @@ -1182,6 +1218,9 @@ The command is triggered if: Directory on server: サーバ上のディレクトリ: +Access timeout (in seconds): +アクセスのタイムアウト(秒): + SFTP channels per connection: 接続当たりの SFTP チャンネル数: @@ -1260,12 +1299,6 @@ The command is triggered if: Stop synchronization at first error 最初のエラーで同期処理を停止 -Save log: -ログ保存: - -Limit number of log files: -ログファイル数の制限: - How can I schedule a batch job? 一括ジョブ スケジュールの作成方法 @@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again 非表示にしたすべてのダイアログと警告メッセージを再表示 -Remove old log files after x days: - +Default log path: +既定のログ保存パス: + +&Delete logs after x days: +x 日経過後にログを削除(&D): Customize context menu: コンテキストメニューのカスタマイズ: @@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error. &Default デフォルト(&D) -Feedback and suggestions are welcome -フィードバック、提案などはこちらから +Feedback and suggestions are welcome: +フィードバック、提案はいつでも歓迎します: Home page ホーム ページ @@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: ソースコードは C++ で書かれています: -Published under the GNU General Public License -GNU 一般共有使用許諾に基づき公開 +Published under the GNU General Public License: +GNU 一般公衆ライセンスの下で公開されています: Many thanks for localization: ローカライズのご協力に感謝します: @@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error. 情報 No log entries - +ログ エントリなし Select all すべて選択 @@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error. Overview 概要 +Swap sides +パネルを入れ替え + Show "%x" "%x" で表示 @@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files フィルター済、または一時除外ファイルを表示 -Save as default -既定として保存 - Filter フィルター @@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. フォルダ %x のゴミ箱のチェックに失敗. -The following XML elements could not be read: -次の XML 要素は読み取ることができません: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -構成設定ファイル %x は不完全です。存在しない要素が既定値としてセットされています. - Prepare installation インストールの準備 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 @@ File %x has an invalid date. -파일 %x 의 날짜가 유효하지 않습니다. +파일 %x의 날짜가 유효하지 않습니다. Date: 날짜: @@ -159,6 +159,9 @@ Items differ in attributes only 항목들이 속성에서만 차이가 있습니다. +The name %x is used by more than one item in the folder. +이름 %x이(가) 같은 폴더 내 한 개 이상의 항목에서 사용되고 있습니다. + Resolving symbolic link %x 심볼릭 링크 %x 해결 @@ -186,9 +189,6 @@ File time tolerance 파일 시간 허용 -Folder access timeout -폴더 접근 제한 - Run with background priority 배경 우선 순위로 실행 @@ -332,8 +332,11 @@ Update attributes on right 우측 속성 업데이트 -Warning -경고 +Errors: +오류: + +Warnings: +경고: Items processed: 처리된 항목: @@ -344,6 +347,9 @@ Total time: 전체 시간: +Warning +경고 + Stopped 중단 @@ -353,6 +359,21 @@ Error parsing file %x, row %y, column %z. 분석 오류 - 파일: %x; 행: %y; 열: %z. +Services +서비스 + +Show All +모두 보이기 + +Hide Others +기타 다른 항목 숨기기 + +Hide %x +%x 숨기기 + +Quit %x +%x 끝내기 + Cannot set directory locks for the following folders: 다음 폴더의 디렉터리 잠금을 설정할 수 없습니다: @@ -367,10 +388,10 @@ Cannot read directory %x. 디렉터리 %x을(를) 읽을 수 없습니다. -/sec -/초 +%x/sec +%x/초 -%x items/sec +%x items %x 항목 Show in Explorer @@ -416,7 +437,7 @@ 파일 크기 Two way -양측 방향 (Two Way) +양측 방향 (투 웨이) Mirror 미러 @@ -520,11 +541,11 @@ Generating database... 데이터베이스 생성 중... -Searching for excess file versions: -초과 파일 버전 검색 중: +Searching for old file versions: +이전 파일 버전 검색 중: -Removing excess file versions: -초과 파일 버전 제거 중: +Removing old file versions: +이전 파일 버전 제거 중: Unable to create time stamp for versioning: 버전 관리를 위한 타임 스탬프 생성 불가: @@ -582,6 +603,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. 휴지통으로 %x을(를) 이동할 수 없습니다. +Unable to access %x. +%x에 접근할 수 없습니다. + +Authentication completed. +인증 완료. + +Authentication failed. +인증 실패. + +You may close this page now and continue with FreeFileSync. +지금 이 페이지를 닫고 FreeFileSync를 계속 진행할 수 있습니다. + +The server returned an error: +서버가 오류를 반환했습니다: + +Cannot determine free disk space for %x. +%x에 대한 사용 가능한 디스크 공간을 확인할 수 없습니다. + Cannot find %x. %x을(를) 찾을 수 없습니다. @@ -594,18 +633,12 @@ Actual: %y bytes Cannot delete symbolic link %x. 심볼릭 링크 %x을(를) 삭제할 수 없습니다. -Cannot determine free disk space for %x. -%x에 대한 사용 가능한 디스크 공간을 확인할 수 없습니다. - Incorrect command line: 부정확한 명령줄: The server does not support authentication via %x. 서버가 %x을(를) 통한 인증을 지원하지 않습니다. -Unable to access %x. -%x에 접근할 수 없습니다. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -628,23 +661,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. SFTP 채널번호 %x을(를) 열지 못 했습니다. - -1 byte -%x bytes - - -%x 바이트 - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop 끌어서 놓기(&&) [드래그-앤-드랍] @@ -758,6 +774,23 @@ The command is triggered if: &Retry 다시 시도(&R) + +1 byte +%x bytes + + +%x 바이트 + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... 로드 중... @@ -902,7 +935,7 @@ The command is triggered if: 일괄 작업으로 저장(&b)... Show &log - +로그 표시(&l) Start &comparison 비교 시작(&C) @@ -964,9 +997,6 @@ The command is triggered if: Access online storage 온라인 저장소 접속 -Swap sides -양측 위치 바꾸기 - Close search bar 검색 창 닫기 @@ -991,6 +1021,9 @@ The command is triggered if: View type: 유형 보기: +Save as default +기본 값으로 저장 + Select view: 보기 선택: @@ -1048,6 +1081,15 @@ The command is triggered if: Handle daylight saving time 서머타임 설정 +Ignore errors +오류 무시 + +Retry count: +재시도 횟수: + +Delay (in seconds): +지연 (초 단위): + Performance improvements: 성능 향상: @@ -1122,17 +1164,11 @@ The command is triggered if: Last x days: 최근 x일: -Ignore errors -오류 무시 - -Retry count: -재시도 횟수: - -Delay (in seconds): -지연 (초 단위): +&Override default log path: +기본 로그 경로 다시 정의(&O): -Run a command after synchronization: -동기화 이후 명령 실행: +Run a command: +명령 실행: OK 확인 @@ -1182,6 +1218,9 @@ The command is triggered if: Directory on server: 서버 디렉터리: +Access timeout (in seconds): +액세스 제한 시간(초): + SFTP channels per connection: 연결 당 SFTP 채널 수: @@ -1260,12 +1299,6 @@ The command is triggered if: Stop synchronization at first error 첫 오류 발생 시 동기화 중지 -Save log: -로그 저장: - -Limit number of log files: -로그 파일 개수 제한: - How can I schedule a batch job? 일괄 작업 예약 방법은? @@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again 영구적으로 숨겨진 모든 대화 상자 및 경고 메세지 다시 보이기 -Remove old log files after x days: - +Default log path: +기본 로그 경로: + +&Delete logs after x days: +x일 후에 로그 삭제(&D): Customize context menu: 컨텍스트 메뉴 커스터마이즈 (사용자 정의): @@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error. &Default 기본 설정/값(&D) -Feedback and suggestions are welcome -모든 의견 및 건의/제안을 환영합니다 +Feedback and suggestions are welcome: +모든 의견 및 제안을 환영합니다: Home page 홈페이지 @@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: 소스코드는 C++ 언어로 아래 툴을 사용하여 작성되었습니다: -Published under the GNU General Public License -GNU 일반 공용 라이센스에 의한 출시 +Published under the GNU General Public License: +GNU 일반 공용 라이센스에 의한 출시: Many thanks for localization: 현지화 작업에 깊은 감사 드립니다: @@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error. 정보 No log entries - +로그 항목 없음 Select all 모두 선택 @@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error. Overview 개요 +Swap sides +양측 위치 바꾸기 + Show "%x" "%x" 표시 @@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files 필터링 또는 일시적으로 제외된 파일 보이기 -Save as default -기본 값으로 저장 - Filter 필터 @@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. 폴더 %x에 관한 휴지통 실패 확인 중. -The following XML elements could not be read: -다음 XML 요소를 읽을 수 없습니다: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -구성 파일 %x이(가) 불완전합니다. 누락된 요소는 기본 값으로 설정됩니다. - Prepare installation 설치 준비 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 @@ Items differ in attributes only Elementai skiriasi tik atributais +The name %x is used by more than one item in the folder. +Šis pavadinimas %x yra naudojamas daugiau nei vieną kartą. + Resolving symbolic link %x Ieškoma simbolinės nuorodos %x @@ -188,9 +191,6 @@ File time tolerance Failo laiko nuokrypis -Folder access timeout -Pasibaigė skirtas laikas aplankui pasiekti - Run with background priority Vykdyti kaip aukštesnio prioriteto poprograme @@ -336,8 +336,11 @@ Update attributes on right Atnaujinti atributus dešinėje -Warning -Perspėjimas +Errors: +Klaidos: + +Warnings: +Įspėimai: Items processed: Elementų apdorota: @@ -348,6 +351,9 @@ Total time: Visas laikas: +Warning +Perspėjimas + Stopped Sustabdyta @@ -357,6 +363,21 @@ Error parsing file %x, row %y, column %z. Klaida trinant failą %x, eilė %y, stulpelis %z. +Services +Paslaugos + +Show All +Rodyti Visus + +Hide Others +Slėpti Kitus + +Hide %x +Slėpti %x + +Quit %x +Išeiti %x + Cannot set directory locks for the following folders: Negalima užrakinti šiuos aplankus kataloge: @@ -373,11 +394,11 @@ Cannot read directory %x. Negalima nuskaityti katalogo %x. -/sec -/sek. +%x/sec +%x/sek -%x items/sec -%x elementų/sek. +%x items +%x elementų Show in Explorer Rodyti naršyklėje @@ -526,11 +547,11 @@ Generating database... Sukuriama duomenų bazė... -Searching for excess file versions: -Perviršio failų versijos paieška: +Searching for old file versions: +Ieškoma senų failo versijų: -Removing excess file versions: -Perviršio failų versijos pašąlinimas: +Removing old file versions: +Pašalinamos senos failo versijos: Unable to create time stamp for versioning: Nepavyko sukurti versijos laiko žymą: @@ -588,6 +609,24 @@ Esamas: %y baitai Unable to move %x to the recycle bin. %x į šiukšliadėžę perkelti nepavyko. +Unable to access %x. +Nepavyko prisijungti %x. + +Authentication completed. +Autentifikavimas baigtas. + +Authentication failed. +Nepavyko autentifikuoti. + +You may close this page now and continue with FreeFileSync. +Jūs dabar galite uždaryti šį langą ir testi FreeFileSync. + +The server returned an error: +Serveris gražino klaidą: + +Cannot determine free disk space for %x. +Negalima nustatyti laisvos disko dalies %x. + Cannot find %x. Negalima surasti %x. @@ -600,18 +639,12 @@ Esamas: %y baitai Cannot delete symbolic link %x. Negalima ištrinti virtualios nuorodos %x. -Cannot determine free disk space for %x. -Negalima nustatyti laisvos disko dalies %x. - Incorrect command line: Netaisyklinga Komandinė eilutė: The server does not support authentication via %x. Serveris nepalaiko %x autentifikavimo. -Unable to access %x. -Nepavyko prisijungti %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -638,25 +671,6 @@ Esamas: %y baitai Failed to open SFTP channel number %x. Nepavyko atidaryti SFTP kanalo %x. - -1 byte -%x bytes - - -%x baitas -%x baitai -%x baitų - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Vilkti && Numesti @@ -770,6 +784,25 @@ Komanda inicijuojama jei: &Retry &Bandyti vėl + +1 byte +%x bytes + + +%x baitas +%x baitai +%x baitų + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Įkraunama... @@ -916,7 +949,7 @@ Komanda inicijuojama jei: Išsaugoti kaip &užduočių paketą... Show &log - +Rodyti &žurnalą Start &comparison Pradėti &palyginimą @@ -978,9 +1011,6 @@ Komanda inicijuojama jei: Access online storage Internetinės saugyklos prieiga -Swap sides -Sukeisti puses - Close search bar Uždaryti paieškos įrankinę @@ -1005,6 +1035,9 @@ Komanda inicijuojama jei: View type: Rodymo būdai: +Save as default +Išsaugoti kaip pagrindinį + Select view: Pasirinkti rodymo būdą: @@ -1062,6 +1095,15 @@ Komanda inicijuojama jei: Handle daylight saving time Naudoti vasaros laiką +Ignore errors +Ignoruoti klaidas + +Retry count: +Skaičiuoti is naujo: + +Delay (in seconds): +Uždelsimas (sekundėmis): + Performance improvements: Veiklos gerinimai: @@ -1136,17 +1178,11 @@ Komanda inicijuojama jei: Last x days: Paskutinių x dienų: -Ignore errors -Ignoruoti klaidas - -Retry count: -Skaičiuoti is naujo: - -Delay (in seconds): -Uždelsimas (sekundėmis): +&Override default log path: +&Pakeisti pradinę žurnalo nuorodą: -Run a command after synchronization: -Po sinchronizavimo vykdyti komandą: +Run a command: +Vykdyti komandą: OK Gerai @@ -1196,6 +1232,9 @@ Komanda inicijuojama jei: Directory on server: Katalogas serveryje: +Access timeout (in seconds): +Prieigos laikas pasibaigs (sekundės): + SFTP channels per connection: SFTP kanalai per jungtį: @@ -1274,12 +1313,6 @@ Komanda inicijuojama jei: Stop synchronization at first error Stabdyti suvienodinimą, pirmai klaidai įvykus -Save log: -Išsaugoti žurnalą: - -Limit number of log files: -Žurnalinių failų riba: - How can I schedule a batch job? Kaip aš galiu užduočių paketą įtraukti į tvarkaraštį? @@ -1316,8 +1349,11 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Show all permanently hidden dialogs and warning messages again Vėl rodyti visus paslėptus dialogo langus ir įspėjamuosius pranešimus -Remove old log files after x days: - +Default log path: +Pradinė žurnalo nuoroda: + +&Delete logs after x days: +&Ištrinti žurnalo įrašus po x dienų: Customize context menu: Pagrindinio meniu pasirinkimai: @@ -1328,8 +1364,8 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. &Default &Numatyta -Feedback and suggestions are welcome -Nuomonė ir patarimai laukiami +Feedback and suggestions are welcome: +Nuomonės ir pasiūlymai yra laukiami: Home page Pagrindinis puslapis @@ -1355,8 +1391,8 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Source code written in C++ using: Šaltinio kodas parašytas su C++ naudojant: -Published under the GNU General Public License -Platinama su GNU General Public licenzija +Published under the GNU General Public License: +Publikuota naudojant GNU General Public License: Many thanks for localization: Labai dėkojame už vertimą: @@ -1413,7 +1449,7 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Informacija No log entries - +Nėra žurnalo įrašų Select all Pažymėti visus @@ -1439,6 +1475,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Overview Apžvalga +Swap sides +Sukeisti puses + Show "%x" Rodyti "%x" @@ -1617,9 +1656,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Show filtered or temporarily excluded files Rodyti išfiltruotus ar laikinai išskirtus failus -Save as default -Išsaugoti kaip pagrindinį - Filter Filtras @@ -1970,12 +2006,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. Checking recycle bin failed for folder %x. Tikrinama šiukšliadėžė. Failas %x nerastas. -The following XML elements could not be read: -Šie XML elementai yra neperskaitomi: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Konfiguracinis failas %x yra neužbaigtas. Trūkstamiems elementams bus priskirtos pradinės rekšmės. - Prepare installation Ruošiamasi įdiegimui 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 @@ n == 1 ? 0 : 1 -No log entries - - -Remove old log files after x days: - - -Show &log - - Both sides have changed since last synchronization. Begge sider er endret siden siste synkronisering. @@ -169,6 +160,9 @@ Items differ in attributes only Elementene har kun forskjellige attributter +The name %x is used by more than one item in the folder. +Navnet %x brukes av mer enn ett element i mappen. + Resolving symbolic link %x Løser symbolsk forbindelse %x @@ -196,9 +190,6 @@ File time tolerance Filtid toleranse -Folder access timeout -Tidsavbrudd for mappetilgang - Run with background priority Kjør med bakgrunns-prioritet @@ -343,8 +334,11 @@ Update attributes on right Oppdater attributter til høyre -Warning -Advarsel +Errors: +Feil: + +Warnings: +Advarsler: Items processed: Elementer behandlet: @@ -355,6 +349,9 @@ Total time: Samlet tid: +Warning +Advarsel + Stopped Stoppet @@ -364,6 +361,21 @@ Error parsing file %x, row %y, column %z. Behandlingsfeil i filen %x, rad %y, kolonne %z. +Services +Tjenester + +Show All +Vis alt + +Hide Others +Skjul andre + +Hide %x +Skjul %x + +Quit %x +Avslutt %x + Cannot set directory locks for the following folders: Kan ikke angi kataloglås for følgende mapper: @@ -379,11 +391,11 @@ Cannot read directory %x. Kan ikke lese katalogen %x. -/sec -/sek +%x/sec +%x/sek -%x items/sec -%x elementer/sek +%x items +%x elementer Show in Explorer Vis i Explorer @@ -532,11 +544,11 @@ Generating database... Lager database... -Searching for excess file versions: -Søker etter overskytende filversjoner: +Searching for old file versions: +Leter etter gamle filversjoner: -Removing excess file versions: -Fjerne overskytende filversjoner: +Removing old file versions: +Fjerner gamle filversjoner: Unable to create time stamp for versioning: Kan ikke opprette tidsstempel til versjonen: @@ -594,6 +606,24 @@ Faktisk: %y bytes Unable to move %x to the recycle bin. Kunne ikke flytte %x til papirkurven. +Unable to access %x. +Får ikke tilgang til %x. + +Authentication completed. +Autentisering er ferdig. + +Authentication failed. +Autentisering mislyktes. + +You may close this page now and continue with FreeFileSync. +Du kan lukke denne siden nå og fortsette med FreeFileSync. + +The server returned an error: +Serveren returnerte en feilmelding: + +Cannot determine free disk space for %x. +Kan ikke fastslå ledig diskplass for %x. + Cannot find %x. Kan ikke finne %x. @@ -606,18 +636,12 @@ Faktisk: %y bytes Cannot delete symbolic link %x. Kan ikke slette symbolsk lenke %x. -Cannot determine free disk space for %x. -Kan ikke fastslå ledig diskplass for %x. - Incorrect command line: Ugyldig kommando: The server does not support authentication via %x. Tjeneren støtter ikke autentisering via %x. -Unable to access %x. -Får ikke tilgang til %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -642,24 +666,6 @@ Faktisk: %y bytes Failed to open SFTP channel number %x. Kunne ikke åpne SFTP kanalnummer %x. - -1 byte -%x bytes - - -1 byte -%x byte - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Dra && slipp @@ -773,6 +779,24 @@ Kommandoen utføres hvis: &Retry &Prøv igjen + +1 byte +%x bytes + + +1 byte +%x byte + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Laster... @@ -917,6 +941,9 @@ Kommandoen utføres hvis: Save as &batch job... Lagre som &batchfil... +Show &log +Vis &logg + Start &comparison Start &sammenligning @@ -977,9 +1004,6 @@ Kommandoen utføres hvis: Access online storage Få tilgang til nettlagring -Swap sides -Bytt sider - Close search bar Lukk søkelinja @@ -1004,6 +1028,9 @@ Kommandoen utføres hvis: View type: Visning: +Save as default +Lagre som standard + Select view: Velg visning: @@ -1061,6 +1088,15 @@ Kommandoen utføres hvis: Handle daylight saving time Behandle sommertid +Ignore errors +Ignorér feil + +Retry count: +Antall forsøk: + +Delay (in seconds): +Forsinkelse (i sekunder): + Performance improvements: Ytelsesforbedringer: @@ -1135,17 +1171,11 @@ Kommandoen utføres hvis: Last x days: Siste x dager: -Ignore errors -Ignorér feil - -Retry count: -Antall forsøk: +&Override default log path: +&Velg en annen loggbane: -Delay (in seconds): -Forsinkelse (i sekunder): - -Run a command after synchronization: -Kjør en kommando etter synkronisering: +Run a command: +Kjør en kommando: OK OK @@ -1195,6 +1225,9 @@ Kommandoen utføres hvis: Directory on server: Katalog på server: +Access timeout (in seconds): +Få tilgang til tidsavbrudd (i sekunder): + SFTP channels per connection: SFTP kanaler per tilkobling: @@ -1273,12 +1306,6 @@ Kommandoen utføres hvis: Stop synchronization at first error Stopp synkronisering ved første feil -Save log: -Lagre logg: - -Limit number of log files: -Begrens antall loggfiler: - How can I schedule a batch job? Hvordan kan jeg lage en batchfil? @@ -1315,6 +1342,12 @@ Sikrer prosessen ved alvorlige feil. Show all permanently hidden dialogs and warning messages again Vis alle skjulte vinduer og advarsler igjen +Default log path: +Standard loggbane: + +&Delete logs after x days: +&Slett logger etter x dager: + Customize context menu: Tilpass kontekst-meny: @@ -1324,8 +1357,8 @@ Sikrer prosessen ved alvorlige feil. &Default &Standard -Feedback and suggestions are welcome -Tilbakemeldinger og forslag er ønsket +Feedback and suggestions are welcome: +Tilbakemelding og forslag er velkomne: Home page Hjemmeside @@ -1351,8 +1384,8 @@ Sikrer prosessen ved alvorlige feil. Source code written in C++ using: Kildekoden er skrevet i C++ med hjelp fra: -Published under the GNU General Public License -Utgitt under GNU General Public Licence +Published under the GNU General Public License: +Publisert under GNU General Public License: Many thanks for localization: Takk for oversettelse: @@ -1408,6 +1441,9 @@ Sikrer prosessen ved alvorlige feil. Info Info +No log entries +Ingen loggfiler + Select all Velg alt @@ -1432,6 +1468,9 @@ Sikrer prosessen ved alvorlige feil. Overview Oversikt +Swap sides +Bytt sider + Show "%x" Vis "%x" @@ -1606,9 +1645,6 @@ Sikrer prosessen ved alvorlige feil. Show filtered or temporarily excluded files Vis filtrerte eller midlertidig ekskluderte filer -Save as default -Lagre som standard - Filter Filter @@ -1954,12 +1990,6 @@ Sikrer prosessen ved alvorlige feil. Checking recycle bin failed for folder %x. Det å sjekke papirkurven gikk galt for mappen %x. -The following XML elements could not be read: -De følgende XML-elementer kunne ikke leses: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Konfigurasjonsfil %x er ufullstendig. De manglende elementene vil bli satt til standardverdiene. - Prepare installation Forbered installering 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 @@ Items differ in attributes only Elementy różnią się wyłącznie atrybutami +The name %x is used by more than one item in the folder. +Nazwa %x jest używana przez więcej niż jeden element w folderze. + Resolving symbolic link %x Rozwiązywanie dowiązania symbolicznego %x @@ -188,9 +191,6 @@ File time tolerance Tolerancja czasu dla pliku -Folder access timeout -Limit czasu odpowiedzi podczas dostępu do katalogu - Run with background priority Uruchom w tle z priorytetem @@ -336,8 +336,11 @@ Update attributes on right Aktualizuj atrybuty po prawej stronie -Warning -Ostrzeżenie +Errors: +Błędy: + +Warnings: +Ostrzeżenia: Items processed: Przetworzone elementy: @@ -348,6 +351,9 @@ Total time: Całkowity czas: +Warning +Ostrzeżenie + Stopped Zatrzymana @@ -357,6 +363,21 @@ Error parsing file %x, row %y, column %z. Błąd podczas parsowania pliku %x, rząd %y, kolumna %z. +Services +Usługi + +Show All +Pokaż Wszystko + +Hide Others +Ukryj Inne + +Hide %x +Ukryj %x + +Quit %x +Zamknij %x + Cannot set directory locks for the following folders: Nie można zablokować katalogów dla poniższych folderów: @@ -373,11 +394,11 @@ Cannot read directory %x. Nie można odczytać katalogu %x. -/sec -/sekundę +%x/sec +%x/sek -%x items/sec -%x elementów/sek +%x items +%x elementów Show in Explorer Wyświetl w Eksploratorze @@ -526,11 +547,11 @@ Generating database... Generowanie bazy danych... -Searching for excess file versions: -Wyszukiwanie nadmiarowych wersji plików: +Searching for old file versions: +Wyszukiwanie starych wersji plików: -Removing excess file versions: -Usuwanie nadmiarowych wersji plików: +Removing old file versions: +Usuwanie starych wersji plików: Unable to create time stamp for versioning: Nie można utworzyć znacznika czasu dla wersjonowania: @@ -588,6 +609,24 @@ Przesłany: %y bajtów Unable to move %x to the recycle bin. Nie można przenieść %x do kosza. +Unable to access %x. +Brak dostępu do %x. + +Authentication completed. +Uwierzytelnianie zakończone. + +Authentication failed. +Uwierzytelnianie nie powiodło się. + +You may close this page now and continue with FreeFileSync. +Możesz teraz zamknąć tę stronę i kontynuować FreeFileSync. + +The server returned an error: +Serwer zwrócił błąd: + +Cannot determine free disk space for %x. +Nie można określić wolnego miejsca na %x. + Cannot find %x. Nie można odnaleźć %x. @@ -600,18 +639,12 @@ Przesłany: %y bajtów Cannot delete symbolic link %x. Nie można usunąć dowiązania symbolicznego %x. -Cannot determine free disk space for %x. -Nie można określić wolnego miejsca na %x. - Incorrect command line: Niepoprawne polecenie: The server does not support authentication via %x. Serwer nie wspiera uwierzytelniania przez %x. -Unable to access %x. -Brak dostępu do %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -638,25 +671,6 @@ Przesłany: %y bajtów Failed to open SFTP channel number %x. Nie można otworzyć kanału SFTP numer %x. - -1 byte -%x bytes - - -1 bajt -%x bajty -%x bajtów - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Drag && Drop @@ -770,6 +784,25 @@ Komenda jest wykonywana gdy: &Retry &Powtórz + +1 byte +%x bytes + + +1 bajt +%x bajty +%x bajtów + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Wczytywanie... @@ -916,7 +949,7 @@ Komenda jest wykonywana gdy: Zapisz w trybie &wsadowym... Show &log - +Pokaż &log Start &comparison Rozpo&cznij porównywanie @@ -978,9 +1011,6 @@ Komenda jest wykonywana gdy: Access online storage Dostęp do zasobu zdalnego -Swap sides -Zamień stronami - Close search bar Zamknij pasek wyszukiwania @@ -1005,6 +1035,9 @@ Komenda jest wykonywana gdy: View type: Typ widoku: +Save as default +Zapisz jako domyślne + Select view: Widok: @@ -1062,6 +1095,15 @@ Komenda jest wykonywana gdy: Handle daylight saving time Uwzględniaj przesunięcie czasu +Ignore errors +Ignoruj błędy + +Retry count: +Liczba prób: + +Delay (in seconds): +Opóźnienie (w sekundach): + Performance improvements: Polepszenie wydajności: @@ -1136,17 +1178,11 @@ Komenda jest wykonywana gdy: Last x days: Ostatnie x dni: -Ignore errors -Ignoruj błędy - -Retry count: -Liczba prób: - -Delay (in seconds): -Opóźnienie (w sekundach): +&Override default log path: +&Zastąp domyślną ścieżkę logu: -Run a command after synchronization: -Uruchom komendę po zakończonej synchronizacji: +Run a command: +Uruchom polecenie: OK OK @@ -1196,6 +1232,9 @@ Komenda jest wykonywana gdy: Directory on server: Katalog na serwerze: +Access timeout (in seconds): +Czas oczekiwania na dostęp (w sekundach): + SFTP channels per connection: Liczba kanałów dla połączenia SFTP: @@ -1274,12 +1313,6 @@ Komenda jest wykonywana gdy: Stop synchronization at first error Przerwij synchronizację przy pierwszym błędzie -Save log: -Zapisz logi: - -Limit number of log files: -Ogranicz liczbę plików logów: - How can I schedule a batch job? Jak zaplanować zadanie w trybie wsadowym? @@ -1316,8 +1349,11 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Show all permanently hidden dialogs and warning messages again Przywróć wszystkie, stale ukryte dialogi i powiadomienia -Remove old log files after x days: - +Default log path: +Domyślna ścieżka logu: + +&Delete logs after x days: +&Skasuj logi po x dniach: Customize context menu: Dostosuj menu kontekstowe: @@ -1328,11 +1364,11 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp &Default &Domyślne -Feedback and suggestions are welcome -Wszelkie opinie i sugestie mile widziane +Feedback and suggestions are welcome: +Opinie i sugestie mile widziane: Home page -Strona domowa: +Strona domowa FreeFileSync Forum Forum FreeFileSync @@ -1355,8 +1391,8 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Source code written in C++ using: Kod stworzony w C++ z wykorzystaniem: -Published under the GNU General Public License -Udostępnione na zasadach licencji GNU General Public License +Published under the GNU General Public License: +Opublikowano na licencji GNU General Public Licence: Many thanks for localization: Podziękowania za tłumaczenia: @@ -1413,7 +1449,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Info No log entries - +Brak wpisów w dzienniku Select all Zaznacz wszystko @@ -1439,6 +1475,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Overview Przegląd +Swap sides +Zamień stronami + Show "%x" Pokaż "%x" @@ -1617,9 +1656,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Show filtered or temporarily excluded files Pokaż pliki wyfiltrowane lub wykluczone tymczasowo -Save as default -Zapisz jako domyślne - Filter Filtr @@ -1970,12 +2006,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp Checking recycle bin failed for folder %x. Sprawdzanie kosza systemowego dla katalogu %x zakończone niepowodzeniem. -The following XML elements could not be read: -Poniższy element XML nie może zostać odczytany: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Plik konfiguracyjny %x jest niekompletny. Zostaną ustawione domyślne wartości dla brakujących elementów. - Prepare installation Przygotuj instalację 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 @@ Items differ in attributes only Itens diferem apenas nos atributos +The name %x is used by more than one item in the folder. +O nome %x é usado por mais de um item na pasta. + Resolving symbolic link %x Resolver link simbólico %x @@ -187,9 +190,6 @@ File time tolerance Tolerância de tempo de ficheiro -Folder access timeout -Tempo limite de acesso à pasta - Run with background priority Correr com prioridade de fundo @@ -334,8 +334,11 @@ Update attributes on right Actualizar atributos à direita -Warning -Atenção +Errors: +Erros: + +Warnings: +Avisos: Items processed: Elementos processados: @@ -346,6 +349,9 @@ Total time: Tempo total: +Warning +Atenção + Stopped Parado @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Erro ao analisar ficheiro %x, linha %y, coluna %z. +Services +Serviços + +Show All +Mostrar Todos + +Hide Others +Ocultar Outros + +Hide %x +Ocultar %x + +Quit %x +Cerrar o %x + Cannot set directory locks for the following folders: Incapaz de definir bloqueios de directórios para as seguintes pastas: @@ -370,11 +391,11 @@ Cannot read directory %x. Não é possível ler o directório %x. -/sec -/seg +%x/sec +%x/seg. -%x items/sec -%x itens/seg +%x items +%x itens Show in Explorer Mostrar no Explorer @@ -523,11 +544,11 @@ Generating database... A gerar base de dados... -Searching for excess file versions: -A pesquisar por versões em excesso de ficheiro: +Searching for old file versions: +A pesquisar por versões antigas do ficheiro: -Removing excess file versions: -A remover versões em excesso de ficheiro: +Removing old file versions: +A remover versões antigas do ficheiro: Unable to create time stamp for versioning: Não é possível criar data/hora para controlo de versões: @@ -585,6 +606,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. Não é possível mover %x para a reciclagem. +Unable to access %x. +Incapaz de aceder %x. + +Authentication completed. +Autenticação completada. + +Authentication failed. +Falha na autenticação. + +You may close this page now and continue with FreeFileSync. +Você pode cerrar esta página agora e continuar com o FreeFileSync. + +The server returned an error: +O servidor retornou um erro: + +Cannot determine free disk space for %x. +Não é possível determinar o espaço livre no disco %x. + Cannot find %x. Não é possível encontrar %x. @@ -597,18 +636,12 @@ Actual: %y bytes Cannot delete symbolic link %x. Não é possível remover o link simbólico %x. -Cannot determine free disk space for %x. -Não é possível determinar o espaço livre no disco %x. - Incorrect command line: Linha de comandos incorrecta: The server does not support authentication via %x. O servidor não suporta autenticação via %x. -Unable to access %x. -Incapaz de aceder %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. Falha ao abrir canal SFTP número %x. - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Arrastar && Largar @@ -764,6 +779,24 @@ O comando é executado se: &Retry &Tentar de Novo + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... A carregar... @@ -909,7 +942,7 @@ O comando é executado se: Guardar como &batch... Show &log - +Mostrar ®isto Start &comparison Iniciar &comparação @@ -971,9 +1004,6 @@ O comando é executado se: Access online storage Aceder armazenagem local -Swap sides -Trocar lados - Close search bar Fechar barra de pesquisa @@ -998,6 +1028,9 @@ O comando é executado se: View type: Tipo de vista: +Save as default +Guardar como padrão + Select view: Seleccionar vista: @@ -1055,6 +1088,15 @@ O comando é executado se: Handle daylight saving time Lidar com horário de verão +Ignore errors +Ignorar erros + +Retry count: +Nº de tentativas: + +Delay (in seconds): +Atraso (em segundos): + Performance improvements: Melhorias de desempenho: @@ -1129,17 +1171,11 @@ O comando é executado se: Last x days: Últimos x dias: -Ignore errors -Ignorar erros - -Retry count: -Nº de tentativas: - -Delay (in seconds): -Atraso (em segundos): +&Override default log path: +&Substituir caminho padrão do registo: -Run a command after synchronization: -Executar um comando após a sincronização: +Run a command: +Executar um comando: OK OK @@ -1189,6 +1225,9 @@ O comando é executado se: Directory on server: Directório no servidor: +Access timeout (in seconds): +Tempo limite de acesso (em segundos): + SFTP channels per connection: Canais SFTP por conexão: @@ -1267,12 +1306,6 @@ O comando é executado se: Stop synchronization at first error Para sincronização ao primeiro erro -Save log: -Guardar registo: - -Limit number of log files: -Limitar número de ficheiros de registo: - How can I schedule a batch job? Como posso agendar um trabalho batch? @@ -1309,8 +1342,11 @@ Isto garante um estado consistente mesmo em caso de falha grave. Show all permanently hidden dialogs and warning messages again Mostrar todos os diálogos escondidos permanentemente e mensagens de aviso novamente -Remove old log files after x days: - +Default log path: +Caminho padrão do registo: + +&Delete logs after x days: +&Eliminar registos após X dias: Customize context menu: Personalizar menu de contexto: @@ -1321,8 +1357,8 @@ Isto garante um estado consistente mesmo em caso de falha grave. &Default &Config. Iniciais -Feedback and suggestions are welcome -Comentários e sugestões são apreciados +Feedback and suggestions are welcome: +Comentários e sugestões são bem-vindos: Home page Sítio da web @@ -1348,8 +1384,8 @@ Isto garante um estado consistente mesmo em caso de falha grave. Source code written in C++ using: Código fonte escrito em C++ utilizando: -Published under the GNU General Public License -Publicado sobre GNU General Public License +Published under the GNU General Public License: +Publicado sob a Licença Pública Geral GNU: Many thanks for localization: Muito obrigado pela localização: @@ -1406,7 +1442,7 @@ Isto garante um estado consistente mesmo em caso de falha grave. Info No log entries - +Nenhuma entrada no registo Select all Seleccionar tudo @@ -1432,6 +1468,9 @@ Isto garante um estado consistente mesmo em caso de falha grave. Overview Vista +Swap sides +Trocar lados + Show "%x" Mostrar "%x" @@ -1606,9 +1645,6 @@ Isto garante um estado consistente mesmo em caso de falha grave. Show filtered or temporarily excluded files Mostrar ficheiros filtrados ou temporariamente excluídos -Save as default -Guardar como padrão - Filter Filtro @@ -1954,12 +1990,6 @@ Isto garante um estado consistente mesmo em caso de falha grave. Checking recycle bin failed for folder %x. Verificar a reciclagem falhou no directório %x. -The following XML elements could not be read: -Os seguintes elementos XML não puderam ser lidos: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -O ficheiro de instalação %x está incompleto. Os elementos em falta serão definidos para seus valores iniciais. - Prepare installation A preparar a instalação 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 @@ Items differ in attributes only Os itens diferem apenas nos atributos +The name %x is used by more than one item in the folder. +O nome %x é usado por mais de um item na pasta. + Resolving symbolic link %x Resolvendo link simbólico %x @@ -187,9 +190,6 @@ File time tolerance Tolerância de tempo do arquivo -Folder access timeout -Tempo limite de acesso da pasta - Run with background priority Executar com prioridade de segundo plano @@ -334,8 +334,11 @@ Update attributes on right Atualizar atributos à direita -Warning -Aviso +Errors: +Erros: + +Warnings: +Avisos: Items processed: Elementos processados: @@ -346,8 +349,11 @@ Total time: Tempo total: +Warning +Aviso + Stopped -Interrompido +Parado Cleaning up log files: Limpando arquivos de log: @@ -355,8 +361,23 @@ Error parsing file %x, row %y, column %z. Erro analisando o arquivo %x, linha %y, coluna %z. +Services +Serviços + +Show All +Mostrar Todos + +Hide Others +Ocultar Outros + +Hide %x +Ocultar %x + +Quit %x +Sair %x + Cannot set directory locks for the following folders: -Não é possível estabelecer bloqueio de diretório para as seguintes pastas: +Não é possível definir bloqueios de diretório para as seguintes pastas: 1 thread @@ -370,11 +391,11 @@ Cannot read directory %x. Não é possível ler o diretório %x. -/sec -/s +%x/sec +%x/s -%x items/sec -%x itens/s +%x items +%x itens Show in Explorer Mostrar no Explorer @@ -523,11 +544,11 @@ Generating database... Gerando banco de dados... -Searching for excess file versions: -Procurando por versões de arquivos em excesso: +Searching for old file versions: +Procurando por versões antigas de arquivos: -Removing excess file versions: -Removendo versões de arquivos em excesso: +Removing old file versions: +Removendo versões antigas de arquivos: Unable to create time stamp for versioning: Não é possível criar a estampa de tempo para o controle de versões: @@ -585,6 +606,24 @@ Atual: %y bytes Unable to move %x to the recycle bin. Não é possível mover %x para a Lixeira. +Unable to access %x. +Não foi possível acessar %x. + +Authentication completed. +Autenticação concluída. + +Authentication failed. +Autenticação falhou. + +You may close this page now and continue with FreeFileSync. +Você pode fechar esta página agora e continuar com o FreeFileSync. + +The server returned an error: +O servidor retornou um erro: + +Cannot determine free disk space for %x. +Não é possível determinar o espaço livre em disco para %x. + Cannot find %x. Não é possível encontrar %x. @@ -597,18 +636,12 @@ Atual: %y bytes Cannot delete symbolic link %x. Não é possível excluir o link simbólico %x. -Cannot determine free disk space for %x. -Não é possível determinar o espaço livre em disco para %x. - Incorrect command line: Linha de comando incorreta: The server does not support authentication via %x. O servidor não suporta autenticação via %x. -Unable to access %x. -Não foi possível acessar %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Atual: %y bytes Failed to open SFTP channel number %x. Falha ao abrir o canal SFTP número %x. - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x kB - -%x GB -%x GB - Drag && drop Arrastar && Soltar @@ -764,6 +779,24 @@ O comando é disparado se: &Retry &Tentar Novamente + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x kB + +%x GB +%x GB + Loading... Carregando... @@ -909,7 +942,7 @@ O comando é disparado se: Salvar como &tarefa em lotes... Show &log - +Mostrar &log Start &comparison Iniciar &Comparação @@ -971,9 +1004,6 @@ O comando é disparado se: Access online storage Acessar armazenamento online -Swap sides -Trocar lados - Close search bar Fechar barra de localização @@ -998,6 +1028,9 @@ O comando é disparado se: View type: Tipo de visualização: +Save as default +Salvar como padrão + Select view: Selecionar visualização: @@ -1055,6 +1088,15 @@ O comando é disparado se: Handle daylight saving time Como lidar com horário de verão +Ignore errors +Ignorar erros + +Retry count: +Número de tentativas: + +Delay (in seconds): +Atraso (em segundos): + Performance improvements: Melhorias de desempenho: @@ -1129,17 +1171,11 @@ O comando é disparado se: Last x days: Últimos x dias: -Ignore errors -Ignorar erros - -Retry count: -Número de tentativas: - -Delay (in seconds): -Atraso (em segundos): +&Override default log path: +&Substituir caminho de log padrão: -Run a command after synchronization: -Executar um comando após a sincronização: +Run a command: +Executar um comando: OK OK @@ -1189,6 +1225,9 @@ O comando é disparado se: Directory on server: Diretório no servidor: +Access timeout (in seconds): +Tempo limite de acesso (em segundos): + SFTP channels per connection: Canais SFTP por conexão: @@ -1211,7 +1250,7 @@ O comando é disparado se: &Não mostrar esta caixa de diálogo novamente Items found: -Elementos encontrados: +Itens encontrados: Time remaining: Tempo restante: @@ -1267,12 +1306,6 @@ O comando é disparado se: Stop synchronization at first error Interromper a sincronização ao primeiro erro -Save log: -Salvar arquivo de log: - -Limit number of log files: -Limitar número de arquivos de log: - How can I schedule a batch job? Como posso agendar uma tarefa em lotes? @@ -1309,8 +1342,11 @@ Isto garante um estado consistente mesmo em caso de erro grave. Show all permanently hidden dialogs and warning messages again Mostrar todas as caixas de diálogo e as mensagens de aviso permanentemente ocultadas -Remove old log files after x days: - +Default log path: +Caminho de log padrão: + +&Delete logs after x days: +&Excluir logs após x days: Customize context menu: Personalizar menu de contexto: @@ -1321,8 +1357,8 @@ Isto garante um estado consistente mesmo em caso de erro grave. &Default &Config. Padrão -Feedback and suggestions are welcome -Críticas e sugestões são bem-vindas +Feedback and suggestions are welcome: +Comentários e sugestões são bem-vindos: Home page Página web @@ -1348,8 +1384,8 @@ Isto garante um estado consistente mesmo em caso de erro grave. Source code written in C++ using: Código-fonte escrito em C++ utilizando: -Published under the GNU General Public License -Publicado sob a GNU General Public License +Published under the GNU General Public License: +Publicado sob a Licença Pública Geral GNU: Many thanks for localization: Pela tradução, um agradecimento a: @@ -1406,7 +1442,7 @@ Isto garante um estado consistente mesmo em caso de erro grave. Informações No log entries - +Nenhuma entrada de log Select all Selecionar todos @@ -1432,6 +1468,9 @@ Isto garante um estado consistente mesmo em caso de erro grave. Overview Parâmetros +Swap sides +Trocar lados + Show "%x" Mostrar "%x" @@ -1606,9 +1645,6 @@ Isto garante um estado consistente mesmo em caso de erro grave. Show filtered or temporarily excluded files Mostrar arquivos que foram filtrados ou excluídos temporariamente -Save as default -Salvar como padrão - Filter Filtro @@ -1954,12 +1990,6 @@ Isto garante um estado consistente mesmo em caso de erro grave. Checking recycle bin failed for folder %x. Verificando falha na Lixeira para pasta %x. -The following XML elements could not be read: -Os seguintes elementos XML não puderam ser lidos: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Arquivo de configuração %x incompleto. Os elementos faltantes serão configurados com seus valores padrões. - Prepare installation Preparando a instalação 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 @@ Items differ in attributes only Elementele diferă doar prin atributele lor +The name %x is used by more than one item in the folder. +Numele %x e folosit de mai multe elemente din dosar. + Resolving symbolic link %x Rezolv legătura simbolică %x @@ -188,9 +191,6 @@ File time tolerance Toleranța timpului filei -Folder access timeout -Expirarea accesului la dosar - Run with background priority Rulează cu prioritate de fundal [background] @@ -336,8 +336,11 @@ Update attributes on right Actualizează atributele în partea dreaptă -Warning -Atenție +Errors: +Erori: + +Warnings: +Avertizări: Items processed: Elemente Procesate: @@ -348,6 +351,9 @@ Total time: Timp Total: +Warning +Atenție + Stopped Oprită @@ -357,6 +363,21 @@ Error parsing file %x, row %y, column %z. Eroare la parsarea filei %x, rîndul %y, coloana %z. +Services +Servicii + +Show All +Arată Tot + +Hide Others +Ascunde Restul + +Hide %x +Ascunde %x + +Quit %x +Închide %x + Cannot set directory locks for the following folders: Nu pot seta zăvorîrea [lock] pentru dosarele următoare: @@ -373,11 +394,11 @@ Cannot read directory %x. Nu pot citi dosarul %x. -/sec -/sec +%x/sec +%x/sec -%x items/sec -%x elemente/sec +%x items +%x elemente Show in Explorer Arată în Exploratorul de File @@ -526,11 +547,11 @@ Generating database... Generez baza de date... -Searching for excess file versions: -Caut versiunile în exces ale filelor: +Searching for old file versions: +Caut versiuni mai vechi ale filelor: -Removing excess file versions: -Înlătur versiunile în exces ale filelor: +Removing old file versions: +Înlătur versiunile vechi ale filelor: Unable to create time stamp for versioning: Nu pot crea marcajul de timp pentru versionare: @@ -588,6 +609,24 @@ Actuală: %y baiți Unable to move %x to the recycle bin. Nu pot muta %x în Reciclator. +Unable to access %x. +Nu pot accesa %x. + +Authentication completed. +Autentificare realizată. + +Authentication failed. +Autentificare eșuată. + +You may close this page now and continue with FreeFileSync. +Poți închide pagina asta acum și continua cu FreeFileSync. + +The server returned an error: +Serverul a returnat o eroare: + +Cannot determine free disk space for %x. +Nu pot determina spațiul liber de stocare pentru %x. + Cannot find %x. Nu pot găsi %x. @@ -600,18 +639,12 @@ Actuală: %y baiți Cannot delete symbolic link %x. Nu pot șterge legătura simbolică %x. -Cannot determine free disk space for %x. -Nu pot determina spațiul liber de stocare pentru %x. - Incorrect command line: Linie de comandă incorectă: The server does not support authentication via %x. Serverul nu suportă autentificarea prin %x. -Unable to access %x. -Nu pot accesa %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -638,25 +671,6 @@ Actuală: %y baiți Failed to open SFTP channel number %x. N-am putut deschide canalul SFTP cu numărul %x. - -1 byte -%x bytes - - -1 bait -%x baiți -%x de baiți - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Trage și pune un dosar mai jos sau explorează către el @@ -770,6 +784,25 @@ Comanda este declanșată dacă: &Retry &Reîncearcă + +1 byte +%x bytes + + +1 bait +%x baiți +%x de baiți + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Deschid... @@ -916,7 +949,7 @@ Comanda este declanșată dacă: Salvea&ză ca Sarcină Set... Show &log - +Arată &Jurnalul Start &comparison Pornește &Compararea @@ -978,9 +1011,6 @@ Comanda este declanșată dacă: Access online storage Accesează stocarea de pe internet [online] -Swap sides -Schimbă compartimentele stîng și drept între ele - Close search bar Închide bara de căutare @@ -1005,6 +1035,9 @@ Comanda este declanșată dacă: View type: Tipul Vederii: +Save as default +Salvează ca implicit + Select view: Selectează Vederea: @@ -1062,6 +1095,15 @@ Comanda este declanșată dacă: Handle daylight saving time Gestionarea timpului de vară (DST) +Ignore errors +Ignoră erorile + +Retry count: +Numărul reîncercărilor: + +Delay (in seconds): +Întîrziere (în secunde): + Performance improvements: Îmbunătățiri de Performanță: @@ -1136,17 +1178,11 @@ Comanda este declanșată dacă: Last x days: Ultimele x zile: -Ignore errors -Ignoră erorile - -Retry count: -Numărul reîncercărilor: - -Delay (in seconds): -Întîrziere (în secunde): +&Override default log path: +Ign&oră calea implicită a jurnalului: -Run a command after synchronization: -Rulează o comandă după sincronizare: +Run a command: +Rulează o comandă: OK OK @@ -1196,6 +1232,9 @@ Comanda este declanșată dacă: Directory on server: Dosarul de pe server: +Access timeout (in seconds): +Timp de expirare a accesului (în sec.): + SFTP channels per connection: Canale SFTP per conexiune: @@ -1274,12 +1313,6 @@ Comanda este declanșată dacă: Stop synchronization at first error Oprește sincronizarea la prima eroare întîlnită -Save log: -Salvează jurnalul: - -Limit number of log files: -Limitează numărul filelor de jurnalizare: - How can I schedule a batch job? Cum pot planifica o sarcină set? @@ -1316,8 +1349,11 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Show all permanently hidden dialogs and warning messages again Sînt arătate din nou dialogurile și mesajele de eroare care au fost ascunse permanent -Remove old log files after x days: - +Default log path: +Calea implicită a jurnalului: + +&Delete logs after x days: +Șter&ge jurnalul după x zile: Customize context menu: Personalizează meniul contextual: @@ -1328,8 +1364,8 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției &Default Coloanele &Implicite -Feedback and suggestions are welcome -Opiniile și sugestiile despre program sînt binevenite. +Feedback and suggestions are welcome: +Părerile și sugestiile sînt binevenite: Home page Pagina Sitului @@ -1355,8 +1391,8 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Source code written in C++ using: Cod sursă scris în C++ folosind: -Published under the GNU General Public License -Publicat sub licența GNU GPL +Published under the GNU General Public License: +Publicat sub Licența Publică Generală GNU: Many thanks for localization: Multe mulțumiri pentru localizare: @@ -1413,7 +1449,7 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Informații No log entries - +Nu există intrări în jurnal Select all Selectează Tot @@ -1439,6 +1475,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Overview Panoramă +Swap sides +Schimbă compartimentele stîng și drept între ele + Show "%x" Arată "%x" @@ -1617,9 +1656,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Show filtered or temporarily excluded files Arată filele filtrate sau excluse temporar -Save as default -Salvează ca implicit - Filter Filtrare @@ -1970,12 +2006,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției Checking recycle bin failed for folder %x. Verificarea disponibilității Reciclatorului a eșuat pentru dosarul %x. -The following XML elements could not be read: -Elementele XML următoare nu pot fi citite: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Fila configurație %x este incompletă. Elementele lipsă vor fi setate la valorile implicite. - Prepare installation Pregătirea Instalării 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 @@ Items differ in attributes only Элементы различаются только атрибутами +The name %x is used by more than one item in the folder. +Имя %x используется более чем одним элементом в папке. + Resolving symbolic link %x Разрешение символьной ссылки %x @@ -188,9 +191,6 @@ File time tolerance Допускаемая разница во времени изменения файла -Folder access timeout -Тайм-аут доступа к папкам - Run with background priority Запустить с фоновым приоритетом @@ -336,8 +336,11 @@ Update attributes on right Обновление атрибутов справа -Warning -Внимание +Errors: +Ошибки: + +Warnings: +Предупеждения: Items processed: Элементов обработано: @@ -348,15 +351,33 @@ Total time: Общее время: +Warning +Внимание + Stopped Остановлено Cleaning up log files: -Очистка лог-файлов (журналов): +Очистка файлов журнала: Error parsing file %x, row %y, column %z. Ошибка при разборе файла %x, строка %y, колонка %z. +Services +Службы + +Show All +Показать все + +Hide Others +Скрыть остальные + +Hide %x +Скрыть %x + +Quit %x +Выйти из %x + Cannot set directory locks for the following folders: Невозможно установить блокировки для следующих папок: @@ -373,11 +394,11 @@ Cannot read directory %x. Невозможно прочитать папку %x. -/sec - +%x/sec +%x/с -%x items/sec -%x элементов/с +%x items +%x элемент(ов) Show in Explorer Показать в Проводнике @@ -526,11 +547,11 @@ Generating database... Создание базы данных... -Searching for excess file versions: -Поиск лишних версий файлов: +Searching for old file versions: +Поиск старых версий файлов: -Removing excess file versions: -Удаление лишних версий файлов: +Removing old file versions: +Удаление старых версий файлов: Unable to create time stamp for versioning: Невозможно создать метку времени для архивирования файлов: @@ -588,6 +609,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. Невозможно переместить %x в "Корзину". +Unable to access %x. +Невозможно получить доступ к %x. + +Authentication completed. +Аутентификация завершена. + +Authentication failed. +Ошибка аутентификации. + +You may close this page now and continue with FreeFileSync. +Вы можете закрыть эту страницу сейчас и продолжить с FreeFileSync. + +The server returned an error: +Сервер возвратил ошибку: + +Cannot determine free disk space for %x. +Невозможно определить свободное место на диске для %x. + Cannot find %x. Невозможно найти %x. @@ -600,18 +639,12 @@ Actual: %y bytes Cannot delete symbolic link %x. Невозможно удалить символьную ссылку %x. -Cannot determine free disk space for %x. -Невозможно определить свободное место на диске для %x. - Incorrect command line: Неверная командная строка: The server does not support authentication via %x. Сервер не поддерживает аутентификацию с помощью %x. -Unable to access %x. -Невозможно получить доступ к %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -638,25 +671,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. Не удалось открыть SFTP канал номер %x. - -1 byte -%x bytes - - -%x байт -%x байта -%x байт - - -%x MB -%x МБ - -%x KB -%x КБ - -%x GB -%x ГБ - Drag && drop Drag && drop @@ -770,6 +784,25 @@ The command is triggered if: &Retry &Повторить + +1 byte +%x bytes + + +%x байт +%x байта +%x байт + + +%x MB +%x МБ + +%x KB +%x КБ + +%x GB +%x ГБ + Loading... Загрузка... @@ -838,7 +871,7 @@ The command is triggered if: Последняя синхронизация Log -Лог (журнал) +Журнал Folder Папка @@ -916,7 +949,7 @@ The command is triggered if: Сохранить как пакетное &задание... Show &log - +Показать &журнал Start &comparison Начать с&равнение @@ -978,9 +1011,6 @@ The command is triggered if: Access online storage Доступ к онлайн-хранилищу -Swap sides -Поменять направление - Close search bar Закрыть строку поиска @@ -1005,6 +1035,9 @@ The command is triggered if: View type: Тип просмотра: +Save as default +Сохранить по умолчанию + Select view: Выберите отображение: @@ -1062,6 +1095,15 @@ The command is triggered if: Handle daylight saving time Ручной переход на летнее время +Ignore errors +Игнорировать ошибки + +Retry count: +Число повторений: + +Delay (in seconds): +Задержка (в секундах): + Performance improvements: Повышение производительности: @@ -1136,17 +1178,11 @@ The command is triggered if: Last x days: Последние x дней: -Ignore errors -Игнорировать ошибки - -Retry count: -Число повторений: - -Delay (in seconds): -Задержка (в секундах): +&Override default log path: +&Изменить путь журнала по умолчанию: -Run a command after synchronization: -Запустить команду после синхронизации: +Run a command: +Выполнить команду: OK OK @@ -1196,6 +1232,9 @@ The command is triggered if: Directory on server: Папка на сервере: +Access timeout (in seconds): +Тайм-аут доступа (в секундах): + SFTP channels per connection: Количество SFTP каналов на одно соединение: @@ -1274,12 +1313,6 @@ The command is triggered if: Stop synchronization at first error Остановить синхронизацию при первой ошибке -Save log: -Сохранять лог-файлы (журналы): - -Limit number of log files: -Ограничить количество лог-файлов (журналов): - How can I schedule a batch job? Как запланировать пакетное задание? @@ -1316,8 +1349,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Показать все скрытые окна и сообщения с предупреждениями снова -Remove old log files after x days: - +Default log path: +Путь журнала по умолчанию: + +&Delete logs after x days: +&Удаление журналов через x дней: Customize context menu: Кастомизация контекстного меню: @@ -1328,8 +1364,8 @@ This guarantees a consistent state even in case of a serious error. &Default &По умолчанию -Feedback and suggestions are welcome -Замечания и предложения приветствуются +Feedback and suggestions are welcome: +Обратная связь и предложения приветствуются: Home page Домашняя страница @@ -1355,8 +1391,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: Исходный код написан на C++ с использованием: -Published under the GNU General Public License -Издается под лицензией GNU General Public License +Published under the GNU General Public License: +Опубликовано в соответствии с GNU General Public License: Many thanks for localization: Большое спасибо за перевод: @@ -1413,7 +1449,7 @@ This guarantees a consistent state even in case of a serious error. Информация No log entries - +Нет записей в журнале Select all Выделить все @@ -1439,6 +1475,9 @@ This guarantees a consistent state even in case of a serious error. Overview Главная +Swap sides +Поменять направление + Show "%x" Показать "%x" @@ -1617,9 +1656,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files Показать отфильтрованные или временно исключенные файлы -Save as default -Сохранить по умолчанию - Filter Фильтр @@ -1970,12 +2006,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. Проверка "Корзины" не удалась для папки %x. -The following XML elements could not be read: -Следующие XML-элементы не могут быть прочитаны: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Файл конфигурации %x является неполным. Недостающие элементы будут установлены в значения по умолчанию. - Prepare installation Подготовка установки 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 @@ n==1 ? 0 : n>=2 && n<=4 ? 1 : 2 +No log entries + + +Published under the GNU General Public License: + + +Feedback and suggestions are welcome: + + +&Delete logs after x days: + + +Default log path: + + +Access timeout (in seconds): + + +Run a command: + + +&Override default log path: + + +Show &log + + +The server returned an error: + + +You may close this page now and continue with FreeFileSync. + + +Authentication failed. + + +Authentication completed. + + +Removing old file versions: + + +Searching for old file versions: + + +%x items + + +%x/sec + + +Quit %x + + +Hide %x + + +Hide Others + + +Show All + + +Services + + +Warnings: + + +Errors: + + +The name %x is used by more than one item in the folder. + + Both sides have changed since last synchronization. Od poslednej synchronizácie došlo ku zmene obidvoch strán. @@ -188,9 +263,6 @@ File time tolerance Časová tolerancia súboru -Folder access timeout -Časový limit prístupu k priečinku - Run with background priority Vykonať s prioritou na pozadí @@ -336,9 +408,6 @@ Update attributes on right Aktualizovať atribúty napravo -Warning -Varovanie - Items processed: Spracovaných položiek: @@ -348,6 +417,9 @@ Total time: Celkový čas: +Warning +Varovanie + Stopped Zastavené @@ -373,12 +445,6 @@ Cannot read directory %x. Nie je možné načítať adresár %x. -/sec -/s - -%x items/sec -%x položiek/s - Show in Explorer Zobraziť v Prieskumníkovi @@ -526,12 +592,6 @@ Generating database... Vytváranie databázy... -Searching for excess file versions: -Hľadanie prebytočných verzií súborov: - -Removing excess file versions: -Odstránenie prebytočných verzií súborov: - Unable to create time stamp for versioning: Nie je možné vytvoriť časovú značku verzovania: @@ -588,6 +648,12 @@ Aktuálne: %y b Unable to move %x to the recycle bin. Nie je možné presunúť %x do Koša. +Unable to access %x. +Nie je možný prístup k %x. + +Cannot determine free disk space for %x. +Nie je možné zistiť voľné miesto na disku %x. + Cannot find %x. Nie je možné nájsť %x. @@ -600,18 +666,12 @@ Aktuálne: %y b Cannot delete symbolic link %x. Nie je možné zmazať symbolický odkaz %x. -Cannot determine free disk space for %x. -Nie je možné zistiť voľné miesto na disku %x. - Incorrect command line: Neplatný príkaz: The server does not support authentication via %x. Server nepodporuje overenie pomocou %x. -Unable to access %x. -Nie je možný prístup k %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -638,25 +698,6 @@ Aktuálne: %y b Failed to open SFTP channel number %x. Nie je možné otvoriť kanál číslo %x. - -1 byte -%x bytes - - -%x B -%x B -%x B - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Pretiahni sem && pusť @@ -770,6 +811,25 @@ Príkaz bude spustení ak: &Retry &Opakovať + +1 byte +%x bytes + + +%x B +%x B +%x B + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Načítanie... @@ -915,9 +975,6 @@ Príkaz bude spustení ak: Save as &batch job... Uložiť ako &dávku... -Show &log - - Start &comparison Spustiť &porovnanie @@ -978,9 +1035,6 @@ Príkaz bude spustení ak: Access online storage Prístup k online úložisku -Swap sides -Zámena strán - Close search bar Zavrieť hľadanie @@ -1005,6 +1059,9 @@ Príkaz bude spustení ak: View type: Typ zobrazenia: +Save as default +Uložiť ako predvolené + Select view: Výber zobrazenia: @@ -1062,6 +1119,15 @@ Príkaz bude spustení ak: Handle daylight saving time Používať letný čas +Ignore errors +Ignorovať chyby + +Retry count: +Počet opakovaní: + +Delay (in seconds): +Oneskorenie (v sekundách): + Performance improvements: Zlepšenie výkonu: @@ -1136,18 +1202,6 @@ Príkaz bude spustení ak: Last x days: Posledných x dní: -Ignore errors -Ignorovať chyby - -Retry count: -Počet opakovaní: - -Delay (in seconds): -Oneskorenie (v sekundách): - -Run a command after synchronization: -Spustiť príkaz po synchronizácií: - OK OK @@ -1274,12 +1328,6 @@ Príkaz bude spustení ak: Stop synchronization at first error Ukončiť synchronizáciu pri prvej chybe -Save log: -Uložiť log: - -Limit number of log files: -Limit počtu log súborov: - How can I schedule a batch job? Ako nastaviť spustenie dávky v Plánovači? @@ -1313,9 +1361,6 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Zobraziť znovu všetky trvale skryté dialógy a varovné hlásenia -Remove old log files after x days: - - Customize context menu: Prispôsobiť kontextovú ponuku: @@ -1325,9 +1370,6 @@ This guarantees a consistent state even in case of a serious error. &Default &Predvolené -Feedback and suggestions are welcome -Komentáre a námety sú vždy vítané - Home page Domovská stránka @@ -1352,9 +1394,6 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: Zdrojový kód bol napísaný kompletne v C++ pomocou: -Published under the GNU General Public License -Vydané pod GNU General Public License (GPL) - Many thanks for localization: Poďakovanie za preklad FreeFileSync: @@ -1409,9 +1448,6 @@ This guarantees a consistent state even in case of a serious error. Info Info -No log entries - - Select all Vybrať všetko @@ -1436,6 +1472,9 @@ This guarantees a consistent state even in case of a serious error. Overview Prehľad +Swap sides +Zámena strán + Show "%x" Zobraziť "%x" @@ -1614,9 +1653,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files Zobraziť filtrované alebo dočasne vynechané súbory -Save as default -Uložiť ako predvolené - Filter Filter @@ -1967,12 +2003,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. Zlyhala kontrola Koša pre priečinok %x. -The following XML elements could not be read: -Nie je možné načítať následujúce XML elementy: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Konfiguračný súbor %x je nekompletný. Chybajúce položky budú nahradené predvolenými hodnotami. - Prepare installation Príprava inštalácie @@ -2043,7 +2073,7 @@ This guarantees a consistent state even in case of a serious error. Upraviť vo FreeFileSync Instead of an ad, here's an animal. -Namiesto reklami je tu obrázok zvieraťa. +Namiesto reklamy je tu obrázok zvieraťa. The FreeFileSync portable version cannot install into a subfolder of %x. Prenosnú verziu FreeFileSync nie je možné inštalovať do podpriečinka %x. 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 @@ Items differ in attributes only Postavke se razlikujejo samo po atributih +The name %x is used by more than one item in the folder. +Ime %x uporablja več kot ena postavka v mapi. + Resolving symbolic link %x Razrešujem simbolično povezavo %x @@ -189,9 +192,6 @@ File time tolerance Časovna toleranca datoteke -Folder access timeout -Časovna omejitev dostopa do mape - Run with background priority Zaženi s prioriteto v ozadju @@ -338,8 +338,11 @@ Update attributes on right Posodobi atribute na desni strani -Warning -Opozorilo +Errors: +Napake: + +Warnings: +Opozorila: Items processed: Obdelanih postavk: @@ -350,6 +353,9 @@ Total time: Celoten čas: +Warning +Opozorilo + Stopped Ustavljeno @@ -359,6 +365,21 @@ Error parsing file %x, row %y, column %z. Napaka pri razčlenjevanju datoteke %x, vrstica %y, stolpec %z. +Services +Storitve + +Show All +Pokaži vse + +Hide Others +Skrij druge + +Hide %x +Skrij %x + +Quit %x +Nehaj %x + Cannot set directory locks for the following folders: Ne morem nastaviti zaklepanje imenika za naslednje mape: @@ -376,11 +397,11 @@ Cannot read directory %x. Ne morem prebrati direktorija %x. -/sec -/sek +%x/sec +%x/sek -%x items/sec -%x postavk/sek +%x items +%x postavk Show in Explorer Prikaži v Raziskovalcu @@ -529,11 +550,11 @@ Generating database... Ustvarjam podatkovno bazo... -Searching for excess file versions: -Iskanje presežnih različic datotek: +Searching for old file versions: +Iskanje starih različic datotek: -Removing excess file versions: -Odstranjevanje presežnih različic datotek: +Removing old file versions: +Odstranjevanje starejših različic datotek: Unable to create time stamp for versioning: Časovnega žiga za oznčitev ni bilo mogoče ustvariti: @@ -591,6 +612,24 @@ Dejansko: %y bajtov Unable to move %x to the recycle bin. Ne morem premakniti %x v koš. +Unable to access %x. +Ne morem dostopati do %x. + +Authentication completed. +Preverjanje pristnosti končano. + +Authentication failed. +Preverjanje pristnosti ni uspelo. + +You may close this page now and continue with FreeFileSync. +To stran lahko zdaj zaprete in nadaljujete z FreeFileSync. + +The server returned an error: +Strežnik je vrnil napako: + +Cannot determine free disk space for %x. +Ne morem določiti prostega prostora na disku za %x. + Cannot find %x. Ne najdem %x. @@ -603,18 +642,12 @@ Dejansko: %y bajtov Cannot delete symbolic link %x. Ne morem izbrisati simbolične povezave %x. -Cannot determine free disk space for %x. -Ne morem določiti prostega prostora na disku za %x. - Incorrect command line: Napačna ukazna vrstica: The server does not support authentication via %x. Strežnik ne podpira preverjanja pristnosti preko %x. -Unable to access %x. -Ne morem dostopati do %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -643,26 +676,6 @@ Dejansko: %y bajtov Failed to open SFTP channel number %x. Ne morem odpreti SFTP kanala številka %x. - -1 byte -%x bytes - - -%x bajt -%x bajta -%x bajti -%x bajtov - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Povleci && spusti @@ -776,6 +789,26 @@ Ukaz se sproži če: &Retry &Ponovi + +1 byte +%x bytes + + +%x bajt +%x bajta +%x bajti +%x bajtov + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Nalagam... @@ -923,7 +956,7 @@ Ukaz se sproži če: Shrani kot paketno op&ravilo... Show &log - +Pokaži &dnevnik Start &comparison Začni &primerjavo @@ -985,9 +1018,6 @@ Ukaz se sproži če: Access online storage Dostop do spletnega prostora za shranjevanje -Swap sides -Zamenjaj strani - Close search bar Zapri iskalno vrstico @@ -1012,6 +1042,9 @@ Ukaz se sproži če: View type: Vrsta prikaza: +Save as default +Shrani kot privzeto + Select view: Izberi prikaz: @@ -1069,6 +1102,15 @@ Ukaz se sproži če: Handle daylight saving time Upoštevaj poletni in zimski čas +Ignore errors +Prezri napake + +Retry count: +Število poskusov: + +Delay (in seconds): +Zakasnitev (v sekundah): + Performance improvements: Izboljšave zmogljivosti: @@ -1143,17 +1185,11 @@ Ukaz se sproži če: Last x days: Zadnji x dnevi: -Ignore errors -Prezri napake - -Retry count: -Število poskusov: - -Delay (in seconds): -Zakasnitev (v sekundah): +&Override default log path: +&Preglasi privzeto pot dnevnika: -Run a command after synchronization: -Zaženi ukaz po sinhronizaciji: +Run a command: +Zaženi ukaz: OK V redu @@ -1203,6 +1239,9 @@ Ukaz se sproži če: Directory on server: Imenik na strežniku: +Access timeout (in seconds): +Časovna omejitev dostopa (v sekundah): + SFTP channels per connection: SFTP kanali za povezavo: @@ -1281,12 +1320,6 @@ Ukaz se sproži če: Stop synchronization at first error Ustavi sinhronizacijo ob prvi napaki -Save log: -Shrani dnevnik: - -Limit number of log files: -Omejitve števila datotek dnevnika: - How can I schedule a batch job? Kako lahko načrtujem opravilo v paketu? @@ -1323,8 +1356,11 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Show all permanently hidden dialogs and warning messages again Prikaži vsa trajno skrita pogovorna okna in opozorilna sporočila -Remove old log files after x days: - +Default log path: +Privzeta pot dnevnika: + +&Delete logs after x days: +&Izbriši dnevnike po x dneh: Customize context menu: Prilagodi kontekstni meni: @@ -1335,8 +1371,8 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. &Default &Privzeto -Feedback and suggestions are welcome -Povratne informacije in predlogi so dobrodošli +Feedback and suggestions are welcome: +Povratne informacije in predlogi so dobrodošli: Home page Domača stran @@ -1362,8 +1398,8 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Source code written in C++ using: Izvorna koda napisana v C++ z uporabo: -Published under the GNU General Public License -Objavljeno pod licenco GNU General Public +Published under the GNU General Public License: +Objavljeno pod licenco GNU General Public: Many thanks for localization: Zahvala prevajalcem za lokalizacijo: @@ -1420,7 +1456,7 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Info No log entries - +Ni zapisov v dnevniku Select all Izberi vse @@ -1446,6 +1482,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Overview Predogled +Swap sides +Zamenjaj strani + Show "%x" Prikaži "%x" @@ -1628,9 +1667,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Show filtered or temporarily excluded files Pokaži filtrirane ali začasno izključene datoteke -Save as default -Shrani kot privzeto - Filter Filter @@ -1986,12 +2022,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. Checking recycle bin failed for folder %x. Preverjanje koša za mapo %x ni uspelo. -The following XML elements could not be read: -Neaslednje XML postavke niso berljive: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Nastavitvena datoteka %x je nepopolna. Manjkajoči elementi bodo nastavljeni na privzete vrednosti. - Prepare installation Pripravljam namestitev 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 @@ archivo de config. global: Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files. -Cualquier número de archivos de configuración de FreeFileSync ("ffs_gui" ó "ffs_batch"). +Cualquier número de archivos de configuración "ffs_gui" y/o "ffs_batch" de FreeFileSync. Any number of alternative directory pairs for at most one config file. Cualquier número de pares de directorios alternativos para un archivo de configuración como máximo. @@ -160,6 +160,9 @@ Items differ in attributes only Los elementos sólo se diferencian en los atributos +The name %x is used by more than one item in the folder. +El nombre %x es utilizado por más de un elemento en la carpeta. + Resolving symbolic link %x Resolviendo vínculo simbólico %x @@ -187,9 +190,6 @@ File time tolerance Tolerancia en hora de archivo -Folder access timeout -Tiempo de espera de acceso a carpetas - Run with background priority Ejecutar con prioridad en segundo plano @@ -334,8 +334,11 @@ Update attributes on right Actualizar atributos en la derecha -Warning -Atención +Errors: +Errores: + +Warnings: +Advertencias: Items processed: Elementos procesados: @@ -346,6 +349,9 @@ Total time: Tiempo total: +Warning +Atención + Stopped Detenido @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Error analizando archivo %x, fila %y, columna %z. +Services +Servicios + +Show All +Mostrar todo + +Hide Others +Ocultar otros + +Hide %x +Ocultar %x + +Quit %x +Salir de %x + Cannot set directory locks for the following folders: No se pudieron bloquear directorios para las carpetas siguientes: @@ -370,11 +391,11 @@ Cannot read directory %x. No se puede leer el directorio %x. -/sec -/seg +%x/sec +%x/seg -%x items/sec -%x elementos/seg +%x items +%x elementos Show in Explorer Mostrar en el Explorador @@ -523,10 +544,10 @@ Generating database... Generando base de datos... -Searching for excess file versions: +Searching for old file versions: Buscando versiones antiguas de archivos: -Removing excess file versions: +Removing old file versions: Eliminando versiones antiguas de archivos: Unable to create time stamp for versioning: @@ -585,6 +606,24 @@ Reales: %y bytes Unable to move %x to the recycle bin. No es posible mover %x a la papelera de reciclaje. +Unable to access %x. +No se puede acceder a %x. + +Authentication completed. +Autenticación completada. + +Authentication failed. +Fallo de autenticación. + +You may close this page now and continue with FreeFileSync. +Puede cerrar esta página y continuar con FreeFileSync. + +The server returned an error: +El servidor devolvió un error: + +Cannot determine free disk space for %x. +No se puede determinar el espacio libre en disco para %x. + Cannot find %x. No se encuentra %x. @@ -597,18 +636,12 @@ Reales: %y bytes Cannot delete symbolic link %x. No se puede eliminar el vínculo simbólico %x. -Cannot determine free disk space for %x. -No se puede determinar el espacio libre en disco para %x. - Incorrect command line: Línea de comandos incorrecta: The server does not support authentication via %x. El servidor no admite autenticación via %x. -Unable to access %x. -No se puede acceder a %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Reales: %y bytes Failed to open SFTP channel number %x. Fallo de apertura del canal SFTP número %x. - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x Gb - Drag && drop Arrastrar y soltar @@ -764,6 +779,24 @@ El comando es disparado si: &Retry &Reintentar + +1 byte +%x bytes + + +1 byte +%x bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x Gb + Loading... Cargando... @@ -909,7 +942,7 @@ El comando es disparado si: Guardar como tarea por &lotes... Show &log - +Mostrar re&gistro Start &comparison Iniciar la &comparación @@ -971,9 +1004,6 @@ El comando es disparado si: Access online storage Acceder a almacenamiento en línea -Swap sides -Intercambiar lados - Close search bar Cerrar la barra de búsqueda @@ -998,6 +1028,9 @@ El comando es disparado si: View type: Ver tipo: +Save as default +Guardar como predeterminado + Select view: Seleccionar vista: @@ -1055,6 +1088,15 @@ El comando es disparado si: Handle daylight saving time Respetar el horario de verano +Ignore errors +Ignorar errores + +Retry count: +Cuenta de reintentos: + +Delay (in seconds): +Retardo (en segundos): + Performance improvements: Mejoras en el rendimiento: @@ -1129,17 +1171,11 @@ El comando es disparado si: Last x days: Últimos x días: -Ignore errors -Ignorar errores - -Retry count: -Cuenta de reintentos: - -Delay (in seconds): -Retardo (en segundos): +&Override default log path: +&Reemplazar la ruta predeterminada del registro: -Run a command after synchronization: -Ejecutar un comando tras la sincronización: +Run a command: +Ejecutar un comando: OK OK @@ -1189,6 +1225,9 @@ El comando es disparado si: Directory on server: Directorio del servidor: +Access timeout (in seconds): +Tiempo de espera en acceso (segundos): + SFTP channels per connection: Canales SFTP por conexión: @@ -1250,7 +1289,7 @@ El comando es disparado si: 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 Progress dialog: -Diálogo de progreson: +Diálogo de progreso: Run minimized Ejecutar minimizado @@ -1267,12 +1306,6 @@ El comando es disparado si: Stop synchronization at first error Detener la sincronización con el primer error -Save log: -Guardar registro: - -Limit number of log files: -Limitar el número de archivos de registro: - How can I schedule a batch job? ¿Cómo puedo programar una tarea por lotes? @@ -1309,8 +1342,11 @@ Esto garantiza un estado coherente incluso en caso de error grave. Show all permanently hidden dialogs and warning messages again Volver a mostrar todos los diálogos y mensajes de advertencia que fueron permanentemente ocultados -Remove old log files after x days: - +Default log path: +Ruta predeterminada del registro: + +&Delete logs after x days: +&Eliminar los registros tras x días: Customize context menu: Personalizar menú contextual: @@ -1321,8 +1357,8 @@ Esto garantiza un estado coherente incluso en caso de error grave. &Default &Configuración predeterminada -Feedback and suggestions are welcome -Tus comentarios y sugerencias son bienvenidos: +Feedback and suggestions are welcome: +Comentarios y sugerencias son bienvenidos: Home page Página de inicio @@ -1348,8 +1384,8 @@ Esto garantiza un estado coherente incluso en caso de error grave. Source code written in C++ using: Código fuente C++ con soporte de: -Published under the GNU General Public License -Publicado bajo régimen de derechos GNU General Public License: +Published under the GNU General Public License: +Publicado con licencia pública general GNU: Many thanks for localization: Agradecimientos a los traductores: @@ -1406,7 +1442,7 @@ Esto garantiza un estado coherente incluso en caso de error grave. Info No log entries - +No hay entradas de registro Select all Seleccionar todo @@ -1432,6 +1468,9 @@ Esto garantiza un estado coherente incluso en caso de error grave. Overview Vista general +Swap sides +Intercambiar lados + Show "%x" Mostrar "%x" @@ -1606,9 +1645,6 @@ Esto garantiza un estado coherente incluso en caso de error grave. Show filtered or temporarily excluded files Mostrar archivos excluidos temporalmente o filtrados -Save as default -Guardar como predeterminado - Filter Filtro @@ -1954,12 +1990,6 @@ Esto garantiza un estado coherente incluso en caso de error grave. Checking recycle bin failed for folder %x. Fallo al comprobar la papelera de reciclaje para la carpeta %x. -The following XML elements could not be read: -No se pueden leer los siguientes elementos XML: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -El archivo de configuración %x está incompleto. Los elementos ausentes se definirán con valores predeterminados. - Prepare installation Preparar la instalación 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 @@ Items differ in attributes only Endast attribut skiljer objekten åt +The name %x is used by more than one item in the folder. +Namnet %x, används av mer än ett objekt, i mappen. + Resolving symbolic link %x Översätter den symboliska länken %x @@ -187,9 +190,6 @@ File time tolerance Tidsstämpeltolerans -Folder access timeout -Tidsgräns för mappåtkomst - Run with background priority Kör med bakgrundsprioritet @@ -334,8 +334,11 @@ Update attributes on right Uppdatera attribut på höger sida -Warning -Varning +Errors: +Fel: + +Warnings: +Varningar: Items processed: Processade poster: @@ -346,6 +349,9 @@ Total time: Total tid: +Warning +Varning + Stopped Stoppad @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. Tolkningsfel på filen %x, rad %y, kolumn %z. +Services +Tjänster + +Show All +Visa all + +Hide Others +Dölj andra + +Hide %x +Dölj %x + +Quit %x +Avsluta %x + Cannot set directory locks for the following folders: Kan inte ange mapplås för följanda mappar: @@ -370,11 +391,11 @@ Cannot read directory %x. Kan inte läsa mappen %x. -/sec -/s +%x/sec +%x/sek -%x items/sec -%x objekt/sek. +%x items +%x objekt Show in Explorer Visa i Utforskaren @@ -523,11 +544,11 @@ Generating database... Skapar databas... -Searching for excess file versions: -Söker efter överflödiga filversioner: +Searching for old file versions: +Söker efter gamla filversioner: -Removing excess file versions: -Tar bort överflödiga filversioner: +Removing old file versions: +Tar bort gamla filversioner: Unable to create time stamp for versioning: Kunde inte skapa tidsstämpel för versionshantering: @@ -585,6 +606,24 @@ Aktuell: %y byte Unable to move %x to the recycle bin. Kan inte att flytta %x till papperskorgen. +Unable to access %x. +Kan inte komma åt %x. + +Authentication completed. +Autentisering slutförd. + +Authentication failed. +Autentisering misslyckades. + +You may close this page now and continue with FreeFileSync. +Du kan nu stänga denna sida och fortsätta med FreeFileSync. + +The server returned an error: +Servern returnerade ett fel: + +Cannot determine free disk space for %x. +Kan inte avgöra ledigt utrymme på %x. + Cannot find %x. Kan inte hitta %x. @@ -597,18 +636,12 @@ Aktuell: %y byte Cannot delete symbolic link %x. Kan inte ta bort den symboliska länken %x. -Cannot determine free disk space for %x. -Kan inte avgöra ledigt utrymme på %x. - Incorrect command line: Felaktig kommandorad: The server does not support authentication via %x. Servern stödjer inte autentisering via %x. -Unable to access %x. -Kan inte komma åt %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Aktuell: %y byte Failed to open SFTP channel number %x. Kunde inte öppna SFTP-kanal nummer %x. - -1 byte -%x bytes - - -1 byte -%x byte - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Dra && släpp @@ -764,6 +779,24 @@ Kommandot triggas om: &Retry &Försök igen + +1 byte +%x bytes + + +1 byte +%x byte + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Läser in... @@ -909,7 +942,7 @@ Kommandot triggas om: Spara som &batch-fil... Show &log - +Visa &logg Start &comparison &Jämför @@ -971,9 +1004,6 @@ Kommandot triggas om: Access online storage Anslut fjärrlagring -Swap sides -Byt sida - Close search bar Stäng sökfältet @@ -998,6 +1028,9 @@ Kommandot triggas om: View type: Visningstyp: +Save as default +Spara som standard + Select view: Välj vy: @@ -1055,6 +1088,15 @@ Kommandot triggas om: Handle daylight saving time Hantera sommartid +Ignore errors +Ignorera fel + +Retry count: +Antal försök: + +Delay (in seconds): +Fördröjning (i sekunder): + Performance improvements: Prestandaförbättringar: @@ -1129,17 +1171,11 @@ Kommandot triggas om: Last x days: Senaste x dagarna: -Ignore errors -Ignorera fel - -Retry count: -Antal försök: - -Delay (in seconds): -Fördröjning (i sekunder): +&Override default log path: +&Åsidosätt standard loggsökväg: -Run a command after synchronization: -Kör ett kommando efter synkronisering: +Run a command: +Kör ett kommando: OK OK @@ -1189,6 +1225,9 @@ Kommandot triggas om: Directory on server: Målmapp på servern: +Access timeout (in seconds): +Åtkomsttidsgräns (i sekunder): + SFTP channels per connection: SFTP-kanaler per anslutning: @@ -1267,12 +1306,6 @@ Kommandot triggas om: Stop synchronization at first error Stoppa synkroniseringen vid första fel som uppstår -Save log: -Spara logg: - -Limit number of log files: -Begränsa antalet loggfiler: - How can I schedule a batch job? Hur schemalägger jag en batch-fil? @@ -1309,8 +1342,11 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Show all permanently hidden dialogs and warning messages again Visa alla permanent dolda dialoger och varningsmeddelanden igen -Remove old log files after x days: - +Default log path: +Standard loggsökväg: + +&Delete logs after x days: +&Ta bort loggar efter x dagar: Customize context menu: Anpassad kontextmeny: @@ -1321,8 +1357,8 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. &Default &Standard -Feedback and suggestions are welcome -Återkoppling och förslag är välkommna +Feedback and suggestions are welcome: +Återkoppling och förslag välkomnas: Home page Hemsida @@ -1348,8 +1384,8 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Source code written in C++ using: Källkod skriven i C++ med hjälp av: -Published under the GNU General Public License -Publiserad under GNU General Public License +Published under the GNU General Public License: +Publicerat under GNU General Public License: Many thanks for localization: Tack för översättning: @@ -1406,7 +1442,7 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Info No log entries - +Inga loggposter Select all Markera alla @@ -1432,6 +1468,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Overview Översikt +Swap sides +Byt sida + Show "%x" Visa "%x" @@ -1606,9 +1645,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Show filtered or temporarily excluded files Dölj filtrerade eller temporärt undantagna filer -Save as default -Spara som standard - Filter Filter @@ -1954,12 +1990,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. Checking recycle bin failed for folder %x. Kontroll av papperskorgen misslyckades för %x. -The following XML elements could not be read: -Följande XML-element kunde inte läsas: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Konfigurationsfilen %x är inte komplett. De saknade elementen kommer att anges till standardvärdet. - Prepare installation Förbered installation 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 @@ Items differ in attributes only Yalnız öznitelikleri farklı olan ögeler +The name %x is used by more than one item in the folder. + + Resolving symbolic link %x %x sembolik bağlantısı çözümleniyor @@ -187,9 +190,6 @@ File time tolerance Yok sayılacak dosya zamanı farkı -Folder access timeout -Klasör erişimi zaman aşımı - Run with background priority Artalan önceliği ile çalışsın @@ -334,8 +334,11 @@ Update attributes on right Sağdaki öznitelikler güncellensin -Warning -Uyarı +Errors: + + +Warnings: + Items processed: İşlenen öge: @@ -346,6 +349,9 @@ Total time: Toplam süre: +Warning +Uyarı + Stopped Durduruldu @@ -355,6 +361,21 @@ Error parsing file %x, row %y, column %z. %x dosyası işlenirken sorun çıktı, satır %y, sütun %z. +Services + + +Show All + + +Hide Others + + +Hide %x + + +Quit %x + + Cannot set directory locks for the following folders: Şu klasörler kilitlenemedi: @@ -370,11 +391,11 @@ Cannot read directory %x. %x klasörü okunamadı. -/sec -/saniye +%x/sec + -%x items/sec -%x öge/saniye +%x items + Show in Explorer Tarayıcıda Görüntüle @@ -523,11 +544,11 @@ Generating database... Veritabanı oluşturuluyor... -Searching for excess file versions: -Fazla dosya sürümleri aranıyor: +Searching for old file versions: + -Removing excess file versions: -Fazla dosya sürümleri siliniyor: +Removing old file versions: + Unable to create time stamp for versioning: Sürümlendirme için zaman damgası oluşturulamadı: @@ -585,6 +606,24 @@ Gerçekleşen: %y bayt Unable to move %x to the recycle bin. %x geri dönüşüm kutusuna atılamadı. +Unable to access %x. +%x üzerine erişilemedi. + +Authentication completed. + + +Authentication failed. + + +You may close this page now and continue with FreeFileSync. + + +The server returned an error: + + +Cannot determine free disk space for %x. +%x için boş disk alanı belirlenemedi. + Cannot find %x. %x bulunamadı. @@ -597,18 +636,12 @@ Gerçekleşen: %y bayt Cannot delete symbolic link %x. %x sembolik bağlantısı silinemedi. -Cannot determine free disk space for %x. -%x için boş disk alanı belirlenemedi. - Incorrect command line: Satırdaki komut geçersiz: The server does not support authentication via %x. Sunucu %x üzerinden kimlik doğrulamasını desteklemiyor. -Unable to access %x. -%x üzerine erişilemedi. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -633,24 +666,6 @@ Gerçekleşen: %y bayt Failed to open SFTP channel number %x. %x SFTP kanal numarası açılamadı. - -1 byte -%x bytes - - -1 bayt -%x bayt - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - Drag && drop Dosyaları sürükleyip buraya bırakın @@ -764,6 +779,24 @@ Komut şu durumlarda yürütülür: &Retry &Yeniden Dene + +1 byte +%x bytes + + +1 bayt +%x bayt + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + Loading... Yükleniyor... @@ -971,9 +1004,6 @@ Komut şu durumlarda yürütülür: Access online storage Çevrimiçi Depolama Erişimi -Swap sides -Sağ ve Sol Tarafları Değiştir - Close search bar Arama Çubuğunu Kapat @@ -998,6 +1028,9 @@ Komut şu durumlarda yürütülür: View type: Görünüm Kipi: +Save as default +Varsayılan Olarak Kaydet + Select view: Eşit Dosya Görünümü: @@ -1055,6 +1088,15 @@ Komut şu durumlarda yürütülür: Handle daylight saving time Yaz saati hakkında bilgiler +Ignore errors +Sorunlar yok sayılsın + +Retry count: +Deneme Sayısı: + +Delay (in seconds): +Bekleme (saniye): + Performance improvements: Başarım İyileştirmeleri: @@ -1129,17 +1171,11 @@ Komut şu durumlarda yürütülür: Last x days: Son x Gün: -Ignore errors -Sorunlar yok sayılsın - -Retry count: -Deneme Sayısı: - -Delay (in seconds): -Bekleme (saniye): +&Override default log path: + -Run a command after synchronization: -Eşitleme sonrası yürütülecek komut: +Run a command: + OK Tamam @@ -1189,6 +1225,9 @@ Komut şu durumlarda yürütülür: Directory on server: Sunucudaki Klasör: +Access timeout (in seconds): + + SFTP channels per connection: Bir Bağlantı için SFTP Kanalı Sayısı: @@ -1267,12 +1306,6 @@ Komut şu durumlarda yürütülür: Stop synchronization at first error Çıkan ilk sorunda eşitleme durdurulsun -Save log: -İşlem Günlüğü Kaydedilsin: - -Limit number of log files: -Günlük dosyaları sayısı sınırlaması: - How can I schedule a batch job? Toplu İşlem Zamanlaması Hakkında Bilgiler @@ -1309,7 +1342,10 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Show all permanently hidden dialogs and warning messages again Kalıcı olarak gizlenmiş tüm ileti ve uyarılar yeniden görüntülenir -Remove old log files after x days: +Default log path: + + +&Delete logs after x days: Customize context menu: @@ -1321,8 +1357,8 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y &Default &Varsayılan -Feedback and suggestions are welcome -Öneri ve geri bildirimlerinizi bekleriz +Feedback and suggestions are welcome: + Home page Ana Sayfa @@ -1348,8 +1384,8 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Source code written in C++ using: Kaynak kodu C++ kullanılarak yazılmıştır: -Published under the GNU General Public License -GNU Genel Kamu Lisansı koşulları altında yayınlanmıştır +Published under the GNU General Public License: + Many thanks for localization: Çeviriler için çok teşekkürler: @@ -1432,6 +1468,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Overview Genel +Swap sides +Sağ ve Sol Tarafları Değiştir + Show "%x" "%x" paneli görüntülensin @@ -1606,9 +1645,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Show filtered or temporarily excluded files Süzülmüş ya da geçici olarak katılmayan dosyaları görüntüler ya da gizler -Save as default -Varsayılan Olarak Kaydet - Filter Süzme @@ -1954,12 +1990,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y Checking recycle bin failed for folder %x. %x klasörü için Geri Dönüşüm Kutusu denetlenemedi. -The following XML elements could not be read: -Şu XML bileşenleri okunamadı: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -%x yapılandırma dosyası tam değil. Eksik bileşenler için varsayılan değerler kullanılacak. - Prepare installation Kuruluma hazırlanıyor 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 @@ Items differ in attributes only Елементи відрізняються тільки атрибутами +The name %x is used by more than one item in the folder. +Ім'я %x використовується більше ніж одним елементом у папці. + Resolving symbolic link %x Вирішення символьного посилання %x @@ -188,9 +191,6 @@ File time tolerance Толеранс часу файлу -Folder access timeout -Таймаут доступу до папки - Run with background priority Запустити з фоновим пріоритетом @@ -336,8 +336,11 @@ Update attributes on right Оновити атрибути праворуч -Warning -Увага +Errors: +Помилки: + +Warnings: +Попередження: Items processed: Елементів оброблено: @@ -348,6 +351,9 @@ Total time: Загальний час: +Warning +Увага + Stopped Зупинено @@ -357,6 +363,21 @@ Error parsing file %x, row %y, column %z. Помилка розбору файлу %x, рядок %y, колонка %z. +Services +Служби + +Show All +Показати Усі + +Hide Others +Сховати Інші + +Hide %x +Сховати %x + +Quit %x +Вихід %x + Cannot set directory locks for the following folders: Неможливо встановити блокування каталогів для таких папок: @@ -373,11 +394,11 @@ Cannot read directory %x. Не вдається прочитати папку %x. -/sec -/сек +%x/sec +%x/сек -%x items/sec -%x елемента/сек +%x items +%x елементів Show in Explorer Показати у Провіднику @@ -526,11 +547,11 @@ Generating database... Створення бази даних... -Searching for excess file versions: -Пошук надлишкових версій файлів: +Searching for old file versions: +Пошук старих версій файлів: -Removing excess file versions: -Видалення надлишкових версій файлів: +Removing old file versions: +Видалення старих версій файлів: Unable to create time stamp for versioning: Не вдається створити часової мітки для версій: @@ -588,6 +609,24 @@ Actual: %y bytes Unable to move %x to the recycle bin. Не вдається перемістити %x до корзини. +Unable to access %x. +Не вдалося отримати доступ до %x. + +Authentication completed. +Автентифікація виконана. + +Authentication failed. +Автентифікація не виконана. + +You may close this page now and continue with FreeFileSync. +Ви можете закрити цю сторінку зараз і продовжити з FreeFileSync. + +The server returned an error: +Сервер вернув помилку: + +Cannot determine free disk space for %x. +Не вдається визначити об'єм вільного місця для %x. + Cannot find %x. Не вдається знайти %x. @@ -600,18 +639,12 @@ Actual: %y bytes Cannot delete symbolic link %x. Не вдалося вилучити символьне посилання %x. -Cannot determine free disk space for %x. -Не вдається визначити об'єм вільного місця для %x. - Incorrect command line: Неправильний командний рядок: The server does not support authentication via %x. Сервер не підтримує аутентифікацію за допомогою %x. -Unable to access %x. -Не вдалося отримати доступ до %x. - Operation timed out after 1 second. Operation timed out after %x seconds. @@ -638,25 +671,6 @@ Actual: %y bytes Failed to open SFTP channel number %x. Не вдалося відкрити SFTP канал номер %x. - -1 byte -%x bytes - - -%x байт -%x байти -%x байтів - - -%x MB -%x МБ - -%x KB -%x КБ - -%x GB -%x ГБ - Drag && drop Drag && drop @@ -770,6 +784,25 @@ The command is triggered if: &Retry &Повторити + +1 byte +%x bytes + + +%x байт +%x байти +%x байтів + + +%x MB +%x МБ + +%x KB +%x КБ + +%x GB +%x ГБ + Loading... Завантаження... @@ -916,7 +949,7 @@ The command is triggered if: Зберегти як &пакетне завдання... Show &log - +Показати &журнал Start &comparison Запуск по&рівняння @@ -978,9 +1011,6 @@ The command is triggered if: Access online storage Доступ до online сховища -Swap sides -Поміняти місцями - Close search bar Закрити панель пошуку @@ -1005,6 +1035,9 @@ The command is triggered if: View type: Тип перегляду: +Save as default +Зберегти як замовчування + Select view: Вибрати перегляд: @@ -1062,6 +1095,15 @@ The command is triggered if: Handle daylight saving time Перехід на літній час вручну +Ignore errors +Ігнорувати помилки + +Retry count: +Кількість спроб: + +Delay (in seconds): +Затримка (секунд): + Performance improvements: Підвищення продуктивності: @@ -1136,17 +1178,11 @@ The command is triggered if: Last x days: Останні x днів: -Ignore errors -Ігнорувати помилки - -Retry count: -Кількість спроб: - -Delay (in seconds): -Затримка (секунд): +&Override default log path: +&Перевизначити шлях журналу за замовчуванням: -Run a command after synchronization: -Виконати команду після синхронізації: +Run a command: +Запустити команду: OK OK @@ -1196,6 +1232,9 @@ The command is triggered if: Directory on server: Папка на сервері: +Access timeout (in seconds): +Тайм-аут доступу (у секундах): + SFTP channels per connection: SFTP канали на з'єднання: @@ -1274,12 +1313,6 @@ The command is triggered if: Stop synchronization at first error Зупинити синхронізацію при першій помилці -Save log: -Зберегти журнал: - -Limit number of log files: -Обмеження кількості файлів журналу: - How can I schedule a batch job? Як можна запланувати пакетне завдання? @@ -1316,8 +1349,11 @@ This guarantees a consistent state even in case of a serious error. Show all permanently hidden dialogs and warning messages again Показати всі сховані діалоги і повідомлення з попередженнями знову -Remove old log files after x days: - +Default log path: +Шлях до журналу за замовчуванням: + +&Delete logs after x days: +&Видалити журнали після x днів: Customize context menu: Налаштування контекстного меню: @@ -1328,8 +1364,8 @@ This guarantees a consistent state even in case of a serious error. &Default &За замовчуванням -Feedback and suggestions are welcome -Відгуки та пропозиції вітаються +Feedback and suggestions are welcome: +Зворотній зв'язок і пропозиції вітаються: Home page Домашня сторінка @@ -1355,8 +1391,8 @@ This guarantees a consistent state even in case of a serious error. Source code written in C++ using: Код програми написаний на C++ з використанням: -Published under the GNU General Public License -Видано за ліцензією GNU General Public License +Published under the GNU General Public License: +Опубліковано за GNU General Public License: Many thanks for localization: Подяка за локалізацію: @@ -1413,7 +1449,7 @@ This guarantees a consistent state even in case of a serious error. Інформація No log entries - +Немає записів журналу Select all Виділити все @@ -1439,6 +1475,9 @@ This guarantees a consistent state even in case of a serious error. Overview Огляд +Swap sides +Поміняти місцями + Show "%x" Показати "%x" @@ -1617,9 +1656,6 @@ This guarantees a consistent state even in case of a serious error. Show filtered or temporarily excluded files Показати відфільтровані чи тимчасово виключені елементи -Save as default -Зберегти як замовчування - Filter Фільтр @@ -1970,12 +2006,6 @@ This guarantees a consistent state even in case of a serious error. Checking recycle bin failed for folder %x. Перевірка корзини для папки %x не вдалася. -The following XML elements could not be read: -Наступні XML елементи не вдалося прочитати: - -Configuration file %x is incomplete. The missing elements will be set to their default values. -Файл конфігурації %x неповний. Елементи, яких не вистачає, будуть заповнені значеннями за замовчуванням. - Prepare installation Підготовка встановлення 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(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); - wxSafeShowMessage(title, e.what()); + const auto titleFmt = copyStringTo(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); + std::cerr << utfTo(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(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(folderPathFmt)); + staticText->SetLabel(equalNativePath(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ? + wxString(_("Drag && drop")) : utfTo(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(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); auto defaultFileName = utfTo(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(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 waitForMissingDirs(const std::vector& folderPathPhrases, //throw FileError - const std::function& requestUiRefresh, std::chrono::milliseconds cbInterval) +std::set waitForMissingDirs(const std::vector& folderPathPhrases, //throw FileError + const std::function& 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 waitForMissingDirs(const std::vector& Zstring folderPathPhrase; std::future folderAvailable; }; - std::map folderInfos; //folderPath => FolderInfo + std::map folderInfos; //folderPath => FolderInfo for (const Zstring& phrase : folderPathPhrases) { @@ -50,8 +50,8 @@ std::set waitForMissingDirs(const std::vector& folderInfos[folderPath] = { phrase, runAsync([folderPath]{ return dirAvailable(folderPath); }) }; } - std::set availablePaths; - std::set missingPathPhrases; + std::set availablePaths; + std::set missingPathPhrases; for (auto& [folderPath, folderInfo] : folderInfos) { std::future& folderAvailable = folderInfo.folderAvailable; @@ -118,7 +118,7 @@ struct WaitResult }; -WaitResult waitForChanges(const std::set& folderPaths, //throw FileError +WaitResult waitForChanges(const std::set& folderPaths, //throw FileError const std::function& 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& folderPaths, / { std::vector 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& folderPathPhrases, std: for (;;) try { - std::set folderPaths = waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { requestUiRefresh(&folderPath); }, cbInterval); //throw FileError + std::set 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 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 uniqueFolders; + std::set 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 inline -bool matchesDbEntry(const FilePair& file, const InSyncFolder::FileList::value_type* dbFile, const std::vector& ignoreTimeShiftMinutes) +bool matchesDbEntry(const FilePair& file, const InSyncFile* dbFile, const std::vector& ignoreTimeShiftMinutes) { if (file.isEmpty()) return !dbFile; else if (!dbFile) return false; - const Zstring& fileNameDb = dbFile->first; - const InSyncDescrFile& descrDb = SelectParam::ref(dbFile->second.left, dbFile->second.right); + const InSyncDescrFile& descrDb = SelectParam::ref(dbFile->left, dbFile->right); - return getUnicodeNormalForm(file.getItemName()) == 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(), descrDb.modTime, 2, ignoreTimeShiftMinutes) && - file.getFileSize() == 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(), descrDb.modTime, 2, ignoreTimeShiftMinutes) && + file.getFileSize() == 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 inline -bool matchesDbEntry(const SymlinkPair& symlink, const InSyncFolder::SymlinkList::value_type* dbSymlink, const std::vector& ignoreTimeShiftMinutes) +bool matchesDbEntry(const SymlinkPair& symlink, const InSyncSymlink* dbSymlink, const std::vector& ignoreTimeShiftMinutes) { if (symlink.isEmpty()) return !dbSymlink; else if (!dbSymlink) return false; - const Zstring& linkNameDb = dbSymlink->first; - const InSyncDescrLink& descrDb = SelectParam::ref(dbSymlink->second.left, dbSymlink->second.right); + const InSyncDescrLink& descrDb = SelectParam::ref(dbSymlink->left, dbSymlink->right); - return getUnicodeNormalForm(symlink.getItemName()) == 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(), 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(), 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 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(); - else if (folder.isEmpty()) - return false; - - const Zstring& folderNameDb = dbFolder->first; - - return getUnicodeNormalForm(folder.getItemName()) == getUnicodeNormalForm(folderNameDb); + const bool haveDbEntry = dbFolder && dbFolder->status != InSyncFolder::DIR_STATUS_STRAW_MAN; + return haveDbEntry == !folder.isEmpty(); } @@ -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())) + exLeftOnlyByPath_.emplace(dbEntry, &file); else if (!file.getFileId().empty()) { auto rv = exLeftOnlyById_.emplace(file.getFileId(), &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())) + exRightOnlyByPath_.emplace(dbEntry, &file); else if (!file.getFileId().empty()) { auto rv = exRightOnlyById_.emplace(file.getFileId(), &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()); + const InSyncFolder* dbEntryR = dbEntryL; + if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName()) != getUnicodeNormalForm(folder.getItemName())) + dbEntryR = getDbEntry(dbFolderR, folder.getItemName()); - recurse(folder, dbSubFolder); + recurse(folder, dbEntryL, dbEntryR); } } @@ -400,10 +397,10 @@ private: } template - static FilePair* getAssocFilePair(const InSyncFile& dbFile, - const std::unordered_map& exOneSideById, - const std::unordered_map& exOneSideByPath) + FilePair* getAssocFilePair(const InSyncFile& dbFile) const { + const std::unordered_map& exOneSideById = SelectParam::ref(exLeftOnlyById_, exRightOnlyById_); + const std::unordered_map& exOneSideByPath = SelectParam::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(dbFile, exLeftOnlyById_, exLeftOnlyByPath_)) + if (FilePair* fileLeftOnly = getAssocFilePair(dbFile)) if (sameSizeAndDate(*fileLeftOnly, dbFile)) - if (FilePair* fileRightOnly = getAssocFilePair(dbFile, exRightOnlyById_, exRightOnlyByPath_)) + if (FilePair* fileRightOnly = getAssocFilePair(dbFile)) if (sameSizeAndDate(*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()); + const InSyncFile* dbEntryR = dbEntryL; + if (dbFolderL != dbFolderR || getUnicodeNormalForm(file.getItemName()) != getUnicodeNormalForm(file.getItemName())) + dbEntryR = getDbEntry(dbFolderR, file.getItemName()); //evaluation - const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes_); - const bool changeOnRight = !matchesDbEntry(file, dbEntry, ignoreTimeShiftMinutes_); + const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntryL, ignoreTimeShiftMinutes_); + const bool changeOnRight = !matchesDbEntry(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()); + const InSyncSymlink* dbEntryR = dbEntryL; + if (dbFolderL != dbFolderR || getUnicodeNormalForm(symlink.getItemName()) != getUnicodeNormalForm(symlink.getItemName())) + dbEntryR = getDbEntry(dbFolderR, symlink.getItemName()); //evaluation - const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes_); - const bool changeOnRight = !matchesDbEntry(symlink, dbEntry, ignoreTimeShiftMinutes_); + const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntryL, ignoreTimeShiftMinutes_); + const bool changeOnRight = !matchesDbEntry(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()); + const InSyncFolder* dbEntryR = dbEntryL; + if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName()) != getUnicodeNormalForm(folder.getItemName())) + dbEntryR = getDbEntry(dbFolderR, folder.getItemName()); if (cat != DIR_EQUAL) { //evaluation - const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(folder, dbEntry); - const bool changeOnRight = !matchesDbEntry(folder, dbEntry); + const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(folder, dbEntryL); + const bool changeOnRight = !matchesDbEntry(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 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::execute(baseFolder, *normFilter.nameFilter); + ApplyHardFilter::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 fff::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, - const AbstractPath& basePathR, const HardFilter& filterR) +std::optional 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 relPathL = split(AFS::getRootRelativePath(basePathL), FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); - const std::vector relPathR = split(AFS::getRootRelativePath(basePathR), FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); + const std::vector relPathL = split(basePathL.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); + const std::vector relPathR = split(basePathR.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); const bool leftParent = relPathL.size() <= relPathR.size(); @@ -1129,12 +1150,12 @@ std::optional 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 fff::getPathDependency(const AbstractPath& basePat //############################################################################################################ -std::pair fff::getSelectedItemsAsString(const std::vector& selectionLeft, - const std::vector& selectionRight) +std::pair fff::getSelectedItemsAsString(std::span selectionLeft, + std::span 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& 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 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& 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& rowsT } -void fff::copyToAlternateFolder(const std::vector& rowsToCopyOnLeft, - const std::vector& rowsToCopyOnRight, +void fff::copyToAlternateFolder(std::span rowsToCopyOnLeft, + std::span rowsToCopyOnRight, const Zstring& targetFolderPathPhrase, bool keepRelPaths, bool overwriteIfExists, WarningDialogs& warnings, ProcessCallback& callback) { - std::vector itemSelectionLeft = rowsToCopyOnLeft; - std::vector itemSelectionRight = rowsToCopyOnRight; - erase_if(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); - erase_if(itemSelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); + std::vector itemSelectionLeft (rowsToCopyOnLeft .begin(), rowsToCopyOnLeft .end()); + std::vector 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(); }); // const int itemTotal = static_cast(itemSelectionLeft.size() + itemSelectionRight.size()); int64_t bytesTotal = 0; @@ -1514,8 +1500,8 @@ void fff::deleteFromGridAndHD(const std::vector& rowsToDelete std::vector deleteLeft = rowsToDeleteOnLeft; std::vector deleteRight = rowsToDeleteOnRight; - erase_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed? - erase_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); //yes, for correct stats: + eraseIf(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed? + eraseIf(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); //yes, for correct stats: const int itemCount = static_cast(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& 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 getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, - const AbstractPath& basePathR, const HardFilter& filterR); +std::optional getPathDependency(const AbstractPath& basePathL, const PathFilter& filterL, + const AbstractPath& basePathR, const PathFilter& filterR); std::pair getSelectedItemsAsString( //returns string with item names and total count of selected(!) items, NOT total files/dirs! - const std::vector& selectionLeft, //all pointers need to be bound! - const std::vector& selectionRight); // + std::span selectionLeft, //all pointers need to be bound! + std::span selectionRight); // //manual copy to alternate folder: -void copyToAlternateFolder(const std::vector& rowsToCopyOnLeft, //all pointers need to be bound! - const std::vector& rowsToCopyOnRight, // +void copyToAlternateFolder(std::span rowsToCopyOnLeft, //all pointers need to be bound! + std::span 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 #include #include +#include #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(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); - wxSafeShowMessage(title, e.what()); + const auto titleFmt = copyStringTo(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); + std::cerr << utfTo(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& commandArgs) { logFatalError(utfTo(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(wxTheApp->GetAppDisplayName()) + SPACED_DASH + title; - wxSafeShowMessage(titleFmt, msg); - + std::cerr << utfTo(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& commandArgs) else if (equalAsciiNoCase(*it, optionLeftDir)) { if (++it == commandArgs.end() || isCommandLineOption(*it)) - { - notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo(optionLeftDir)), _("Syntax error")); - return; - } + return notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo(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(optionRightDir)), _("Syntax error")); - return; - } + return notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo(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(optionDirPair)), _("Syntax error")); - return; - } + return notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo(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(optionDirPair)), _("Syntax error")); - return; - } + return notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo(optionDirPair)), _("Syntax error")); dirPathPhrasePairs.back().second = *it; } else if (equalAsciiNoCase(*it, optionSendTo)) @@ -283,10 +274,7 @@ void Application::launch(const std::vector& 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& 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& 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& 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& 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 filePaths; for (const auto& [filePath, xmlType] : configFiles) @@ -453,8 +431,7 @@ void Application::launch(const std::vector& 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& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps; + const std::map& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps; std::set 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 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& fpCfgList, const std::map& deviceParallelOps, +ResolvedBaseFolders initializeBaseFolders(const std::vector& fpCfgList, const std::map& deviceParallelOps, bool allowUserInteraction, bool& warnFolderNotExisting, ProcessCallback& callback /*throw X*/) @@ -78,25 +78,31 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector& fpCf tryReportingError([&] { - std::set uniqueBaseFolders; - //support "retry" for environment variable and variable driver letter resolution! - output.resolvedPairs.clear(); + std::set baseFolders; + std::vector> 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& foldersToRead, - const std::map& deviceParallelOps, + const std::map& deviceParallelOps, int fileTimeTolerance, ProcessCallback& callback); @@ -155,12 +161,12 @@ private: std::map directoryBuffer_; //contains only *existing* directories const int fileTimeTolerance_; ProcessCallback& cb_; - const std::map deviceParallelOps_; + const std::map deviceParallelOps_; }; ComparisonBuffer::ComparisonBuffer(const std::set& foldersToRead, - const std::map& deviceParallelOps, + const std::map& 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())); } + +Zstringw getConflictAmbiguousItemName(const Zstring& itemName) +{ + return copyStringTo(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()) == + if (getUnicodeNormalForm(symlink.getItemName< LEFT_SIDE>()) == getUnicodeNormalForm(symlink.getItemName())) symlink.setCategory(); else @@ -311,7 +323,7 @@ std::shared_ptr ComparisonBuffer::compareByTimeSize(const Resolv //3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h if (file->getFileSize() == file->getFileSize()) { - if (getUnicodeNormalForm(file->getItemName()) == + if (getUnicodeNormalForm(file->getItemName< LEFT_SIDE>()) == getUnicodeNormalForm(file->getItemName())) file->setCategory(); 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()) != + if (getUnicodeNormalForm(symlink.getItemName< LEFT_SIDE>()) != getUnicodeNormalForm(symlink.getItemName())) symlink.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(symlink)); //else if (!sameFileTime(symlink.getLastWriteTime(), @@ -450,7 +462,7 @@ void categorizeFileByContent(FilePair& file, const std::wstring& txtComparingCon interruptionPoint(); //throw ThreadInterruption }; - haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath(), + haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath< LEFT_SIDE>(), file.getAbstractPath(), notifyUnbufferedIO, singleThread); //throw FileError statReporter.reportDelta(1, 0); }, acb); //throw ThreadInterruption @@ -491,7 +503,7 @@ std::list> 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 parallelOpsStatus; + std::map parallelOpsStatus; struct BinaryWorkload { @@ -503,15 +515,12 @@ std::list> ComparisonBuffer::compareByContent(co auto addToBinaryWorkload = [&](const AbstractPath& basePathL, const AbstractPath& basePathR, RingBuffer&& 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> ComparisonBuffer::compareByContent(co filesToCompareBytewise.push_back(file); } if (!filesToCompareBytewise.empty()) - addToBinaryWorkload(output.back()->getAbstractPath(), + addToBinaryWorkload(output.back()->getAbstractPath< LEFT_SIDE>(), output.back()->getAbstractPath(), std::move(filesToCompareBytewise)); //finish symlink categorization @@ -591,9 +600,7 @@ std::list> ComparisonBuffer::compareByContent(co ParallelOps& posL = bwl.parallelOpsL; ParallelOps& posR = bwl.parallelOpsR; - const size_t newTaskCount = numeric::min(posL.effectiveMax - posL.current, - posR.effectiveMax - posR.current, - bwl.filesToCompareBytewise.size()); + const size_t newTaskCount = std::min({ 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(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(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(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(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(dirLeft.first, dirLeft.second.first); - const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg); + const Zstringw* errorMsgNew = checkFailedRead(newFolder, conflictMsg ? conflictMsg : errorMsg); this->fillOneSide(dirLeft.second.second, errorMsgNew, newFolder); //recurse }, - [&](const FolderData& dirRight) //right only + [&](const FolderData& dirRight, const Zstringw* conflictMsg) { FolderPair& newFolder = output.addSubFolder(dirRight.first, dirRight.second.first); - const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg); + const Zstringw* errorMsgNew = checkFailedRead(newFolder, conflictMsg ? conflictMsg : errorMsg); this->fillOneSide(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 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 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& dirLocks, const std::vector& fpCfgList, - const std::map& deviceParallelOps, + const std::map& 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 pd = getPathDependency(folderPair.folderPathLeft, *fpCfg.filter.nameFilter, - folderPair.folderPathRight, *fpCfg.filter.nameFilter)) + if (std::optional 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 dirPathsExisting; + std::set folderPathsToLock; for (const AbstractPath& folderPath : resInfo.existingBaseFolders) if (std::optional nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further - dirPathsExisting.insert(*nativePath); + folderPathsToLock.insert(*nativePath); - dirLocks = std::make_unique(dirPathsExisting, warnings.warnDirectoryLockFailed, callback); + dirLocks = std::make_unique(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& dirLocks, //out const std::vector& fpCfgList, - const std::map& deviceParallelOps, + const std::map& 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(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(value); - return rv.first->second = std::forward(value); + return rv.first->second; } void process(const ContainerObject::FileList& currentFiles, const Zstring& parentRelPath, InSyncFolder::FileList& dbFiles) { - std::unordered_set toPreserve; //referencing fixed-in-memory std::map elements + std::set toPreserve; for (const FilePair& file : currentFiles) if (!file.isPairEmpty()) @@ -584,30 +584,29 @@ private: assert(file.getFileSize() == file.getFileSize()); //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(), - file.getFileId ()), - activeCmpVar_, - file.getFileSize())); - toPreserve.insert(&dbFile); + mapAddOrUpdate(dbFiles, file.getItemNameAny(), + InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(), + file.getFileId < LEFT_SIDE>()), + InSyncDescrFile(file.getLastWriteTime(), + file.getFileId ()), + activeCmpVar_, + file.getFileSize())); + 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()); // } } //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 toPreserve; + std::set toPreserve; for (const SymlinkPair& symlink : currentSymlinks) if (!symlink.isPairEmpty()) @@ -625,27 +624,26 @@ private: assert(getUnicodeNormalForm(symlink.getItemName()) == getUnicodeNormalForm(symlink.getItemName())); //create or update new "in-sync" state - InSyncSymlink& dbSymlink = mapAddOrUpdate(dbSymlinks, symlink.getItemNameAny(), - InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime()), - InSyncDescrLink(symlink.getLastWriteTime()), - activeCmpVar_)); - toPreserve.insert(&dbSymlink); + mapAddOrUpdate(dbSymlinks, symlink.getItemNameAny(), + InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime< LEFT_SIDE>()), + InSyncDescrLink(symlink.getLastWriteTime()), + 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()); // } } //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()) == getUnicodeNormalForm(folder.getItemName())); - - //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()) == getUnicodeNormalForm(folder.getItemName())); + + //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()); + + //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()) != getUnicodeNormalForm(folder.getItemName())) + preserveDbEntry(folder.getItemName()); } + } //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; // - using FileList = std::map; // key: file name - using SymlinkList = std::map; // + using FolderList = std::map; // + using FileList = std::map; // key: file name (ignoring Unicode normal forms) + using SymlinkList = std::map; // //------------------------------------------------------------------ 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 existing; std::set notExisting; std::map failedChecks; + + std::map normalizedPathsEx; //get rid of folder aliases (e.g. path differing in case) }; -FolderStatus getFolderStatusNonBlocking(const std::set& folderPaths, const std::map& 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& folderPaths, const std::map& 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> perDevicePaths; + std::map> 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>> futureInfo; + std::vector>>> futureDetails; - std::vector>> perDeviceThreads; - for (const auto& [rootPath, deviceFolderPaths] : perDevicePaths) + std::vector()>>> 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(AFS::getDisplayPath(rootPath))); + perDeviceThreads.emplace_back(parallelOps, "DirExist: " + utfTo(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 pt([folderPath, allowUserInteraction] //AbstractPath is thread-safe like an int! :) + std::packaged_task()> pt([folderPath, allowUserInteraction]() -> std::optional { //1. login to network share, open FTP connection, etc. AFS::connectNetworkFolder(folderPath, allowUserInteraction); //throw FileError - //2. check dir existence - return static_cast(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& folderPath const auto startTime = std::chrono::steady_clock::now(); FolderStatus output; + std::map 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& folderPath try { //call future::get() only *once*! otherwise: undefined behavior! - if (future.get()) //throw FileError - output.existing.insert(folderPath); + if (std::optional 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& activeLock = getActiveLock(iterGuid->second)) //returns null-lock if not found + auto itGuid = guidByPath_.find(lockFilePath); + if (itGuid != guidByPath_.end()) + if (const std::shared_ptr& 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& 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(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; //n:1 handle uppper/lower case correctly - using GuidToLockMap = std::map>; //1:1 std::shared_ptr 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 guidByPath_; //lockFilePath |-> GUID; n:1; locks can be referenced by a lockFilePath or alternatively a GUID + std::map> 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(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); + return beforeLast(utfTo(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(wxStandardPathsBase::Get().GetResourcesDir())); + //if (isPortableVersion()) + return appendSeparator(getExeFolderParentPath()); + //else //use OS' standard paths + // return appendSeparator(utfTo(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(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(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 +#include 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 parentPathL = AFS::getParentFolderPath(tmpPathL); - std::optional parentPathR = AFS::getParentFolderPath(tmpPathR); + std::optional parentPathL = AFS::getParentPath(tmpPathL); + std::optional 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(commonTrail); auto getLastComponent = [](const AbstractPath& itemPath) { - if (!AFS::getParentFolderPath(itemPath)) //= device root + if (!AFS::getParentPath(itemPath)) //= device root return AFS::getDisplayPath(itemPath); return utfTo(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 #include #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() const { return getAbstractPathL(); } +template <> inline AbstractPath PathInformation::getAbstractPath< LEFT_SIDE>() const { return getAbstractPathL(); } template <> inline AbstractPath PathInformation::getAbstractPath() const { return getAbstractPathR(); } -template <> inline Zstring PathInformation::getRelativePath() const { return getRelativePathL(); } +template <> inline Zstring PathInformation::getRelativePath< LEFT_SIDE>() const { return getRelativePathL(); } template <> inline Zstring PathInformation::getRelativePath() 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& ignoreTimeShiftMinutes) : @@ -303,7 +303,7 @@ public: template 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& 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 ignoreTimeShiftMinutes_; @@ -480,7 +480,7 @@ private: FileSystemObject (const FileSystemObject&) = delete; FileSystemObject& operator=(const FileSystemObject&) = delete; - AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath(), getRelativePath()); } + AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath< LEFT_SIDE>(), getRelativePath< LEFT_SIDE>()); } AbstractPath getAbstractPathR() const override { return AFS::appendRelPath(base().getAbstractPath(), getRelativePath()); } virtual void removeObjectL() = 0; @@ -593,8 +593,8 @@ public: bool isSymlinkSrc); private: - Zstring getRelativePathL() const override { return AFS::appendPaths(parent().getRelativePath(), getItemName(), FILE_NAME_SEPARATOR); } - Zstring getRelativePathR() const override { return AFS::appendPaths(parent().getRelativePath(), getItemName(), FILE_NAME_SEPARATOR); } + Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< LEFT_SIDE>(), getItemName< LEFT_SIDE>()); } + Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath(), getItemName()); } SyncOperation applyMoveOptimization(SyncOperation op) const; @@ -637,8 +637,8 @@ public: int64_t lastWriteTimeSrc); private: - Zstring getRelativePathL() const override { return AFS::appendPaths(parent().getRelativePath(), getItemName(), FILE_NAME_SEPARATOR); } - Zstring getRelativePathR() const override { return AFS::appendPaths(parent().getRelativePath(), getItemName(), FILE_NAME_SEPARATOR); } + Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< LEFT_SIDE>(), getItemName< LEFT_SIDE>()); } + Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath(), getItemName()); } 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(itemNameOldL); + propagateChangedItemName< LEFT_SIDE>(itemNameOldL); propagateChangedItemName(itemNameOldR); } @@ -917,9 +917,9 @@ template inline void ContainerObject::updateRelPathsRecursion(const FileSystemObject& fsAlias) { assert(SelectParam::ref(relPathL_, relPathR_) != //perf: only call if actual item name changed! - AFS::appendPaths(fsAlias.parent().getRelativePath(), fsAlias.getItemName(), FILE_NAME_SEPARATOR)); + nativeAppendPaths(fsAlias.parent().getRelativePath(), fsAlias.getItemName())); - SelectParam::ref(relPathL_, relPathR_) = AFS::appendPaths(fsAlias.parent().getRelativePath(), fsAlias.getItemName(), FILE_NAME_SEPARATOR); + SelectParam::ref(relPathL_, relPathR_) = nativeAppendPaths(fsAlias.parent().getRelativePath(), fsAlias.getItemName()); for (FolderPair& folder : subFolders_) folder.updateRelPathsRecursion(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(), FILE_NAME_SEPARATOR)), + relPathL_(nativeAppendPaths(fsAlias.parent().relPathL_, fsAlias.getItemName())), relPathR_( fsAlias.parent().relPathL_.c_str() == // fsAlias.parent().relPathR_.c_str() && //take advantage of FileSystemObject's Zstring reuse: - fsAlias.getItemName().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().c_str() ? // relPathL_ : //ternary-WTF! (implicit copy-constructor call!!) => no big deal for a Zstring - AFS::appendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName(), FILE_NAME_SEPARATOR)), + nativeAppendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName())), 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 summary; - //write header - std::wstring headerLine = formatTime(FORMAT_DATE); + const std::wstring tabSpace(4, L' '); //4, the one true space count for tabs + + std::wstring headerLine = formatTime(FORMAT_DATE); //+ L" [" + formatTime(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(s.totalTime).count(); summary.push_back(tabSpace + _("Total time:") + L" " + copyStringTo(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 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& logFilePathsToKeep, const std::function& 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 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&& 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(moBuf_, 0x950412de); //magic number + writeNumber(moBuf_, 0); //format version + writeNumber(moBuf_, transMapping.size()); //string count + writeNumber(moBuf_, headerSize); //string references offset: original + writeNumber(moBuf_, headerSize + 8 * transMapping.size()); //string references offset: translation + writeNumber(moBuf_, 0); //size of hashing table + writeNumber(moBuf_, 0); //offset of hashing table + + const int stringsOffset = headerSize + 2 * 8 * transMapping.size(); + std::string stringsList; + + for (const auto& [original, translation] : transMapping) + { + writeNumber(moBuf_, original.size()); //string length + writeNumber(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(item.second); + writeNumber(moBuf_, translation.size()); //string length + writeNumber(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 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(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 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& dirPathsExisting, //resolved paths - bool& warnDirectoryLockFailed, - ProcessCallback& pcb /*throw X*/) + LockHolder(const std::set& folderPaths, bool& warnDirectoryLockFailed, ProcessCallback& pcb /*throw X*/) { using namespace zen; - std::map failedLocks; + std::map 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& 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 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& foldersToRead, std::map& output, - const std::map& deviceParallelOps, + const std::map& deviceParallelOps, const TravErrorCb& onError, const TravStatusCb& onStatusUpdate, std::chrono::milliseconds cbInterval) { @@ -530,10 +530,10 @@ void fff::parallelDeviceTraversal(const std::set& 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> perDeviceFolders; + std::map> 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& 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(worker.size()); - const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, rootPath); + const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, afsDevice); std::map 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(threadIdx) + "]").c_str()); @@ -566,11 +566,10 @@ void fff::parallelDeviceTraversal(const std::set& foldersToRead, for (auto& [folderKey, folderVal] : workload) { - const std::vector 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(folderKey, *folderVal, acb, threadIdx, lastReportTime)); + assert(folderKey.folderPath.afsDevice == afsDevice); + travWorkload.emplace_back(folderKey.folderPath.afsPath, std::make_shared(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 #include #include -#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; using TravStatusCb = std::function< void (const std::wstring& statusLine, int itemsTotal)>; void parallelDeviceTraversal(const std::set& foldersToRead, std::map& output, - const std::map& deviceParallelOps, + const std::map& 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)); + pos_ = std::find_if_not(pos_, stream_.end(), zen::isWhiteSpace); 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)); + pos_ = std::find_if_not(pos_, stream_.end(), zen::isWhiteSpace); 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)); + auto digitEnd = std::find_if_not(pos_, stream_.end(), zen::isDigit); 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 +#include +#include +#include +#include + +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& masksFileFolder, std::vector& 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 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 +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(path, mask)) + return true; + return false; + } + + //*[letter] - pattern + ++mask; + for (;;) + { + path = cStringFind(path, m); + if (!path) + return false; + + ++path; + if (matchesMask(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 inline +bool matchesMask(const Zstring& name, const std::vector& masks) +{ + return std::any_of(masks.begin(), masks.end(), [&](const Zstring& mask) { return matchesMask(name.c_str(), mask.c_str()); }); +} + + +inline +bool matchesMaskBegin(const Zstring& name, const std::vector& masks) +{ + return std::any_of(masks.begin(), masks.end(), [&](const Zstring& mask) { return matchesMaskBegin(name.c_str(), mask.c_str()); }); +} +} + + +std::vector fff::splitByDelimiter(const Zstring& filterPhrase) +{ + //delimiters may be FILTER_ITEM_SEPARATOR or '\n' + std::vector 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(pathFmt, excludeMasksFileFolder) || //either full match on file or partial match on any parent folder + matchesMask(pathFmt, excludeMasksFolder)) //partial match on any parent folder only + return false; + + return matchesMask(pathFmt, includeMasksFileFolder) || + matchesMask(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(pathFmt, excludeMasksFileFolder) || + matchesMask(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(pathFmt, includeMasksFileFolder) && + !matchesMask(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(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 +#include +#include + + +namespace fff +{ +//------------------------------------------------------------------ +/* +Semantics of PathFilter: +1. using it creates a NEW folder hierarchy! -> must be considered by 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; //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 includeMasksFileFolder; // + std::vector includeMasksFolder; //upper-case + Unicode-normalized by construction + std::vector excludeMasksFileFolder; // + std::vector 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(Zstr("*"), excludePhrase); + if (filter.ref().isNull()) + return zen::makeSharedRef(); + return filter; +} + + +inline +FilterRef NameFilter::copyFilterAddingExclusion(const Zstring& excludePhrase) const +{ + auto tmp = zen::makeSharedRef(*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(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(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(includePhrase2, excludePhrase + Zstr("\n") + excludePhrase2); + if (filterTmp.ref().isNull()) + return zen::makeSharedRef(); + + return filterTmp; + } + else + { + if (NameFilter::isNull(includePhrase2, Zstring())) + return zen::makeSharedRef(includePhrase, excludePhrase + Zstr("\n") + excludePhrase2); + else + return zen::makeSharedRef(NameFilter(includePhrase, excludePhrase + Zstr("\n") + excludePhrase2), NameFilter(includePhrase2, Zstring())); + } +} + + +std::vector 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 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 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& deviceParallelOps, int formatVer) +void readConfig(const XmlIn& in, SyncConfig& syncCfg, std::map& 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& deviceParallelOps, int formatVer) +void readConfig(const XmlIn& in, LocalPairConfig& lpc, std::map& 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& deviceParallelOps, XmlOut& out) +void writeConfig(const SyncConfig& syncCfg, const std::map& 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& deviceParallelOps, XmlOut& out) +void writeConfig(const LocalPairConfig& lpc, const std::map& 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& output) +void getFolderAliasesRecursive(const Zstring& pathPhrase, std::set& output) { //3. environment variables: C:\Users\ -> %UserProfile% @@ -203,20 +203,20 @@ void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set fff::getDirectoryAliases(const Zstring& folderPathPhrase) +std::vector fff::getFolderPathAliases(const Zstring& folderPathPhrase) { - const Zstring dirPath = trimCpy(folderPathPhrase, true, false); + const Zstring dirPath = trimCpy(folderPathPhrase); if (dirPath.empty()) return {}; - std::set tmp; - getDirectoryAliasesRecursive(dirPath, tmp); + std::set 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 getDirectoryAliases(const Zstring& folderPathPhrase); //may block for slow USB sticks when resolving [] +std::vector getFolderPathAliases(const Zstring& folderPathPhrase); //may block for slow USB sticks when resolving [] } 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>& workload, - const std::map& deviceParallelOps, + const std::map& deviceParallelOps, const std::string& threadGroupName, ProcessCallback& callback /*throw X*/) { using namespace zen; - std::map*>> perDeviceWorkload; + std::map*>> 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 activeDeviceCount(perDeviceWorkload.size()); // - Protected*> deviceThreadGroupsShared; // + Protected*> deviceThreadGroupsShared; // //--------------------------------------------------------------------------------------------------------- - std::map deviceThreadGroups; //worker threads live here... + std::map 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(AFS::getDisplayPath(rootPath)), - statusPrio, - scheduleExtraTask)); + deviceThreadGroups.emplace(afsDevice, ThreadGroupContext(parallelOps, + threadGroupName + " " + utfTo(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 #include #include -#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& deviceParallelOps, const AbstractPath& ap) +size_t fff::getDeviceParallelOps(const std::map& deviceParallelOps, const AfsDevice& afsDevice) { - const AbstractPath& rootPath = AFS::getRootPath(ap); - auto it = deviceParallelOps.find(rootPath); + auto it = deviceParallelOps.find(afsDevice); return std::max(it != deviceParallelOps.end() ? it->second : 1, 1); } -void fff::setDeviceParallelOps(std::map& deviceParallelOps, const AbstractPath& ap, size_t parallelOps) +void fff::setDeviceParallelOps(std::map& 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& deviceParallelOps, const Zstring& folderPathPhrase) +size_t fff::getDeviceParallelOps(const std::map& deviceParallelOps, const Zstring& folderPathPhrase) { - return getDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase)); + return getDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase).afsDevice); } -void fff::setDeviceParallelOps(std::map& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps) +void fff::setDeviceParallelOps(std::map& 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& mainCfgs) lpc.localFilter = FilterConfig(); } - std::map mergedParallelOps; + std::map 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 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 additionalPairs; - std::map deviceParallelOps; //should only include devices with >= 2 parallel ops + std::map 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& deviceParallelOps, const AbstractPath& ap); -void setDeviceParallelOps( std::map& deviceParallelOps, const AbstractPath& ap, size_t parallelOps); -size_t getDeviceParallelOps(const std::map& deviceParallelOps, const Zstring& folderPathPhrase); -void setDeviceParallelOps( std::map& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps); - +size_t getDeviceParallelOps(const std::map& deviceParallelOps, const AfsDevice& afsDevice); +void setDeviceParallelOps( std::map& deviceParallelOps, const AfsDevice& afsDevice, size_t parallelOps); +size_t getDeviceParallelOps(const std::map& deviceParallelOps, const Zstring& folderPathPhrase); +void setDeviceParallelOps( std::map& 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 getItemTypeIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError -{ return parallelScope([ap] { return AFS::getItemTypeIfExists(ap); /*throw FileError*/ }, singleThread); } +std::optional 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(Zstr("%04x"), static_cast(getCrc16(generateGUID()))); const Zstring fileName = sourceFile.getItemName(); - 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(), singleThread_)) //throw FileError + if (parallel::itemStillExists(parentFolder->getAbstractPath(), 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(), singleThread_); /*throw FileError*/ } + try { sourceWasDeleted = !parallel::itemStillExists(file.getAbstractPath(), 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(), file.getAttributes() }, - file.getRelativePath(), statReporter, singleThread_); //throw FileError, X + file.getRelativePath(), statReporter, singleThread_); //throw FileError, X file.removeObject(); //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(), singleThread_); /*throw FileError*/ } + bool sourceExists = true; + try { sourceExists = !!parallel::itemStillExists(symlink.getAbstractPath(), 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(), singleThread_)) //throw FileError + if (parallel::itemStillExists(folder.getAbstractPath(), singleThread_)) //throw FileError { AsyncItemStatReporter statReporter(1, 0, acb_); try @@ -2170,9 +2170,8 @@ bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, Proc { if (baseFolder.isAvailable()) //copy file permissions { - if (std::optional parentPath = AFS::getParentFolderPath(baseFolderPath)) - if (AFS::getParentFolderPath(*parentPath)) //not device root - AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError + if (std::optional parentPath = AFS::getParentPath(baseFolderPath)) + AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError AFS::copyNewFolder(baseFolder.getAbstractPath(), baseFolderPath, copyFilePermissions); //throw FileError } @@ -2217,7 +2216,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime bool runWithBackgroundPriority, const std::vector& syncConfig, FolderComparison& folderCmp, - const std::map& deviceParallelOps, + const std::map& deviceParallelOps, WarningDialogs& warnings, ProcessCallback& callback) { @@ -2278,7 +2277,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime std::vector unresolvedConflicts; - std::vector> readWriteCheckBaseFolders; + std::vector> readWriteCheckBaseFolders; std::vector> 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 recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder! - std::set verCheckVersioningPaths; - std::vector> verCheckBaseFolderPaths; //hard filter creates new logical hierarchies for otherwise equal AbstractPath... + std::set verCheckVersioningPaths; + std::vector> 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())) + if (baseFolder.getAbstractPath< LEFT_SIDE>() == + baseFolder.getAbstractPath()) { 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(), &baseFolder.getFilter()); + verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(), &baseFolder.getFilter()); verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath(), &baseFolder.getFilter()); } //prepare: check if folders are used by multiple pairs in read/write access - readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath(), &baseFolder.getFilter(), writeLeft); + readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(), &baseFolder.getFilter(), writeLeft); readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath(), &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(*it)) //write access for (auto it2 = readWriteCheckBaseFolders.begin(); it2 != readWriteCheckBaseFolders.end(); ++it2) if (!std::get(*it2) || it < it2) //avoid duplicate comparisons - if (std::optional pd = getPathDependency(std::get(*it), *std::get(*it), - std::get(*it2), *std::get(*it2))) + if (std::optional pd = getPathDependency(std::get(*it), *std::get(*it), + std::get(*it2), *std::get(*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(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()), - getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath())); + size_t parallelOps = std::max(getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath< LEFT_SIDE>().afsDevice), + getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath().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& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise! FolderComparison& folderCmp, // - const std::map& deviceParallelOps, + const std::map& 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 fff::impl::parseVersionedFileName(const Zstring& fileName) { - const StringRef ext(find_last(fileName.begin(), fileName.end(), Zstr('.')), fileName.end()); + const StringRef 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 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 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 type = AFS::getItemTypeIfExists(fileDescr.path)) //throw FileError + if (std::optional 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 type = AFS::getItemTypeIfExists(folderPath)) //throw FileError + if (std::optional 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, LessFilePath>; //relPathOrig => +using VersionInfoMap = std::map>; //relPathOrig => //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& 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& limitFolders, - const std::map& deviceParallelOps, +void fff::applyVersioningLimit(const std::set& folderLimits, + const std::map& deviceParallelOps, ProcessCallback& callback /*throw X*/) { //--------- determine existing folder paths for traversal --------- std::set foldersToRead; + std::set folderLimitsNorm; //get rid of folder aliases (e.g. path differing in case) { - std::set folderPaths; - for (const VersioningLimitFolder& vlf : limitFolders) + std::set pathsToCheck; + std::vector 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(), SymLinkHandling::DIRECT })); + foldersToRead.insert(DirectoryKey({ getNormalizedPath(status, folderPath), makeSharedRef(), 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& 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& 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& 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& limitFolde //--------- remove excess file versions --------- Protected&> 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& limitFolde }, ctx.acb); if (errMsg.empty()) - if (std::optional parentPath = AFS::getParentFolderPath(ctx.itemPath)) + if (std::optional 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& limitFolde }, ctx.acb); if (errMsg.empty()) - if (std::optional parentPath = AFS::getParentFolderPath(ctx.itemPath)) + if (std::optional 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& limitFolders, - const std::map& deviceParallelOps, +void applyVersioningLimit(const std::set& folderLimits, + const std::map& 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 AFS::getParentFolderPath(const AbstractPath& ap) +std::optional AFS::getParentPath(const AbstractPath& ap) { - if (const std::optional parentAfsPath = getParentAfsPath(ap.afsPath)) - return AbstractPath(ap.afs, *parentAfsPath); + if (const std::optional parentAfsPath = getParentPath(ap.afsPath)) + return AbstractPath(ap.afsDevice, *parentAfsPath); return {}; } -std::optional AFS::getParentAfsPath(const AfsPath& afsPath) +std::optional AFS::getParentPath(const AfsPath& afsPath) { if (afsPath.value.empty()) return {}; @@ -61,25 +67,6 @@ std::optional 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 parentPath = AFS::getParentFolderPath(apTarget); + std::optional 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(Zstr("%04x"), static_cast(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(Zstr("%04x"), static_cast(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(/*ItemType =*/ getItemType(ap)); //throw FileError + const std::optional 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::itemStillExistsViaFolderTraversal(const AfsPath& afsPath) const //throw FileError { - const std::optional 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 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::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 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 type = AFS::getItemTypeIfExists(ap)) //throw FileError + if (std::optional 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; + 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& afsIn, const AfsPath& afsPathIn) : afs(afsIn), afsPath(afsPathIn) {} + AbstractPath(const AfsDevice& afsIn, const AfsPath& afsPathIn) : afsDevice(afsIn), afsPath(afsPathIn) {} - //template -> don't use forwarding constructor! => it circumvents AfsPath's explicit constructor - //AbstractPath(T1&& afsIn, T2&& afsPathIn) : afs(std::forward(afsIn)), afsPath(std::forward(afsPathIn)) { assert(isValidRelPath(afsPathIn)); } + //template -> don't use forwarding constructor: it circumvents AfsPath's explicit constructor! + //AbstractPath(T1&& afsIn, T2&& afsPathIn) : afsDevice(std::forward(afsIn)), afsPath(std::forward(afsPathIn)) {} -private: - friend struct AbstractFileSystem; - - std::shared_ptr 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 getParentPath(const AbstractPath& ap); + static std::optional 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 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 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 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; + enum class ItemType { FILE, FOLDER, SYMLINK, }; - struct PathStatus - { - ItemType existingType; - AbstractPath existingPath; //itemPath =: existingPath + relPath - std::vector 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 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 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& onBeforeFileDeletion, //optional const std::function& 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; struct StreamAttributes { @@ -167,13 +166,13 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //return value always bound: static std::unique_ptr 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 getOutputStream(const AbstractPath& ap, //throw FileError const uint64_t* streamSize, //optional const zen::IOCallback& notifyUnbufferedIO) // - { return std::make_unique(ap.afs->getOutputStream(ap.afsPath, streamSize, notifyUnbufferedIO), ap, streamSize); } + { return std::make_unique(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 /*relPath*/, std::shared_ptr /*throw X*/>>; + using TraverserWorkload = std::vector /*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& onFile, // const std::function& onFolder, //optional const std::function& 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& onUpdateGui) { return ap.afs->supportsRecycleBin(ap.afsPath, onUpdateGui); } //throw FileError + static bool supportsRecycleBin(const AbstractPath& ap, const std::function& 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 createRecyclerSession(const AbstractPath& ap) { return ap.afs->createRecyclerSession(ap.afsPath); } //throw FileError, return value must be bound! + static std::unique_ptr 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 getParentAfsPath(const AfsPath& afsPath); - struct PathStatusImpl - { - ItemType existingType; - AfsPath existingAfsPath; //afsPath =: existingAfsPath + relPath - std::vector relPath; // - }; - PathStatusImpl getPathStatusViaFolderTraversal(const AfsPath& afsPath) const; //throw FileError +protected: + std::optional itemStillExistsViaFolderTraversal(const AfsPath& afsPath) const; //throw FileError void traverseFolderFlat(const AfsPath& afsPath, //throw FileError - const std::function& onFile, // - const std::function& onFolder, //optional + const std::function& onFile, // + const std::function& onFolder, //optional const std::function& 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 /*throw X*/>>; - private: virtual std::optional 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 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 - void run(Task&& wi) + void run(Task&& wi, bool insertFront = false) { threadGroup_->run([this, wi = std::move(wi)] { try { this->returnResult({ wi, nullptr, wi.getResult() }); } //throw FileError catch (...) { this->returnResult({ wi, std::current_exception(), {} }); } - }); + }, insertFront); std::lock_guard dummy(lockResult_); ++resultsPending_; @@ -197,7 +197,9 @@ void GenericDirTraverser::evalResult(TaskResultreportItemError(e.toString(), result.wi.ctx.errorRetryCount, result.wi.ctx.errorItemName)) //throw X { case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY: - scheduler_.template run({ 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({ 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 template <> void GenericDirTraverser::evalResultValue(const GetDirDetails::Result& r, std::shared_ptr& cb /*throw X*/) { - for (const FsItemRaw& rawItem : r) - scheduler_.run({ 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() => 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(rawItem), TravContext{ rawItem.itemName, 0 /*errorRetryCount*/, cb }}, + true /*insertFront*/); + }); } @@ -292,7 +297,7 @@ void GenericDirTraverser::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& 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 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(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 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(L"Invalid path %x.", L"%x", fmtPath(resolvedPath))); - return AbstractPath(std::make_shared(comp->rootPath), AfsPath(comp->relPath)); + return AbstractPath(makeSharedRef(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(getAfs(apTarget)).getNativePath(getAfsPath(apTarget)); + const Zstring nativePathTarget = static_cast(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(getAfs(apTarget)).getNativePath(getAfsPath(apTarget)); + const Zstring& targetPath = static_cast(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(getAfs(apTarget)).getNativePath(getAfsPath(apTarget)); + const Zstring nativePathTarget = static_cast(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(getAfs(apTarget)).getNativePath(getAfsPath(apTarget)); + const Zstring nativePathTarget = static_cast(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 comp = parsePathComponents(nativePath)) - return AbstractPath(std::make_shared(comp->rootPath), AfsPath(comp->relPath)); + return AbstractPath(makeSharedRef(comp->rootPath), AfsPath(comp->relPath)); else //path syntax broken - return AbstractPath(std::make_shared(nativePath), AfsPath()); + return AbstractPath(makeSharedRef(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& filePaths) std::tie(detail.name, detail.cfgType, detail.isLastRunCfg) = [&] { - if (equalLocalPath(filePath, lastRunConfigPath_)) + if (equalNativePath(filePath, lastRunConfigPath_)) return std::make_tuple(utfTo(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& filePaths) void ConfigView::removeItems(const std::vector& filePaths) { - const std::set pathsSorted(filePaths.begin(), filePaths.end()); + const std::set 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& filePaths, bo grid.clearSelection(GridEventPolicy::DENY); - const std::set pathsSorted(filePaths.begin(), filePaths.end()); + const std::set pathsSorted(filePaths.begin(), filePaths.end()); std::optional 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; + using CfgFileList = std::map; CfgFileList cfgList_; std::vector 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 dirList; { - //add some aliases to allow user changing to volume name and back, if possible - std::vector aliases = getDirectoryAliases(utfTo(folderPathPhrase)); //may block when resolving [] + //allow user changing to volume name and back, if possible + std::vector aliases = getFolderPathAliases(utfTo(folderPathPhrase)); //may block when resolving [] std::transform(aliases.begin(), aliases.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfTo(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 parentPath = AFS::getParentFolderPath(itemPath)) + if (std::optional 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 #include #include -//#include #include #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(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 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 firstUnavailableFile; + AsyncFirstResult firstUnavailableFile; for (const Zstring& filePath : cfgFilePaths) firstUnavailableFile.addJob([filePath]() -> std::optional @@ -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(globalSettings.gui.mainDlg.cfgGridTopRowPos, //must be set *after* wxAuiManager::LoadPerspective() to have any effect - 0, m_gridCfgHistory->getRowCount() - 1)); + m_gridCfgHistory->scrollTo(std::clamp(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 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 firstMissingDir; + AsyncFirstResult firstMissingDir; for (const AbstractPath& folderPath : folderPathsToCheck) firstMissingDir.addJob([folderPath]() -> std::optional { @@ -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 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& gridRe if (auto prov = grid->getDataProvider()) { std::vector 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 MainDialog::getTreeSelection() const void MainDialog::copyToAlternateFolder(const std::vector& selectionLeft, const std::vector& selectionRight) { - std::vector rowsLeftTmp; - std::vector 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(); }); - - 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& 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& sel void MainDialog::deleteSelectedFiles(const std::vector& selectionLeft, const std::vector& selectionRight, bool moveToRecycler) { - std::vector rowsLeftTmp = selectionLeft; - std::vector rowsRightTmp = selectionRight; - erase_if(rowsLeftTmp, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); - erase_if(rowsRightTmp, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); - 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 -> vector 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& 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 nonEmptySelectionLeft = selectionLeft; std::vector nonEmptySelectionRight = selectionRight; - erase_if(nonEmptySelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); - erase_if(nonEmptySelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); + eraseIf(nonEmptySelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); + eraseIf(nonEmptySelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty(); }); menu.addSeparator(); @@ -2733,7 +2736,7 @@ void MainDialog::cfgHistoryRemoveObsolete(const std::vector& 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(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); auto defaultFileName = utfTo(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& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; + const std::map& 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(m_folderPathLeft ->GetValue())); - folderHistoryRight_->addItem(utfTo(m_folderPathRight->GetValue())); + folderHistoryLeft_ .ref().addItem(utfTo(m_folderPathLeft ->GetValue())); + folderHistoryRight_.ref().addItem(utfTo(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& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; + const std::map& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; std::set 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 dirLocks; if (globalCfg_.createLockFile) { - std::set availableDirPaths; + std::set folderPathsToLock; for (auto it = begin(folderCmp_); it != end(folderCmp_); ++it) { if (it->isAvailable()) //do NOT check directory existence again! if (std::optional nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath())) //restrict directory locking to native paths until further - availableDirPaths.insert(*nativeFolderPath); + folderPathsToLock.insert(*nativeFolderPath); if (it->isAvailable()) if (std::optional nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath())) - availableDirPaths.insert(*nativeFolderPath); + folderPathsToLock.insert(*nativeFolderPath); } - dirLocks = std::make_unique(availableDirPaths, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); //throw AbortProcess + dirLocks = std::make_unique(folderPathsToLock, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); //throw AbortProcess } //START SYNCHRONIZATION @@ -4749,8 +4753,8 @@ void MainDialog::insertAddFolderPair(const std::vector& 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(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(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 folderHistoryLeft_ = std::make_shared(); //shared by all wxComboBox dropdown controls - std::shared_ptr folderHistoryRight_ = std::make_shared(); //always bound! + zen::SharedRef folderHistoryLeft_ = zen::makeSharedRef(); //shared by all wxComboBox dropdown controls + zen::SharedRef folderHistoryRight_ = zen::makeSharedRef(); //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::~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::showSummary(SyncResult finalStatus, const double timeDelta = std::chrono::duration(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 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 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& rowsOnLeft, - const std::vector& rowsOnRight, + std::span rowsOnLeft, + std::span rowsOnRight, Zstring& lastUsedPath, const std::shared_ptr& folderHistory, bool& keepRelPaths, @@ -200,8 +200,8 @@ private: CopyToDialog::CopyToDialog(wxWindow* parent, - const std::vector& rowsOnLeft, - const std::vector& rowsOnRight, + std::span rowsOnLeft, + std::span rowsOnRight, Zstring& lastUsedPath, const std::shared_ptr& folderHistory, bool& keepRelPaths, @@ -291,8 +291,8 @@ void CopyToDialog::OnOK(wxCommandEvent& event) ReturnSmallDlg::ButtonPressed fff::showCopyToDialog(wxWindow* parent, - const std::vector& rowsOnLeft, - const std::vector& rowsOnRight, + std::span rowsOnLeft, + std::span rowsOnRight, Zstring& lastUsedPath, std::vector& folderPathHistory, size_t historySizeMax, @@ -315,8 +315,8 @@ class DeleteDialog : public DeleteDlgGenerated { public: DeleteDialog(wxWindow* parent, - const std::vector& rowsOnLeft, - const std::vector& rowsOnRight, + std::span rowsOnLeft, + std::span rowsOnRight, bool& useRecycleBin); private: @@ -329,8 +329,8 @@ private: void updateGui(); - const std::vector& rowsToDeleteOnLeft_; - const std::vector& rowsToDeleteOnRight_; + const std::span rowsToDeleteOnLeft_; + const std::span 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& rowsOnLeft, - const std::vector& rowsOnRight, + std::span rowsOnLeft, + std::span 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& rowsOnLeft, - const std::vector& rowsOnRight, + std::span rowsOnLeft, + std::span 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& rowsOnLeft, - const std::vector& rowsOnRight, + std::span rowsOnLeft, + std::span rowsOnRight, Zstring& lastUsedPath, std::vector& folderPathHistory, size_t historySizeMax, @@ -37,8 +37,8 @@ ReturnSmallDlg::ButtonPressed showCopyToDialog(wxWindow* parent, bool& overwriteIfExists); ReturnSmallDlg::ButtonPressed showDeleteDialog(wxWindow* parent, - const std::vector& rowsOnLeft, - const std::vector& rowsOnRight, + std::span rowsOnLeft, + std::span 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 devicePathsForEdit_; //helper data for deviceParallelOps - std::map deviceParallelOps_; // + std::set devicesForEdit_; //helper data for deviceParallelOps + std::map 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(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(devicePathsForEdit_.size()) - static_cast(fgSizerPerf->GetItemCount() / 2); + const int rowsToCreate = static_cast(devicesForEdit_.size()) - static_cast(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 (fgSizerPerf->GetItem(i * 2 )->GetWindow()); wxStaticText* staticTextDevice = dynamic_cast(fgSizerPerf->GetItem(i * 2 + 1)->GetWindow()); - spinCtrlParallelOps->SetValue(static_cast(getDeviceParallelOps(deviceParallelOps_, devPath))); - staticTextDevice->SetLabel(AFS::getDisplayPath(devPath)); + spinCtrlParallelOps->SetValue(static_cast(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(localPairCfg_.size()) - 1); + newPairIndexToShow = std::clamp(newPairIndexToShow, -1, static_cast(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 deviceParallelOps; + std::map 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(numeric::round(fraction * pixelCount), 0, pixelCount); + const int startFillPixel = std::clamp(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& baseObj) + eraseIf(folderCmp_, [](const std::shared_ptr& baseObj) { return AFS::isNullPath(baseObj->getAbstractPath< LEFT_SIDE>()) && AFS::isNullPath(baseObj->getAbstractPath()); 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> readyTasks; //Reentrancy; access to AsyncTasks::add is not protected! => evaluate outside erase_if + std::vector> readyTasks; //Reentrancy; access to AsyncTasks::add is not protected! => evaluate outside eraseIf - erase_if(tasks_, [&](std::unique_ptr& task) + eraseIf(tasks_, [&](std::unique_ptr& 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(yFrom, 0, logicalHeight - 1); - numeric::clamp(yTo, 0, logicalHeight - 1); + yFrom = std::clamp(yFrom, 0, logicalHeight - 1); + yTo = std::clamp(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(yFrom, 0, logicalHeight - 1); - numeric::clamp(yTo, 0, logicalHeight - 1); + yFrom = std::clamp(yFrom, 0, logicalHeight - 1); + yTo = std::clamp(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(row, 0, rowCount - 1); + row = std::clamp(row, 0, rowCount - 1); setGridCursor(row, GridEventPolicy::ALLOW); } }; @@ -1508,7 +1508,7 @@ void Grid::onKeyDown(wxKeyEvent& event) { if (rowCount > 0) { - numeric::clamp(row, 0, rowCount - 1); + row = std::clamp(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(rowFirst, 0, rowCount); - numeric::clamp(rowLast, 0, rowCount); + rowFirst = std::clamp(rowFirst, 0, rowCount); + rowLast = std::clamp(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(rowFirst, 0, selected_.size()); - numeric::clamp(rowLast, 0, selected_.size()); + rowFirst = std::clamp(rowFirst, 0, selected_.size()); + rowLast = std::clamp(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 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(std::ceil(fastFromDIP(1000) / 1000.0), 1, xbrz::SCALE_FACTOR_MAX); + const int hqScale = std::clamp(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(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 T abs(T value); template auto dist(T a, T b); template int sign(T value); //returns one of {-1, 0, 1} -template T min(T a, T b, T c); -template T max(T a, T b, T c); template bool isNull(T value); -template void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal -template T clampCpy(T val, T minVal, T maxVal); -//std::clamp() available with C++17 - template //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 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 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 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 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 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 watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr" + std::map 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::getChanges(const std::functionwatchDescrs.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 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 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(::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 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 zen::itemStillExists(const Zstring& itemPath) //throw FileError { - const std::optional 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 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 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 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(/*ItemType =*/ getItemType(dirPath)); //throw FileError + const std::optional 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 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 getItemTypeIfExists(const Zstring& itemPath); //throw FileError - -struct PathStatus -{ - ItemType existingType; - Zstring existingPath; //itemPath =: existingPath + relPath - std::vector 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 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 //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 span +{ +public: + template + span(Iterator first, Iterator last) : size_(last - first), data_(first != last ? &*first : nullptr) {} + + template + 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 type = getItemTypeIfExists(itemPath); //throw FileError + const std::optional 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 -#include -#include #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 + void push_front(U&& value) + { + reserve(size_ + 1); //throw ? + ::new (getBufPtr() + getBufPos(capacity_ - 1)) T(std::forward(value)); //throw ? + ++size_; + bufStart_ = getBufPos(capacity_ - 1); + } template void push_back(U&& value) { - checkInvariants(); reserve(size_ + 1); //throw ? ::new (getBufPtr() + getBufPos(size_)) T(std::forward(value)); //throw ? ++size_; @@ -54,15 +62,21 @@ public: void pop_front() { - checkInvariants(); - if (empty()) - throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo(__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 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(__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(rawMem_.get()); } + /**/ T* getBufPtr() { return reinterpret_cast(rawMem_.get()); } const T* getBufPtr() const { return reinterpret_cast(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::iterator; using const_iterator = std::vector::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> buffer_ = std::make_shared>(); //always bound! + SharedRef> buffer_ = makeSharedRef>(); //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 #include #include +#include #include #include #include "string_traits.h" -#include "build_info.h" +//#include "build_info.h" //enhancements for @@ -22,13 +23,13 @@ namespace zen { //erase selected elements from any container: template -void erase_if(std::vector& v, Predicate p); +void eraseIf(std::vector& v, Predicate p); template -void erase_if(std::set& s, Predicate p); +void eraseIf(std::set& s, Predicate p); template -void erase_if(std::map& m, Predicate p); +void eraseIf(std::map& m, Predicate p); //append STL containers template @@ -48,14 +49,14 @@ void removeDuplicates(std::vector& v, CompLess less); //binary search returning an iterator template -Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less); +Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less); template -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 -BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1, +BidirectionalIterator1 searchLast(BidirectionalIterator1 first1, BidirectionalIterator1 last1, BidirectionalIterator2 first2, BidirectionalIterator2 last2); template Num hashBytes (ByteIterator first, ByteIterator last); @@ -74,17 +75,49 @@ struct StringHash }; -//why, oh wy is there no std::optional::get()??? +//why, oh why is there no std::optional::get()??? template inline T* get( std::optional& opt) { return opt ? &*opt : nullptr; } template inline const T* get(const std::optional& opt) { return opt ? &*opt : nullptr; } +//=========================================================================== +template class SharedRef; +template SharedRef makeSharedRef(Args&& ... args); + +template +class SharedRef //why is there no std::shared_ref??? +{ +public: + SharedRef() = delete; //no suprise memory allocations => always construct with makeSharedRef() + + template + SharedRef(const SharedRef& other) : ref_(other.ref_) {} + + /**/ T& ref() { return *ref_; }; + const T& ref() const { return *ref_; }; + + std::shared_ptr ptr() { return ref_; }; + +private: + explicit SharedRef(std::shared_ptr&& ptr) : ref_(std::move(ptr)) { assert(ref_); } + + template friend SharedRef makeSharedRef(Args&& ... args); + template friend class SharedRef; + + std::shared_ptr ref_; //always bound +}; + +template inline +SharedRef makeSharedRef(Args&&... args) { return SharedRef(std::make_shared(std::forward(args)...)); } +//=========================================================================== + + //######################## implementation ######################## template inline -void erase_if(std::vector& v, Predicate p) +void eraseIf(std::vector& v, Predicate p) { v.erase(std::remove_if(v.begin(), v.end(), p), v.end()); } @@ -93,7 +126,7 @@ void erase_if(std::vector& v, Predicate p) namespace impl { template 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 inline -void erase_if(std::set& 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& s, Predicate p) { impl::setOrMapEraseIf(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!! template inline -void erase_if(std::map& m, Predicate p) { impl::set_or_map_erase_if(m, p); } +void eraseIf(std::map& m, Predicate p) { impl::setOrMapEraseIf(m, p); } template inline @@ -147,7 +180,7 @@ void removeDuplicates(std::vector& v) template 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::iterator_category, std::random_access_iterator_tag>); @@ -160,7 +193,7 @@ Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess l template 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 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::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::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 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(c) < 128 && - std::isspace(static_cast(c)) != 0; -} - -template <> inline -bool isWhiteSpace(wchar_t c) +template inline +bool isWhiteSpace(Char c) { + static_assert(std::is_same_v || std::is_same_v); assert(c != 0); //std C++ does not consider 0 as white space - return std::iswspace(c) != 0; + return c == static_cast(' ') || (static_cast('\t') <= c && c <= static_cast('\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 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 || std::is_same_v); return static_cast('0') <= c && c <= static_cast('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('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)); -> this is NO assert situation + break; //assert(std::all_of(iter, last, isWhiteSpace)); -> 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& 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 GetFirstResult +class AsyncFirstResult { public: - GetFirstResult(); + AsyncFirstResult(); template void addJob(Fun&& f); //f must return a std::optional 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 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 GetFirstResult::AsyncResult +class AsyncFirstResult::AsyncResult { public: //context: worker threads @@ -361,12 +364,12 @@ private: template inline -GetFirstResult::GetFirstResult() : asyncResult_(std::make_shared()) {} +AsyncFirstResult::AsyncFirstResult() : asyncResult_(std::make_shared()) {} template template inline -void GetFirstResult::addJob(Fun&& f) //f must return a std::optional containing a value on success +void AsyncFirstResult::addJob(Fun&& f) //f must return a std::optional containing a value on success { std::thread t([asyncResult = this->asyncResult_, f = std::forward(f)] { asyncResult->reportFinished(f()); }); ++jobsTotal_; @@ -376,11 +379,11 @@ void GetFirstResult::addJob(Fun&& f) //f must return a std::optional conta template template inline -bool GetFirstResult::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); } +bool AsyncFirstResult::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); } template inline -std::optional GetFirstResult::get() const { return asyncResult_->getResult(jobsTotal_); } +std::optional AsyncFirstResult::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; } //------------------------------------------------------------------------------------------ @@ -91,19 +81,51 @@ 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)); + pos_ = std::find_if_not(pos_, stream_.end(), isWhiteSpace); 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(name)); + log_.ref().notifyMissingAttribute(getNameFormatted(), utfTo(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 getErrorsAs() const { std::vector 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(str); }); return output; } private: - XmlIn(const std::vector& siblingList, const std::string& elementNameFmt, const std::shared_ptr& sharedlog) : + XmlIn(const std::vector& siblingList, const std::string& elementNameFmt, const SharedRef& sharedlog) : refList_(siblingList), formattedName_(elementNameFmt), log_(sharedlog) { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); } @@ -423,7 +423,7 @@ private: std::vector 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 log_{ std::make_shared() }; //always bound + mutable SharedRef log_ = makeSharedRef(); }; -- cgit