diff options
author | B Stack <bgstack15@gmail.com> | 2018-11-15 11:22:00 +0000 |
---|---|---|
committer | B Stack <bgstack15@gmail.com> | 2018-11-15 11:22:00 +0000 |
commit | 77c5c2503d459288720a8894349ac74e5eeec7c6 (patch) | |
tree | 30bf08d782d58174a0ca212b2e4b172fabd9c42c | |
parent | Merge branch '10.5' into 'master' (diff) | |
parent | 10.6 (diff) | |
download | FreeFileSync-77c5c2503d459288720a8894349ac74e5eeec7c6.tar.gz FreeFileSync-77c5c2503d459288720a8894349ac74e5eeec7c6.tar.bz2 FreeFileSync-77c5c2503d459288720a8894349ac74e5eeec7c6.zip |
Merge branch '10.6' into 'master'10.6
10.6
See merge request opensource-tracking/FreeFileSync!3
127 files changed, 5361 insertions, 3741 deletions
diff --git a/Changelog.txt b/Changelog.txt index 004aca9d..185970a9 100755 --- a/Changelog.txt +++ b/Changelog.txt @@ -1,9 +1,31 @@ +FreeFileSync 10.6 [2018-11-12] +------------------------------ +Detect and skip traversing folder path aliases +Report conflict when names differ only in Unicode normalization +Unified 32 and 64 bit into single package (Linux) +Notarized application package (macOS) +Save configuration files in user-specific paths (Linux) +Use XDG-style config file paths (Linux) +Fixed (fake) intermittent hangs during comparison (Linux, macOS) +Detect SMB mount points as separate devices (Linux) +Consider /mnt subfolders as device root paths (Linux) +Create missing default log folder upon first run +Don't consider final status for error/warning count +Discard invalid SFTP session after max channel determination +Fixed main dialog position not being remembered (Linux) +Fixed imprecise FTP times due to MLST parsing issue +Fixed application menu not being localized (macOS) +Fixed temp file name hitting file system length limitations +Fixed fatal errors not being written to console (Debian Linux) +Updated translation files + + FreeFileSync 10.5 [2018-10-11] ------------------------------ New file matching algorithm considering Unicode normalization User-configurable timeout for FTP and SFTP connections -Obsoleted old CHM manual in favor of PDF Ignore case sensitivity during filter matching (Linux) +Obsoleted old CHM manual in favor of PDF Unicode-normalized and faster case-insensitive grid search New button to save current view filter settings as default Both slash and backslash can be used in filter expressions diff --git a/FreeFileSync/Build/Languages/arabic.lng b/FreeFileSync/Build/Languages/arabic.lng index 97143023..1d4f1ac1 100755 --- a/FreeFileSync/Build/Languages/arabic.lng +++ b/FreeFileSync/Build/Languages/arabic.lng @@ -164,6 +164,9 @@ <source>Items differ in attributes only</source> <target>العناصر مختلفة في السمات فقط</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>الاسم %x تم استخدامه اكثر من مرة فى نفس المجلد.</target> + <source>Resolving symbolic link %x</source> <target>جاري حل المسار الرمزي %x</target> @@ -191,9 +194,6 @@ <source>File time tolerance</source> <target>التفاوت في وقت الملف</target> -<source>Folder access timeout</source> -<target>مهلة وصول المجلد</target> - <source>Run with background priority</source> <target>تشغيل مع أولوية في الخلفية</target> @@ -342,8 +342,11 @@ <source>Update attributes on right</source> <target>تحديث السمات على اليسار</target> -<source>Warning</source> -<target>تحذير</target> +<source>Errors:</source> +<target>أخطاء:</target> + +<source>Warnings:</source> +<target>تحذيرات:</target> <source>Items processed:</source> <target>معالجة العناصر:</target> @@ -354,6 +357,9 @@ <source>Total time:</source> <target>مجموع الوقت:</target> +<source>Warning</source> +<target>تحذير</target> + <source>Stopped</source> <target>توقف</target> @@ -363,6 +369,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>حدث خطأ أثناء تحليل الملف %x، الصف %y، و العمود %z.</target> +<source>Services</source> +<target>خدمات</target> + +<source>Show All</source> +<target>اظهر الكل</target> + +<source>Hide Others</source> +<target>اخفاء الاخرين</target> + +<source>Hide %x</source> +<target>اخفاء %x</target> + +<source>Quit %x</source> +<target>الغاء %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>لا يمكن وضع قفل المسار للمجلدات الأتية:</target> @@ -382,11 +403,11 @@ <source>Cannot read directory %x.</source> <target>لا يمكن قراءة الدليل %x.</target> -<source>/sec</source> -<target>\ثانية</target> +<source>%x/sec</source> +<target>%x ثانية</target> -<source>%x items/sec</source> -<target>%x عنصر\الثانية</target> +<source>%x items</source> +<target>%x عناصر</target> <source>Show in Explorer</source> <target>إظهار في المستكشف</target> @@ -535,11 +556,11 @@ <source>Generating database...</source> <target>إنشاء قاعدة بيانات...</target> -<source>Searching for excess file versions:</source> -<target>البحث عن اصدارات الملف الزائدة:</target> +<source>Searching for old file versions:</source> +<target>البحث عن اصدارات قديمة للملف:</target> -<source>Removing excess file versions:</source> -<target>إزالة اصدارات الملف الزائدة:</target> +<source>Removing old file versions:</source> +<target>ازالة اصدارات قديمة للملف:</target> <source>Unable to create time stamp for versioning:</source> <target>تعذر إنشاء بصمة زمنية من أجل المفاضلة الزمنية:</target> @@ -597,6 +618,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>تعذر نقل %x إلى سلة المحذوفات.</target> +<source>Unable to access %x.</source> +<target>لا يمكن الوصول إلى %x.</target> + +<source>Authentication completed.</source> +<target>نجحت المصادقة.</target> + +<source>Authentication failed.</source> +<target>فشلت المصادقة.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>يمكنك غلق هذه الصفحة والاستمرار باستخدام FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>الخادم ارسل الخطأ:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>لا يمكن تحديد مساحة القرص الحرة لـ %x.</target> + <source>Cannot find %x.</source> <target>لا يمكن العثور على %x.</target> @@ -609,18 +648,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>لا يمكن حذف الرابط الرمزي %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>لا يمكن تحديد مساحة القرص الحرة لـ %x.</target> - <source>Incorrect command line:</source> <target>سطر أوامر خاطئ:</target> <source>The server does not support authentication via %x.</source> <target>لا يدعم الخادم المصادقة عبر %x.</target> -<source>Unable to access %x.</source> -<target>لا يمكن الوصول إلى %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -653,28 +686,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>فشل فتح قناة SFTP رقم %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>0 byte</pluralform> -<pluralform>1 byte</pluralform> -<pluralform>2 bytes</pluralform> -<pluralform>%x bytes</pluralform> -<pluralform>%x bytes</pluralform> -<pluralform>%x bytes</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>سحب و إفلات</target> @@ -788,6 +799,28 @@ The command is triggered if: <source>&Retry</source> <target>إ&عادة المحاولة</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>0 byte</pluralform> +<pluralform>1 byte</pluralform> +<pluralform>2 bytes</pluralform> +<pluralform>%x bytes</pluralform> +<pluralform>%x bytes</pluralform> +<pluralform>%x bytes</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>تحميل...</target> @@ -937,7 +970,7 @@ The command is triggered if: <target>&حفظ كمهمة دفعية...</target> <source>Show &log</source> -<target></target> +<target>أظهر &السجل</target> <source>Start &comparison</source> <target>بدأ الم&قارنة</target> @@ -999,9 +1032,6 @@ The command is triggered if: <source>Access online storage</source> <target>الوصول إلى التخزين عبر الإنترنت</target> -<source>Swap sides</source> -<target>مبادلة الجانبين</target> - <source>Close search bar</source> <target>إغلاق شريط البحث</target> @@ -1026,6 +1056,9 @@ The command is triggered if: <source>View type:</source> <target>عرض النوع:</target> +<source>Save as default</source> +<target>حفظ كافتراضي</target> + <source>Select view:</source> <target>اختيار نمط العرض:</target> @@ -1083,6 +1116,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>تعامل مع التوقيت الصيفي</target> +<source>Ignore errors</source> +<target>تجاهل الأخطاء</target> + +<source>Retry count:</source> +<target>تعداد محاولات الإعادة:</target> + +<source>Delay (in seconds):</source> +<target>التأخير (بالثواني):</target> + <source>Performance improvements:</source> <target>تحسينات الأداء:</target> @@ -1157,17 +1199,11 @@ The command is triggered if: <source>Last x days:</source> <target>اخر x أيام:</target> -<source>Ignore errors</source> -<target>تجاهل الأخطاء</target> +<source>&Override default log path:</source> +<target>&تجاوز المسار الافتراضى للسجل:</target> -<source>Retry count:</source> -<target>تعداد محاولات الإعادة:</target> - -<source>Delay (in seconds):</source> -<target>التأخير (بالثواني):</target> - -<source>Run a command after synchronization:</source> -<target>تشغيل أمر بعد المزامنة:</target> +<source>Run a command:</source> +<target>تشغيل الأمر:</target> <source>OK</source> <target>موافق</target> @@ -1217,6 +1253,9 @@ The command is triggered if: <source>Directory on server:</source> <target>المسار على الخادم:</target> +<source>Access timeout (in seconds):</source> +<target>أقصى زمن للوصول(ثانية):</target> + <source>SFTP channels per connection:</source> <target>قنوات SFTP لكل اتصال:</target> @@ -1295,12 +1334,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>إحباط المزامنة عند أول خطأ</target> -<source>Save log:</source> -<target>حفظ السجل:</target> - -<source>Limit number of log files:</source> -<target>حد عدد ملفات السجل:</target> - <source>How can I schedule a batch job?</source> <target>كيف يمكنني جدولة مهمة دفعية؟</target> @@ -1337,8 +1370,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>إعادة إظهار جميع التنبهات و نوافذ الحوار التي تم إخفاؤها</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>المسار الافتراضى للسجل:</target> + +<source>&Delete logs after x days:</source> +<target>&ازالة السجلات بعد x يوم:</target> <source>Customize context menu:</source> <target>تخصيص القائمة المحلية:</target> @@ -1349,8 +1385,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>الا&فتراضي</target> -<source>Feedback and suggestions are welcome</source> -<target>التعليقات و الاقتراحات موضع ترحيب</target> +<source>Feedback and suggestions are welcome:</source> +<target>ردود الفعل والاقتراحات مرحب بها:</target> <source>Home page</source> <target>الصفحة الرئيسية</target> @@ -1376,8 +1412,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>الرماز المصدري مكتوب بلغة C++ باستخدام:</target> -<source>Published under the GNU General Public License</source> -<target>نشر تحت رخصة GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>نشر باستخدام رخصة جنو العمومية العامة:</target> <source>Many thanks for localization:</source> <target>شكرا جزيلا للترجمة:</target> @@ -1434,7 +1470,7 @@ This guarantees a consistent state even in case of a serious error. <target>معلومات</target> <source>No log entries</source> -<target></target> +<target>لا يوجد سجلات</target> <source>Select all</source> <target>اختيار الجميع</target> @@ -1460,6 +1496,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>نظرة عامة</target> +<source>Swap sides</source> +<target>مبادلة الجانبين</target> + <source>Show "%x"</source> <target>إظهار "%x"</target> @@ -1650,9 +1689,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>إظهار الملفات التي تم فلترتها أو استبعادها بشكل مؤقت</target> -<source>Save as default</source> -<target>حفظ كافتراضي</target> - <source>Filter</source> <target>عامل الفلترة</target> @@ -2018,12 +2054,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>فشل تصفح سلة المهملات من أجل الملف %x.</target> -<source>The following XML elements could not be read:</source> -<target>عناصر XML التالية لا يمكن قراءتها:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>ملف التكوين %x غير مكتمل. سيتم إعادة تعيين العناصر المفقودة إلى قيمها الافتراضية.</target> - <source>Prepare installation</source> <target>الاستعداد للتثبيت</target> diff --git a/FreeFileSync/Build/Languages/bulgarian.lng b/FreeFileSync/Build/Languages/bulgarian.lng index 935fa43f..6334a693 100755 --- a/FreeFileSync/Build/Languages/bulgarian.lng +++ b/FreeFileSync/Build/Languages/bulgarian.lng @@ -155,11 +155,14 @@ <target>Размер:</target> <source>Content comparison was skipped for excluded files.</source> -<target>Сравняването на съдържанието е прескочено за изключените файлове.</target> +<target>Сравняването на съдържание се прескача за изключените файлове.</target> <source>Items differ in attributes only</source> <target>Елементите се различават само по атрибути</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Името %x се използва от повече от един елемент в папката.</target> + <source>Resolving symbolic link %x</source> <target>Проследява символна връзка %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Толеранс на файловото време</target> -<source>Folder access timeout</source> -<target>Време за достъп до папката</target> - <source>Run with background priority</source> <target>Изпълнявай с фонов приоритет</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Актуализирай атрибутите на десния елемент</target> -<source>Warning</source> -<target>Предупреждение</target> +<source>Errors:</source> +<target>Грешки:</target> + +<source>Warnings:</source> +<target>Предупреждения:</target> <source>Items processed:</source> <target>Обработени елементи:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Общо време:</target> +<source>Warning</source> +<target>Предупреждение</target> + <source>Stopped</source> <target>Спряно</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Грешка при анализ на файл %x, ред %y, колона %z.</target> +<source>Services</source> +<target>Услуги</target> + +<source>Show All</source> +<target>Покажи всички</target> + +<source>Hide Others</source> +<target>Скрий другите</target> + +<source>Hide %x</source> +<target>Скрий %x</target> + +<source>Quit %x</source> +<target>Край на %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Не може да заключи директориите за следните папки:</target> @@ -370,17 +391,17 @@ <source>Cannot read directory %x.</source> <target>Не може да прочете директория %x.</target> -<source>/sec</source> -<target>/сек.</target> +<source>%x/sec</source> +<target>%x/сек.</target> -<source>%x items/sec</source> -<target>%x елем./сек.</target> +<source>%x items</source> +<target>%x елемента</target> <source>Show in Explorer</source> <target>Покажи в Експлорера</target> <source>Open with default application</source> -<target>Отвори с приложение по подразбиране</target> +<target>Отвори с подразбираното приложение</target> <source>Browse directory</source> <target>Преглед на директорията</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Създава база данни...</target> -<source>Searching for excess file versions:</source> -<target>Търси излишни файлови версии:</target> +<source>Searching for old file versions:</source> +<target>Търси стари версии на файлове:</target> -<source>Removing excess file versions:</source> -<target>Премахва излишни файлови версии:</target> +<source>Removing old file versions:</source> +<target>Премахва стари версии на файлове:</target> <source>Unable to create time stamp for versioning:</source> <target>Не може да маркира времето на версиите:</target> @@ -585,6 +606,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Не може да премести %x в кошчето.</target> +<source>Unable to access %x.</source> +<target>Няма достъп до %x.</target> + +<source>Authentication completed.</source> +<target>Аутентификацията приключи.</target> + +<source>Authentication failed.</source> +<target>Аутентификацията неуспешна.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Може да затворите тази страница и да продължите с FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Сървърът връща грешка:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Не може да определи свободното дисково пространство за %x.</target> + <source>Cannot find %x.</source> <target>Не е намерен %x.</target> @@ -597,18 +636,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Не може да изтрие символната връзка %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Не може да определи свободното дисково пространство за %x.</target> - <source>Incorrect command line:</source> <target>Невалиден команден ред:</target> <source>The server does not support authentication via %x.</source> <target>Сървърът не поддържа идентификация чрез %x.</target> -<source>Unable to access %x.</source> -<target>Няма достъп до %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Не може да отвори SFTP-канал номер %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 байт</pluralform> -<pluralform>%x байта</pluralform> -</target> - -<source>%x MB</source> -<target>%x МБ</target> - -<source>%x KB</source> -<target>%x КБ</target> - -<source>%x GB</source> -<target>%x ГБ</target> - <source>Drag && drop</source> <target>Влачете и пуснете</target> @@ -764,6 +779,24 @@ The command is triggered if: <source>&Retry</source> <target>&Повтори</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 байт</pluralform> +<pluralform>%x байта</pluralform> +</target> + +<source>%x MB</source> +<target>%x МБ</target> + +<source>%x KB</source> +<target>%x КБ</target> + +<source>%x GB</source> +<target>%x ГБ</target> + <source>Loading...</source> <target>Зареждане...</target> @@ -909,7 +942,7 @@ The command is triggered if: <target>Запази &като пакетна задача...</target> <source>Show &log</source> -<target></target> +<target>Покажи &log</target> <source>Start &comparison</source> <target>Почни &сравняване</target> @@ -921,10 +954,10 @@ The command is triggered if: <target>Настройки на &филтъра</target> <source>S&ynchronization settings</source> -<target>Настройки на с&инхронизиране</target> +<target>Настройки на с&инхронизация</target> <source>Start &synchronization</source> -<target>Почни &синхронизиране</target> +<target>Почни &синхронизация</target> <source>&Actions</source> <target>&Действия</target> @@ -951,7 +984,7 @@ The command is triggered if: <target>&Провери за обновления сега</target> <source>Check &automatically once a week</source> -<target>Провери автоматично &ежеседмично</target> +<target>Проверявай автоматично &ежеседмично</target> <source>Cancel</source> <target>Отказ</target> @@ -969,10 +1002,7 @@ The command is triggered if: <target>Отнеми двойка папки</target> <source>Access online storage</source> -<target>Достъп до онлайн-запазване</target> - -<source>Swap sides</source> -<target>Размени страните</target> +<target>Достъп до онлайн-съхранение</target> <source>Close search bar</source> <target>Затвори полето за търсене</target> @@ -998,8 +1028,11 @@ The command is triggered if: <source>View type:</source> <target>Тип изглед:</target> +<source>Save as default</source> +<target>Запази като подразбирано</target> + <source>Select view:</source> -<target>Избери изглед:</target> +<target>Изберете изглед:</target> <source>Statistics:</source> <target>Статистика:</target> @@ -1055,6 +1088,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>Отчети лятното време</target> +<source>Ignore errors</source> +<target>Игнорирай грешките</target> + +<source>Retry count:</source> +<target>Брой повторения:</target> + +<source>Delay (in seconds):</source> +<target>Задръжка (сек.):</target> + <source>Performance improvements:</source> <target>Подобряване на производителността:</target> @@ -1129,17 +1171,11 @@ The command is triggered if: <source>Last x days:</source> <target>Последните x дни:</target> -<source>Ignore errors</source> -<target>Игнорирай грешките</target> - -<source>Retry count:</source> -<target>Брой повторения:</target> - -<source>Delay (in seconds):</source> -<target>Задръжка (сек.):</target> +<source>&Override default log path:</source> +<target>&Замени подразбирания log-път:</target> -<source>Run a command after synchronization:</source> -<target>След синхронизация изпълни команда:</target> +<source>Run a command:</source> +<target>Изпълни команда:</target> <source>OK</source> <target>ОК</target> @@ -1189,6 +1225,9 @@ The command is triggered if: <source>Directory on server:</source> <target>Директория на сървъра:</target> +<source>Access timeout (in seconds):</source> +<target>Време за достъп (в секунди):</target> + <source>SFTP channels per connection:</source> <target>SFTP-канали на връзка:</target> @@ -1267,12 +1306,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>Спри синхронизацията при първа грешка</target> -<source>Save log:</source> -<target>Запази протокол:</target> - -<source>Limit number of log files:</source> -<target>Ограничи броя протоколни файлове:</target> - <source>How can I schedule a batch job?</source> <target>Как да планирам пакетна задача?</target> @@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Покажи всички постоянно скрити диалози и предупреждения отново</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Подразбиран log-път:</target> + +<source>&Delete logs after x days:</source> +<target>&Изтрий протоколите след x дни:</target> <source>Customize context menu:</source> <target>Настрой контекстното меню:</target> @@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>&По подразбиране</target> -<source>Feedback and suggestions are welcome</source> -<target>Забележки и предложения са добре дошли</target> +<source>Feedback and suggestions are welcome:</source> +<target>Отзиви и предложения са добре дошли:</target> <source>Home page</source> <target>Домашна страница</target> @@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Изходния код е написан на C++ със:</target> -<source>Published under the GNU General Public License</source> -<target>Публикува се по лиценза GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Публикуван под GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Благодарност за локализацията:</target> @@ -1358,7 +1394,7 @@ This guarantees a consistent state even in case of a serious error. <target>Активирайте дарителско издание на FreeFileSync по един от следните методи:</target> <source>1. Activate via internet now:</source> -<target>1. Активиране по Интернет:</target> +<target>1. Активирайте по Интернет сега:</target> <source>Activate online</source> <target>Активирайте онлайн</target> @@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error. <target>Информация</target> <source>No log entries</source> -<target></target> +<target>Няма log-записи</target> <source>Select all</source> <target>Маркирай всичко</target> @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>Преглед</target> +<source>Swap sides</source> +<target>Размени страните</target> + <source>Show "%x"</source> <target>Покажи "%x"</target> @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>Покажи филтрираните или временно изключени файлове</target> -<source>Save as default</source> -<target>Запази като подразбирано</target> - <source>Filter</source> <target>Филтър</target> @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>Неуспешна проверка на кошчето за папка %x.</target> -<source>The following XML elements could not be read:</source> -<target>Не могат да се прочетат следните XML-елементи:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Конфигурационният файл %x е непълен. Ще бъдат зададени подразбирани стойности за липсващите елементи.</target> - <source>Prepare installation</source> <target>Подготовка за инсталиране</target> diff --git a/FreeFileSync/Build/Languages/chinese_simple.lng b/FreeFileSync/Build/Languages/chinese_simple.lng index a4350d39..5716cb9a 100755 --- a/FreeFileSync/Build/Languages/chinese_simple.lng +++ b/FreeFileSync/Build/Languages/chinese_simple.lng @@ -159,6 +159,9 @@ <source>Items differ in attributes only</source> <target>项目仅是文件属性不同</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>%x 此名称在文件夹中被一个以上的项目所使用.</target> + <source>Resolving symbolic link %x</source> <target>正在解决符号连接 %x</target> @@ -186,9 +189,6 @@ <source>File time tolerance</source> <target>文件时间容差</target> -<source>Folder access timeout</source> -<target>文件夹访问超时</target> - <source>Run with background priority</source> <target>以后台优先级运行</target> @@ -332,8 +332,11 @@ <source>Update attributes on right</source> <target>更新右侧的文件属性</target> -<source>Warning</source> -<target>警告</target> +<source>Errors:</source> +<target>错误:</target> + +<source>Warnings:</source> +<target>警告:</target> <source>Items processed:</source> <target>已处理的项目:</target> @@ -344,6 +347,9 @@ <source>Total time:</source> <target>总共时间:</target> +<source>Warning</source> +<target>警告</target> + <source>Stopped</source> <target>已停止</target> @@ -353,6 +359,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>当分析文件 %x , 行 %y, 列 %z 时出错.</target> +<source>Services</source> +<target>服务</target> + +<source>Show All</source> +<target>显示所有</target> + +<source>Hide Others</source> +<target>隐藏其他</target> + +<source>Hide %x</source> +<target>隐藏 %x</target> + +<source>Quit %x</source> +<target>退出 %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>无法为如下文件夹设置 目录锁定:</target> @@ -367,11 +388,11 @@ <source>Cannot read directory %x.</source> <target>无法读取目录 %x.</target> -<source>/sec</source> -<target>/秒</target> +<source>%x/sec</source> +<target>%x/秒</target> -<source>%x items/sec</source> -<target>%x 个项目/秒</target> +<source>%x items</source> +<target>%x 个项目</target> <source>Show in Explorer</source> <target>在Explorer中显示</target> @@ -520,11 +541,11 @@ <source>Generating database...</source> <target>正在生成数据库...</target> -<source>Searching for excess file versions:</source> -<target>正在搜索多余的文件版本:</target> +<source>Searching for old file versions:</source> +<target>正在搜索旧的文件版本:</target> -<source>Removing excess file versions:</source> -<target>正在删除多余的文件版本:</target> +<source>Removing old file versions:</source> +<target>正在移除旧的文件版本:</target> <source>Unable to create time stamp for versioning:</source> <target>无法为历史版本创建时间戳:</target> @@ -582,6 +603,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>无法将 %x 移动到回收站.</target> +<source>Unable to access %x.</source> +<target>无法访问 %x.</target> + +<source>Authentication completed.</source> +<target>验证完成.</target> + +<source>Authentication failed.</source> +<target>验证失败.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>你可以现在关闭本页面并继续使用 FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>服务器返回一个错误:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>无法确定 %x 上的可用磁盘空间.</target> + <source>Cannot find %x.</source> <target>无法找到 %x.</target> @@ -594,18 +633,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>无法删除符号链接 %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>无法确定 %x 上的可用磁盘空间.</target> - <source>Incorrect command line:</source> <target>不正确的命令行:</target> <source>The server does not support authentication via %x.</source> <target>此服务器并不支持通过 %x 进行认证.</target> -<source>Unable to access %x.</source> -<target>无法访问 %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -628,23 +661,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>打开SFTP通道号 %x 时失败.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x 字节</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>拖放</target> @@ -758,6 +774,23 @@ The command is triggered if: <source>&Retry</source> <target>重试(&R)</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x 字节</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>正在载入...</target> @@ -902,7 +935,7 @@ The command is triggered if: <target>另存为批处理作业(&B)...</target> <source>Show &log</source> -<target></target> +<target>显示日志(&L)</target> <source>Start &comparison</source> <target>开始比较(&C)</target> @@ -964,9 +997,6 @@ The command is triggered if: <source>Access online storage</source> <target>访问在线存储空间</target> -<source>Swap sides</source> -<target>两侧互换</target> - <source>Close search bar</source> <target>关闭搜索条</target> @@ -991,6 +1021,9 @@ The command is triggered if: <source>View type:</source> <target>视图类型:</target> +<source>Save as default</source> +<target>保存为默认值</target> + <source>Select view:</source> <target>选择视图:</target> @@ -1048,6 +1081,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>处理夏令时</target> +<source>Ignore errors</source> +<target>忽略错误</target> + +<source>Retry count:</source> +<target>重试计数:</target> + +<source>Delay (in seconds):</source> +<target>延时 (秒):</target> + <source>Performance improvements:</source> <target>性能改进:</target> @@ -1122,17 +1164,11 @@ The command is triggered if: <source>Last x days:</source> <target>最近 x 天:</target> -<source>Ignore errors</source> -<target>忽略错误</target> - -<source>Retry count:</source> -<target>重试计数:</target> - -<source>Delay (in seconds):</source> -<target>延时 (秒):</target> +<source>&Override default log path:</source> +<target>覆盖默认日志路径(&O):</target> -<source>Run a command after synchronization:</source> -<target>同步之后运行一个命令:</target> +<source>Run a command:</source> +<target>执行一个命令:</target> <source>OK</source> <target>确定</target> @@ -1182,6 +1218,9 @@ The command is triggered if: <source>Directory on server:</source> <target>服务器上的目录:</target> +<source>Access timeout (in seconds):</source> +<target>访问超时(秒):</target> + <source>SFTP channels per connection:</source> <target>每个连接的SFTP通道数:</target> @@ -1260,12 +1299,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>在遇到第一个错误时停止同步</target> -<source>Save log:</source> -<target>保存日志:</target> - -<source>Limit number of log files:</source> -<target>限制日志文件数量:</target> - <source>How can I schedule a batch job?</source> <target>我如何计划一个批处理作业?</target> @@ -1299,8 +1332,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>重新显示所有被永久性隐藏的 对话框和警告信息</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>默认日志路径:</target> + +<source>&Delete logs after x days:</source> +<target>在 x 天后删除日志(&D):</target> <source>Customize context menu:</source> <target>自定义右键菜单:</target> @@ -1311,8 +1347,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>默认(&D)</target> -<source>Feedback and suggestions are welcome</source> -<target>欢迎反馈意见和提出建议</target> +<source>Feedback and suggestions are welcome:</source> +<target>欢迎提出反馈意见和建议:</target> <source>Home page</source> <target>主页</target> @@ -1338,8 +1374,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>源代码使用如下工具由C++写成:</target> -<source>Published under the GNU General Public License</source> -<target>在GNU通用公共许可下发布</target> +<source>Published under the GNU General Public License:</source> +<target>根据GNU通用公共许可证发布:</target> <source>Many thanks for localization:</source> <target>非常感谢以下本地化翻译者:</target> @@ -1396,7 +1432,7 @@ This guarantees a consistent state even in case of a serious error. <target>信息</target> <source>No log entries</source> -<target></target> +<target>没有日志条目</target> <source>Select all</source> <target>选择全部</target> @@ -1422,6 +1458,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>摘要</target> +<source>Swap sides</source> +<target>两侧互换</target> + <source>Show "%x"</source> <target>显示 "%x"</target> @@ -1592,9 +1631,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>显示已被过滤或被临时排除的文件</target> -<source>Save as default</source> -<target>保存为默认值</target> - <source>Filter</source> <target>过滤器</target> @@ -1935,12 +1971,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>检查文件夹 %x 的回收站失败.</target> -<source>The following XML elements could not be read:</source> -<target>下列的XML元素无法被读取:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>配置文件 %x 不完整. 丢失的元素将被设置为它们的默认值.</target> - <source>Prepare installation</source> <target>准备安装</target> diff --git a/FreeFileSync/Build/Languages/chinese_traditional.lng b/FreeFileSync/Build/Languages/chinese_traditional.lng index 2dc930c4..2705c499 100755 --- a/FreeFileSync/Build/Languages/chinese_traditional.lng +++ b/FreeFileSync/Build/Languages/chinese_traditional.lng @@ -159,6 +159,9 @@ <source>Items differ in attributes only</source> <target>只有項目的屬性不同</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>名稱 %x 已被資料夾中的多個項目使用。</target> + <source>Resolving symbolic link %x</source> <target>正在解析符號連結 %x</target> @@ -186,9 +189,6 @@ <source>File time tolerance</source> <target>檔案時間容差範圍</target> -<source>Folder access timeout</source> -<target>資料夾存取超時</target> - <source>Run with background priority</source> <target>背景優先執行</target> @@ -226,7 +226,7 @@ <target>資料庫檔案 %x 不相容。</target> <source>Initial synchronization:</source> -<target>初始化同步:</target> +<target>初始同步:</target> <source>Database file %x does not yet exist.</source> <target>資料庫檔案 %x 不存在。</target> @@ -253,7 +253,7 @@ <target>正在搜尋資料夾 %x…</target> <source>Timeout while searching for folder %x.</source> -<target>搜尋資料夾 %x 時超時。</target> +<target>搜尋資料夾 %x 時逾時。</target> <source>Cannot get process information.</source> <target>無法取得處理訊息。</target> @@ -332,8 +332,11 @@ <source>Update attributes on right</source> <target>更新右邊的屬性</target> -<source>Warning</source> -<target>警告</target> +<source>Errors:</source> +<target>錯誤:</target> + +<source>Warnings:</source> +<target>警告:</target> <source>Items processed:</source> <target>已處理項目:</target> @@ -344,15 +347,33 @@ <source>Total time:</source> <target>全部時間:</target> +<source>Warning</source> +<target>警告</target> + <source>Stopped</source> <target>已停止</target> <source>Cleaning up log files:</source> -<target>清除日誌檔:</target> +<target>清除紀錄檔:</target> <source>Error parsing file %x, row %y, column %z.</source> <target>解析 %x 檔案,第 %y 列,第 %z 行出現錯誤。</target> +<source>Services</source> +<target>服務</target> + +<source>Show All</source> +<target>顯示全部</target> + +<source>Hide Others</source> +<target>隱藏其他</target> + +<source>Hide %x</source> +<target>隱藏 %x</target> + +<source>Quit %x</source> +<target>結束 %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>無法為以下資料夾設定目錄鎖定:</target> @@ -361,17 +382,17 @@ <pluralform>%x threads</pluralform> </source> <target> -<pluralform>%x 執行緒</pluralform> +<pluralform>%x 個執行緒</pluralform> </target> <source>Cannot read directory %x.</source> <target>無法讀取目錄 %x。</target> -<source>/sec</source> -<target>(秒)</target> +<source>%x/sec</source> +<target>%x/秒</target> -<source>%x items/sec</source> -<target>%x 個項目(秒)</target> +<source>%x items</source> +<target>%x 項目</target> <source>Show in Explorer</source> <target>在資源管理器中顯示</target> @@ -520,11 +541,11 @@ <source>Generating database...</source> <target>正在產生資料庫…</target> -<source>Searching for excess file versions:</source> -<target>正在搜尋多餘的檔案版本:</target> +<source>Searching for old file versions:</source> +<target>正在搜尋舊檔版本:</target> -<source>Removing excess file versions:</source> -<target>正在刪除多餘的檔案版本:</target> +<source>Removing old file versions:</source> +<target>正在移除舊檔版本:</target> <source>Unable to create time stamp for versioning:</source> <target>無法建立時間戳記的版本控制:</target> @@ -582,6 +603,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>無法將 %x 移動到資源回收筒。</target> +<source>Unable to access %x.</source> +<target>無法存取 %x。</target> + +<source>Authentication completed.</source> +<target>認證完成。</target> + +<source>Authentication failed.</source> +<target>認證失敗。</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>您可以立即關閉此頁面並繼續使用FreeFileSync。</target> + +<source>The server returned an error:</source> +<target>伺服器傳回錯誤:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>無法確定 %x 的可用磁碟空間。</target> + <source>Cannot find %x.</source> <target>找不到 %x。</target> @@ -594,24 +633,18 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>無法刪除符號連結 %x。</target> -<source>Cannot determine free disk space for %x.</source> -<target>無法確定 %x 的可用磁碟空間。</target> - <source>Incorrect command line:</source> <target>不正確的命令列:</target> <source>The server does not support authentication via %x.</source> -<target>伺服器不支援透過 %x 進行身份驗證。</target> - -<source>Unable to access %x.</source> -<target>無法存取 %x。</target> +<target>伺服器不支援透過 %x 進行認證。</target> <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> </source> <target> -<pluralform>在 %x 秒後操作超時。</pluralform> +<pluralform>在 %x 秒後操作逾時。</pluralform> </target> <source> @@ -628,23 +661,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>無法開啟SFTP通道號 %x。</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x 位元組</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>拖放</target> @@ -758,6 +774,23 @@ The command is triggered if: <source>&Retry</source> <target>重試(&R)</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x 位元組</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>正在載入…</target> @@ -824,7 +857,7 @@ The command is triggered if: <target>上次同步</target> <source>Log</source> -<target>日誌</target> +<target>紀錄</target> <source>Folder</source> <target>資料夾</target> @@ -902,7 +935,7 @@ The command is triggered if: <target>另存為批次工作(&B)…</target> <source>Show &log</source> -<target></target> +<target>顯示紀錄(&L)</target> <source>Start &comparison</source> <target>開始比對(&C)</target> @@ -964,9 +997,6 @@ The command is triggered if: <source>Access online storage</source> <target>存取線上儲存空間</target> -<source>Swap sides</source> -<target>兩邊交換</target> - <source>Close search bar</source> <target>關閉搜尋欄位</target> @@ -991,6 +1021,9 @@ The command is triggered if: <source>View type:</source> <target>檢視類型:</target> +<source>Save as default</source> +<target>儲存為預設值</target> + <source>Select view:</source> <target>選擇視圖:</target> @@ -1028,7 +1061,7 @@ The command is triggered if: <target>包含符號連結(&S):</target> <source>&Follow</source> -<target>跟隨(&F)</target> +<target>追隨(&F)</target> <source>&Direct</source> <target>直接(&D)</target> @@ -1048,6 +1081,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>處理日光節約時間</target> +<source>Ignore errors</source> +<target>忽略錯誤</target> + +<source>Retry count:</source> +<target>重試次數:</target> + +<source>Delay (in seconds):</source> +<target>延遲(以秒為單位):</target> + <source>Performance improvements:</source> <target>效能改善:</target> @@ -1122,17 +1164,11 @@ The command is triggered if: <source>Last x days:</source> <target>最近 x 天:</target> -<source>Ignore errors</source> -<target>忽略錯誤</target> - -<source>Retry count:</source> -<target>重試次數:</target> - -<source>Delay (in seconds):</source> -<target>延遲(以秒為單位):</target> +<source>&Override default log path:</source> +<target>覆蓋預設紀錄路徑(&O):</target> -<source>Run a command after synchronization:</source> -<target>同步後執行一個命令:</target> +<source>Run a command:</source> +<target>執行命令:</target> <source>OK</source> <target>確定</target> @@ -1159,7 +1195,7 @@ The command is triggered if: <target>外顯式SSL/TLS(&E)</target> <source>Authentication:</source> -<target>身份驗證:</target> +<target>認證:</target> <source>&Password</source> <target>密碼(&P):</target> @@ -1182,6 +1218,9 @@ The command is triggered if: <source>Directory on server:</source> <target>在伺服器上的目錄:</target> +<source>Access timeout (in seconds):</source> +<target>存取逾時(以秒為單位):</target> + <source>SFTP channels per connection:</source> <target>每個連接SFTP通道數:</target> @@ -1260,12 +1299,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>在第一個錯誤時停止同步</target> -<source>Save log:</source> -<target>儲存日誌:</target> - -<source>Limit number of log files:</source> -<target>限制日誌檔數量:</target> - <source>How can I schedule a batch job?</source> <target>如何排程批次工作?</target> @@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>再次顯示所有永久 隱藏的對話框和警告訊息</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>預設紀錄路徑:</target> + +<source>&Delete logs after x days:</source> +<target>在 x 天後刪除紀錄(&D):</target> <source>Customize context menu:</source> <target>自訂內容功能表:</target> @@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>預設(&D)</target> -<source>Feedback and suggestions are welcome</source> -<target>歡迎反映意見和建議</target> +<source>Feedback and suggestions are welcome:</source> +<target>歡迎回饋和建議:</target> <source>Home page</source> <target>主頁</target> @@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>使用C++編寫的原始碼:</target> -<source>Published under the GNU General Public License</source> -<target>在GNU通用公共許可證下發佈</target> +<source>Published under the GNU General Public License:</source> +<target>根據GNU通用公共授權條款發佈:</target> <source>Many thanks for localization:</source> <target>非常感謝在地化翻譯人員:</target> @@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error. <target>訊息</target> <source>No log entries</source> -<target></target> +<target>沒有紀錄</target> <source>Select all</source> <target>全選</target> @@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>摘要</target> +<source>Swap sides</source> +<target>兩邊交換</target> + <source>Show "%x"</source> <target>顯示「%x」</target> @@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>顯示已篩選或暫時排除的檔案</target> -<source>Save as default</source> -<target>儲存為預設值</target> - <source>Filter</source> <target>篩選器</target> @@ -1644,7 +1680,7 @@ This guarantees a consistent state even in case of a serious error. <target>進度</target> <source>Thank you, %x, for your donation and support!</source> -<target>%x, 感謝您的贊助與支持!</target> +<target>%x,感謝您的贊助與支持!</target> <source>Connections</source> <target>連線數</target> @@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>檢查資源回收筒的資料夾 %x 失敗。</target> -<source>The following XML elements could not be read:</source> -<target>無法讀取下列XML元件:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>配置檔案 %x 不完整。缺少的元件將設定為其本身預設值。</target> - <source>Prepare installation</source> <target>準備安裝</target> @@ -2014,7 +2044,7 @@ This guarantees a consistent state even in case of a serious error. <target>使用FreeFileSync進行編輯</target> <source>Instead of an ad, here's an animal.</source> -<target>這是一隻動物,而不是廣告。</target> +<target>這是一隻動物,不是廣告。</target> <source>The FreeFileSync portable version cannot install into a subfolder of %x.</source> <target>FreeFileSync可攜式版本無法安裝到 %x 的子資料夾。</target> diff --git a/FreeFileSync/Build/Languages/croatian.lng b/FreeFileSync/Build/Languages/croatian.lng index 78107ef3..ee6721a2 100755 --- a/FreeFileSync/Build/Languages/croatian.lng +++ b/FreeFileSync/Build/Languages/croatian.lng @@ -161,6 +161,9 @@ <source>Items differ in attributes only</source> <target>Stavke se razlikuju samo u atributima</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Naziv %x se koristi za više od jedne stavke u mapi.</target> + <source>Resolving symbolic link %x</source> <target>Rješavam simboličku vezu %x</target> @@ -188,9 +191,6 @@ <source>File time tolerance</source> <target>Tolerancija vremena datoteke</target> -<source>Folder access timeout</source> -<target>Istek vremena pristupa mapi</target> - <source>Run with background priority</source> <target>Pokreni s pozadinskim prioritetom</target> @@ -336,8 +336,11 @@ <source>Update attributes on right</source> <target>Osvježi atribute desno</target> -<source>Warning</source> -<target>Upozorenje</target> +<source>Errors:</source> +<target>Pogreške:</target> + +<source>Warnings:</source> +<target>Upozorenja:</target> <source>Items processed:</source> <target>Obrađene stavke:</target> @@ -348,6 +351,9 @@ <source>Total time:</source> <target>Ukupno vrijeme:</target> +<source>Warning</source> +<target>Upozorenje</target> + <source>Stopped</source> <target>Zaustavljeno</target> @@ -357,6 +363,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Greška u analizi datoteke %x, red %y, stupac %z.</target> +<source>Services</source> +<target>Servisi</target> + +<source>Show All</source> +<target>Prikaži sve</target> + +<source>Hide Others</source> +<target>Sakrij ostale</target> + +<source>Hide %x</source> +<target>Sakrij %x</target> + +<source>Quit %x</source> +<target>Napusti %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Nije moguće zaključavanje slijedećih foldera:</target> @@ -373,11 +394,11 @@ <source>Cannot read directory %x.</source> <target>Nije moguće učitati mapu %x.</target> -<source>/sec</source> -<target>/sek</target> +<source>%x/sec</source> +<target>%x/sek</target> -<source>%x items/sec</source> -<target>%x stavki/sek</target> +<source>%x items</source> +<target>%x stavki</target> <source>Show in Explorer</source> <target>Prikaži u Exploreru</target> @@ -526,11 +547,11 @@ <source>Generating database...</source> <target>Izrađivanje baze podataka...</target> -<source>Searching for excess file versions:</source> -<target>Traženje višestrukih verzija datoteka:</target> +<source>Searching for old file versions:</source> +<target>Pretraživanje starih verzija datoteka:</target> -<source>Removing excess file versions:</source> -<target>Uklanjanje višestrukih verzija datoteka:</target> +<source>Removing old file versions:</source> +<target>Uklanjanje starih verzija datoteka:</target> <source>Unable to create time stamp for versioning:</source> <target>Nije moguća izrada vremenske oznake za označavanje:</target> @@ -588,6 +609,24 @@ Stvarno: %y bajta <source>Unable to move %x to the recycle bin.</source> <target>Nije moguće premjestiti %x u koš za smeće.</target> +<source>Unable to access %x.</source> +<target>Nije moguće pristupiti %x.</target> + +<source>Authentication completed.</source> +<target>Provjera autentičnosti dovršena.</target> + +<source>Authentication failed.</source> +<target>Provjera autentičnosti nije uspjela.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Sada možete zatvoriti ovu stranicu i nastaviti sa FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Pogreška poslužitelja:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Nije moguće izračunati slobodan prostor diska za %x.</target> + <source>Cannot find %x.</source> <target>Nije moguće pronaći %x.</target> @@ -600,18 +639,12 @@ Stvarno: %y bajta <source>Cannot delete symbolic link %x.</source> <target>Nije moguće brisanje simbolične poveznice %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Nije moguće izračunati slobodan prostor diska za %x.</target> - <source>Incorrect command line:</source> <target>Netočna naredbena linija:</target> <source>The server does not support authentication via %x.</source> <target>Server ne podržava provjeru autentičnosti preko %x.</target> -<source>Unable to access %x.</source> -<target>Nije moguće pristupiti %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -638,25 +671,6 @@ Stvarno: %y bajta <source>Failed to open SFTP channel number %x.</source> <target>Nije uspjelo povezivanje na SFTP kanal broj %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x bajt</pluralform> -<pluralform>%x bajta</pluralform> -<pluralform>%x bajtova</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Povuci && ispusti</target> @@ -770,6 +784,25 @@ Naredba će biti pokrenuta ako se: <source>&Retry</source> <target>&Ponovi</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x bajt</pluralform> +<pluralform>%x bajta</pluralform> +<pluralform>%x bajtova</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Učitavam...</target> @@ -916,7 +949,7 @@ Naredba će biti pokrenuta ako se: <target>Spremi kao &Slijedni zadatak...</target> <source>Show &log</source> -<target></target> +<target>Prikaži &zapisnik</target> <source>Start &comparison</source> <target>Započni &usporedbu</target> @@ -978,9 +1011,6 @@ Naredba će biti pokrenuta ako se: <source>Access online storage</source> <target>Pristupi online pohrani</target> -<source>Swap sides</source> -<target>Zamjeni strane</target> - <source>Close search bar</source> <target>Zatvori traku pretraživanja</target> @@ -1005,6 +1035,9 @@ Naredba će biti pokrenuta ako se: <source>View type:</source> <target>Vrsta prikaza:</target> +<source>Save as default</source> +<target>Spremi kao standardno</target> + <source>Select view:</source> <target>Odaberite prikaz:</target> @@ -1062,6 +1095,15 @@ Naredba će biti pokrenuta ako se: <source>Handle daylight saving time</source> <target>Upravljaj ljetnim računanjem vremena</target> +<source>Ignore errors</source> +<target>Zanemariti pogreške</target> + +<source>Retry count:</source> +<target>Broj pokušaja:</target> + +<source>Delay (in seconds):</source> +<target>Odgoda (u sekundama):</target> + <source>Performance improvements:</source> <target>Poboljšanje perfomansi:</target> @@ -1136,17 +1178,11 @@ Naredba će biti pokrenuta ako se: <source>Last x days:</source> <target>Zadnjih nekoliko dana:</target> -<source>Ignore errors</source> -<target>Zanemariti pogreške</target> - -<source>Retry count:</source> -<target>Broj pokušaja:</target> - -<source>Delay (in seconds):</source> -<target>Odgoda (u sekundama):</target> +<source>&Override default log path:</source> +<target>&Prepiši zadanu putanju zapisnika:</target> -<source>Run a command after synchronization:</source> -<target>Pokreni naredbu nakon sinkronizacije:</target> +<source>Run a command:</source> +<target>Izvrši naredbu:</target> <source>OK</source> <target>U redu</target> @@ -1196,6 +1232,9 @@ Naredba će biti pokrenuta ako se: <source>Directory on server:</source> <target>Mapa na serveru:</target> +<source>Access timeout (in seconds):</source> +<target>Prekid pristupa nakon (u sekundama):</target> + <source>SFTP channels per connection:</source> <target>SFTP kanala po konekciji:</target> @@ -1274,12 +1313,6 @@ Naredba će biti pokrenuta ako se: <source>Stop synchronization at first error</source> <target>Zaustavi sinkronizaciju pri prvoj pogrešci</target> -<source>Save log:</source> -<target>Spremi izvještaj:</target> - -<source>Limit number of log files:</source> -<target>Limitiranje broja log datoteka:</target> - <source>How can I schedule a batch job?</source> <target>Kako zakazati slijedni zadatak?</target> @@ -1316,8 +1349,11 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Prikaži sve trajno skrivene prozore i poruke upozorenja ponovno</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Zadana putanja zapisnika:</target> + +<source>&Delete logs after x days:</source> +<target>&Izbrisati zapisnike nakon x dana:</target> <source>Customize context menu:</source> <target>Prilagodite kontekstni izbornik:</target> @@ -1328,8 +1364,8 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>&Default</source> <target>&Zadano</target> -<source>Feedback and suggestions are welcome</source> -<target>Povratne informacije i prijedlozi su dobrodošli</target> +<source>Feedback and suggestions are welcome:</source> +<target>Povratne informacije i prijedlozi su dobrodošli:</target> <source>Home page</source> <target>Web stranica</target> @@ -1355,8 +1391,8 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Source code written in C++ using:</source> <target>Izvorni kod napisan u C++ uz korištenje:</target> -<source>Published under the GNU General Public License</source> -<target>Objavljeno pod licencom GNU General Public</target> +<source>Published under the GNU General Public License:</source> +<target>Objavljeno pod GNU General Public Licencom:</target> <source>Many thanks for localization:</source> <target>Velike zahvale idu:</target> @@ -1413,7 +1449,7 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Nema izvještaja</target> <source>Select all</source> <target>Odaberi sve</target> @@ -1439,6 +1475,9 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Overview</source> <target>Pregled</target> +<source>Swap sides</source> +<target>Zamjeni strane</target> + <source>Show "%x"</source> <target>Prikaži "%x"</target> @@ -1617,9 +1656,6 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Show filtered or temporarily excluded files</source> <target>Prikaži filtrirane ili privremeno odvojene datoteke</target> -<source>Save as default</source> -<target>Spremi kao standardno</target> - <source>Filter</source> <target>Filtriranje</target> @@ -1970,12 +2006,6 @@ Ovo garantira stabilno stanje čak u slučaju ozbiljne greške. <source>Checking recycle bin failed for folder %x.</source> <target>Provjeravanje koša za smeće neuspješno za mapu %x.</target> -<source>The following XML elements could not be read:</source> -<target>Slijedeći XML elementi se nisu mogli učitati:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Konfiguracijska datoteka %x je nepotpuna. Nedostajući elementi će biti postavljeni na početne vrijednosti.</target> - <source>Prepare installation</source> <target>Pripremam instalaciju</target> diff --git a/FreeFileSync/Build/Languages/czech.lng b/FreeFileSync/Build/Languages/czech.lng index 3403389a..1cf9fb65 100755 --- a/FreeFileSync/Build/Languages/czech.lng +++ b/FreeFileSync/Build/Languages/czech.lng @@ -161,6 +161,9 @@ <source>Items differ in attributes only</source> <target>Položky se liší pouze ve vlastnostech</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Název %x je použit více jak jednou položkou ve složce.</target> + <source>Resolving symbolic link %x</source> <target>Hledání odkazu symbolického zástupce %x</target> @@ -188,9 +191,6 @@ <source>File time tolerance</source> <target>Časová tolerance</target> -<source>Folder access timeout</source> -<target>Časový limit přístupu ke složce</target> - <source>Run with background priority</source> <target>Spuštění s prioritou na pozadí</target> @@ -336,8 +336,11 @@ <source>Update attributes on right</source> <target>Nastavit vlastnosti vpravo</target> -<source>Warning</source> -<target>Varování</target> +<source>Errors:</source> +<target>Chyb:</target> + +<source>Warnings:</source> +<target>Varování:</target> <source>Items processed:</source> <target>Zpracováno položek:</target> @@ -348,6 +351,9 @@ <source>Total time:</source> <target>Celkový čas:</target> +<source>Warning</source> +<target>Varování</target> + <source>Stopped</source> <target>Zastaveno</target> @@ -357,6 +363,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Chyba zpracování souboru %x: na řádku %y ve sloupci %z.</target> +<source>Services</source> +<target>Služby</target> + +<source>Show All</source> +<target>Zobrazit vše</target> + +<source>Hide Others</source> +<target>Skrýt ostatní</target> + +<source>Hide %x</source> +<target>Skrýt %x</target> + +<source>Quit %x</source> +<target>Ukončit %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Nelze uzamčít následující adresáře:</target> @@ -373,11 +394,11 @@ <source>Cannot read directory %x.</source> <target>Nelze číst adresář %x.</target> -<source>/sec</source> -<target>/s</target> +<source>%x/sec</source> +<target>%x/s</target> -<source>%x items/sec</source> -<target>%x položek/s</target> +<source>%x items</source> +<target>%x položek</target> <source>Show in Explorer</source> <target>Zobrazit v Průzkumníkovi</target> @@ -526,11 +547,11 @@ <source>Generating database...</source> <target>Vytváření databáze...</target> -<source>Searching for excess file versions:</source> -<target>Hledání nadbytečných verzí:</target> +<source>Searching for old file versions:</source> +<target>Hledání starších verzí:</target> -<source>Removing excess file versions:</source> -<target>Odstraňování nadbytečných verzí:</target> +<source>Removing old file versions:</source> +<target>Odstraňování starších verzí:</target> <source>Unable to create time stamp for versioning:</source> <target>Nelze vytvořit časové značky verzování:</target> @@ -588,6 +609,24 @@ Aktuálně: %y b <source>Unable to move %x to the recycle bin.</source> <target>Není možné přesunout %x do Koše.</target> +<source>Unable to access %x.</source> +<target>Nepodařil se přístup k %x.</target> + +<source>Authentication completed.</source> +<target>Přístup povolen.</target> + +<source>Authentication failed.</source> +<target>Přístup odmítnut.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Nyní můžete tuto stránku zavřít a pokračovat s FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Server vrátil chybu:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Nelze zjistit volné místo na disku %x.</target> + <source>Cannot find %x.</source> <target>Nelze najít %x.</target> @@ -600,18 +639,12 @@ Aktuálně: %y b <source>Cannot delete symbolic link %x.</source> <target>Nelze smazat symbolický odkaz %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Nelze zjistit volné místo na disku %x.</target> - <source>Incorrect command line:</source> <target>Neplatný příkaz:</target> <source>The server does not support authentication via %x.</source> <target>Server nepodporuje ověření pomocí %x.</target> -<source>Unable to access %x.</source> -<target>Nepodařil se přístup k %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -638,25 +671,6 @@ Aktuálně: %y b <source>Failed to open SFTP channel number %x.</source> <target>Nelze otevřít SFTP kanál %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x B</pluralform> -<pluralform>%x B</pluralform> -<pluralform>%x B</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Přetáhni sem && pusť</target> @@ -770,6 +784,25 @@ Příkaz je spuštěn když: <source>&Retry</source> <target>&Opakovat</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x B</pluralform> +<pluralform>%x B</pluralform> +<pluralform>%x B</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Načítání...</target> @@ -916,7 +949,7 @@ Příkaz je spuštěn když: <target>Uložit jako &dávku...</target> <source>Show &log</source> -<target></target> +<target>&Zobrazit žurnál</target> <source>Start &comparison</source> <target>Začít &porovnání</target> @@ -978,9 +1011,6 @@ Příkaz je spuštěn když: <source>Access online storage</source> <target>Přístup k uložišti</target> -<source>Swap sides</source> -<target>Změna stran</target> - <source>Close search bar</source> <target>Zavřít hledání</target> @@ -1005,6 +1035,9 @@ Příkaz je spuštěn když: <source>View type:</source> <target>Typ zobrazení:</target> +<source>Save as default</source> +<target>Uložit jako výchozí</target> + <source>Select view:</source> <target>Výběr zobrazení:</target> @@ -1062,6 +1095,15 @@ Příkaz je spuštěn když: <source>Handle daylight saving time</source> <target>Používat letní čas</target> +<source>Ignore errors</source> +<target>Přeskočit chyby</target> + +<source>Retry count:</source> +<target>Počet opakování:</target> + +<source>Delay (in seconds):</source> +<target>Prodleva (v sekundách):</target> + <source>Performance improvements:</source> <target>Vylepšení rychlosti:</target> @@ -1136,17 +1178,11 @@ Příkaz je spuštěn když: <source>Last x days:</source> <target>Posledních dní:</target> -<source>Ignore errors</source> -<target>Přeskočit chyby</target> - -<source>Retry count:</source> -<target>Počet opakování:</target> - -<source>Delay (in seconds):</source> -<target>Prodleva (v sekundách):</target> +<source>&Override default log path:</source> +<target>&Nahradit výchozí cestu žurnálu:</target> -<source>Run a command after synchronization:</source> -<target>Po dokončení synchronizace spustit příkaz:</target> +<source>Run a command:</source> +<target>Spustit příkaz:</target> <source>OK</source> <target>OK</target> @@ -1196,6 +1232,9 @@ Příkaz je spuštěn když: <source>Directory on server:</source> <target>Adresář na serveru:</target> +<source>Access timeout (in seconds):</source> +<target>Časový limit přístupu (v sekundách):</target> + <source>SFTP channels per connection:</source> <target>SFTP kanálů na spojení:</target> @@ -1274,12 +1313,6 @@ Příkaz je spuštěn když: <source>Stop synchronization at first error</source> <target>Ukončit synchronizaci při první chybě</target> -<source>Save log:</source> -<target>Uložit žurnál:</target> - -<source>Limit number of log files:</source> -<target>Omezit množství žurnálů:</target> - <source>How can I schedule a batch job?</source> <target>Jak nastavit spouštění dávky?</target> @@ -1313,8 +1346,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Zobrazit znovu všechny trvale skryté dialogy a varovná hlášení</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Výchozí cesta žurnálu:</target> + +<source>&Delete logs after x days:</source> +<target>&Smazat žurnály po (dnech):</target> <source>Customize context menu:</source> <target>Přizpůsobit kontextovou nabídku:</target> @@ -1325,8 +1361,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>&Předdefinované</target> -<source>Feedback and suggestions are welcome</source> -<target>Komentáře a náměty jsou vždy vítány</target> +<source>Feedback and suggestions are welcome:</source> +<target>Komentáře a náměty jsou vždy vítány:</target> <source>Home page</source> <target>Navštivte</target> @@ -1352,8 +1388,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Zdrojový kód byl napsán kompletně v C++ pomocí:</target> -<source>Published under the GNU General Public License</source> -<target>Vydáno pod GNU General Public License (GPL)</target> +<source>Published under the GNU General Public License:</source> +<target>Vydáno pod GNU General Public License (GPL):</target> <source>Many thanks for localization:</source> <target>Poděkování za překlad FreeFileSync:</target> @@ -1410,7 +1446,7 @@ This guarantees a consistent state even in case of a serious error. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Žádné záznamy</target> <source>Select all</source> <target>Vybrat vše</target> @@ -1436,6 +1472,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>Přehled</target> +<source>Swap sides</source> +<target>Změna stran</target> + <source>Show "%x"</source> <target>Zobrazit "%x"</target> @@ -1614,9 +1653,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>Zobrazit filtrované nebo dočasně vynechané soubory</target> -<source>Save as default</source> -<target>Uložit jako výchozí</target> - <source>Filter</source> <target>Filtr</target> @@ -1967,12 +2003,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>Selhala kontrola Koše pro složku %x.</target> -<source>The following XML elements could not be read:</source> -<target>Nelze načíst následující XML elementy:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Konfigurační soubor %x je nekompletní. Chybějící položky budou nahrazeny výchozími hodnotami.</target> - <source>Prepare installation</source> <target>Příprava instalace</target> diff --git a/FreeFileSync/Build/Languages/danish.lng b/FreeFileSync/Build/Languages/danish.lng index fe6ed455..c3ed5775 100755 --- a/FreeFileSync/Build/Languages/danish.lng +++ b/FreeFileSync/Build/Languages/danish.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Enhederne har kun attributter til forskel</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Navnet %x bruges af flere emner i mappen.</target> + <source>Resolving symbolic link %x</source> <target>Løser symbolsk link %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Tidstolerance</target> -<source>Folder access timeout</source> -<target>Tidsgrænse mappeadgang</target> - <source>Run with background priority</source> <target>Kør med baggrundsprioritet</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Opdater attributter mod højre</target> -<source>Warning</source> -<target>Advarsel</target> +<source>Errors:</source> +<target>Fejl:</target> + +<source>Warnings:</source> +<target>Advarsler:</target> <source>Items processed:</source> <target>Emner behandlet:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Samlet tid:</target> +<source>Warning</source> +<target>Advarsel</target> + <source>Stopped</source> <target>Afbrudt</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Behandlingsfejl i filen %x, række %y, kolonne %z.</target> +<source>Services</source> +<target>Tjenester</target> + +<source>Show All</source> +<target>Vis alle</target> + +<source>Hide Others</source> +<target>Skjul andre</target> + +<source>Hide %x</source> +<target>Skjul %x</target> + +<source>Quit %x</source> +<target>Luk %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Kan ikke sætte mappelåse for følgende mapper:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Kan ikke læse mappen %x.</target> -<source>/sec</source> -<target>/sek</target> +<source>%x/sec</source> +<target>%x/sek</target> -<source>%x items/sec</source> -<target>%x emner/sek</target> +<source>%x items</source> +<target>%x emner</target> <source>Show in Explorer</source> <target>Åben filplacering</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Opretter database...</target> -<source>Searching for excess file versions:</source> -<target>Finder overskredne filversioner:</target> +<source>Searching for old file versions:</source> +<target>Søger efter gamle filversioner:</target> -<source>Removing excess file versions:</source> -<target>Fjerner overskredne filversioner:</target> +<source>Removing old file versions:</source> +<target>Fjerner gamle filversioner:</target> <source>Unable to create time stamp for versioning:</source> <target>Kan ikke oprette tidsstempel til versionering:</target> @@ -585,6 +606,24 @@ Aktuel: %y byte <source>Unable to move %x to the recycle bin.</source> <target>Kunne ikke flytte %x til papirkurv.</target> +<source>Unable to access %x.</source> +<target>Kan ikke tilgå %x.</target> + +<source>Authentication completed.</source> +<target>Godkendelse gennemført.</target> + +<source>Authentication failed.</source> +<target>Godkendelse fejlede.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Du kan lukke siden og returnere til FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Serverfejl:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Kan ikke definere ledig plads på %x.</target> + <source>Cannot find %x.</source> <target>Kan ikke finde %x.</target> @@ -597,18 +636,12 @@ Aktuel: %y byte <source>Cannot delete symbolic link %x.</source> <target>Kan ikke slette det symbolske link %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Kan ikke definere ledig plads på %x.</target> - <source>Incorrect command line:</source> <target>Ugyldig kommando:</target> <source>The server does not support authentication via %x.</source> <target>Serveren støtter ikke godkendelse via %x.</target> -<source>Unable to access %x.</source> -<target>Kan ikke tilgå %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Aktuel: %y byte <source>Failed to open SFTP channel number %x.</source> <target>Fejl ved åbning af SFTP kanal nr. %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x byte</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Træk emner hertil</target> @@ -764,6 +779,24 @@ Kommandoen udføres hvis: <source>&Retry</source> <target>&Prøv igen</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x byte</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Indlæser...</target> @@ -909,7 +942,7 @@ Kommandoen udføres hvis: <target>Gem som &batchfil...</target> <source>Show &log</source> -<target></target> +<target>Vis &log</target> <source>Start &comparison</source> <target>Start &analyse</target> @@ -971,9 +1004,6 @@ Kommandoen udføres hvis: <source>Access online storage</source> <target>Åben onlinelager</target> -<source>Swap sides</source> -<target>Byt side</target> - <source>Close search bar</source> <target>Luk søgelinie</target> @@ -998,6 +1028,9 @@ Kommandoen udføres hvis: <source>View type:</source> <target>Visning:</target> +<source>Save as default</source> +<target>Gem som standard</target> + <source>Select view:</source> <target>Vælg visning:</target> @@ -1055,6 +1088,15 @@ Kommandoen udføres hvis: <source>Handle daylight saving time</source> <target>Tag hensyn til sommertid</target> +<source>Ignore errors</source> +<target>Ignorér fejl</target> + +<source>Retry count:</source> +<target>Antal forsøg:</target> + +<source>Delay (in seconds):</source> +<target>Forsinkelse (sek):</target> + <source>Performance improvements:</source> <target>Ydelsesforbedring:</target> @@ -1129,17 +1171,11 @@ Kommandoen udføres hvis: <source>Last x days:</source> <target>Sidste x dage:</target> -<source>Ignore errors</source> -<target>Ignorér fejl</target> - -<source>Retry count:</source> -<target>Antal forsøg:</target> - -<source>Delay (in seconds):</source> -<target>Forsinkelse (sek):</target> +<source>&Override default log path:</source> +<target>&Tilpas logplacering:</target> -<source>Run a command after synchronization:</source> -<target>Kør kommando efter synkronisering:</target> +<source>Run a command:</source> +<target>Kør kommando:</target> <source>OK</source> <target>OK</target> @@ -1189,6 +1225,9 @@ Kommandoen udføres hvis: <source>Directory on server:</source> <target>Servermappe:</target> +<source>Access timeout (in seconds):</source> +<target>Timeout (sekunder):</target> + <source>SFTP channels per connection:</source> <target>SFTP kanaler pr. forbindelse:</target> @@ -1267,12 +1306,6 @@ Kommandoen udføres hvis: <source>Stop synchronization at first error</source> <target>Stop synkronisering ved første fejl</target> -<source>Save log:</source> -<target>Gem log:</target> - -<source>Limit number of log files:</source> -<target>Begræns antal logfiler:</target> - <source>How can I schedule a batch job?</source> <target>Hvordan oprettes en batchfil?</target> @@ -1309,8 +1342,11 @@ Sikrer processen ved alvorlige fejl. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Vis skjulte advarsler og beskeder igen</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Logplacering:</target> + +<source>&Delete logs after x days:</source> +<target>&Slet logs efter x dage:</target> <source>Customize context menu:</source> <target>Tilpas kontekstmenu:</target> @@ -1321,8 +1357,8 @@ Sikrer processen ved alvorlige fejl. <source>&Default</source> <target>S&tandard</target> -<source>Feedback and suggestions are welcome</source> -<target>Kritik og forslag er meget velkomne</target> +<source>Feedback and suggestions are welcome:</source> +<target>Kritik og foreslag er meget velkomne:</target> <source>Home page</source> <target>Hjemmeside</target> @@ -1348,8 +1384,8 @@ Sikrer processen ved alvorlige fejl. <source>Source code written in C++ using:</source> <target>Kildekoden er skrevet i C++ med hjælp fra:</target> -<source>Published under the GNU General Public License</source> -<target>Udgivet under GNU General Public Licence</target> +<source>Published under the GNU General Public License:</source> +<target>Udgivet under GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Tak for oversættelse til:</target> @@ -1406,7 +1442,7 @@ Sikrer processen ved alvorlige fejl. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Ingen logemner</target> <source>Select all</source> <target>Vælg alt</target> @@ -1432,6 +1468,9 @@ Sikrer processen ved alvorlige fejl. <source>Overview</source> <target>Oversigt</target> +<source>Swap sides</source> +<target>Byt side</target> + <source>Show "%x"</source> <target>Vis "%x"</target> @@ -1606,9 +1645,6 @@ Sikrer processen ved alvorlige fejl. <source>Show filtered or temporarily excluded files</source> <target>Vis filtrerede eller midlertidigt ekskluderede filer</target> -<source>Save as default</source> -<target>Gem som standard</target> - <source>Filter</source> <target>Filter</target> @@ -1954,12 +1990,6 @@ Sikrer processen ved alvorlige fejl. <source>Checking recycle bin failed for folder %x.</source> <target>Check papirkurv fejlede for mappen %x.</target> -<source>The following XML elements could not be read:</source> -<target>Kunne ikke læse følgende XML emner:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Indstillingsfilen %x er ikke komplet. Manglende elementer sættes til standard.</target> - <source>Prepare installation</source> <target>Forbereder installering</target> diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/dutch.lng index b68a5d70..74467d30 100755 --- a/FreeFileSync/Build/Languages/dutch.lng +++ b/FreeFileSync/Build/Languages/dutch.lng @@ -101,7 +101,7 @@ <target>globaal configuratiebestand:</target> <source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> -<target>Een willekeurig aantal FreeFileSync. ffs_gui en/of "ffs_batch" configuratiebestanden.</target> +<target>Een willekeurig aantal FreeFileSync "ffs_gui" en/of "ffs_batch" configuratiebestanden.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Een willekeurig aantal alternatieve mapparen voor maximaal een configuratiebestand.</target> @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>De items verschillen alleen in de kenmerken</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>De naam %x wordt gebruikt door meer dan één item in de map.</target> + <source>Resolving symbolic link %x</source> <target>Symbolische link %x oplossen</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Tolerantie bestandstijd</target> -<source>Folder access timeout</source> -<target>Time-out map toegang</target> - <source>Run with background priority</source> <target>Uitvoeren met achtergrond prioriteit</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Update kenmerken aan de rechterzijde</target> -<source>Warning</source> -<target>Waarschuwing</target> +<source>Errors:</source> +<target>Fouten:</target> + +<source>Warnings:</source> +<target>Waarschuwingen:</target> <source>Items processed:</source> <target>Items verwerkt:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Totale tijd:</target> +<source>Warning</source> +<target>Waarschuwing</target> + <source>Stopped</source> <target>Gestopt</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Fout bij het analyseren van bestand %x, rij %y, kolom %z.</target> +<source>Services</source> +<target>Diensten</target> + +<source>Show All</source> +<target>Toon alles</target> + +<source>Hide Others</source> +<target>Verberg anderen</target> + +<source>Hide %x</source> +<target>Verberg %x</target> + +<source>Quit %x</source> +<target>Sluit %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Kan map vergrendelingen niet instellen voor de volgende mappen:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Kan de map %x niet lezen.</target> -<source>/sec</source> -<target>/sec</target> +<source>%x/sec</source> +<target>%x/sec</target> -<source>%x items/sec</source> -<target>%x items/sec</target> +<source>%x items</source> +<target>%x items</target> <source>Show in Explorer</source> <target>In Explorer weergeven</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Genereren database...</target> -<source>Searching for excess file versions:</source> -<target>Zoeken naar overtollige bestandsversies:</target> +<source>Searching for old file versions:</source> +<target>Naar oude bestandsversies zoeken:</target> -<source>Removing excess file versions:</source> -<target>Overmatige bestandsversies verwijderen:</target> +<source>Removing old file versions:</source> +<target>Oude bestandsversies verwijderen:</target> <source>Unable to create time stamp for versioning:</source> <target>Kan geen tijdstempel maken voor versiebeheer:</target> @@ -585,6 +606,24 @@ Werkelijk: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Niet in staat om %x naar de prullenbak te verplaatsen.</target> +<source>Unable to access %x.</source> +<target>Geen toegang tot %x.</target> + +<source>Authentication completed.</source> +<target>Authenticatie voltooid.</target> + +<source>Authentication failed.</source> +<target>Verificatie mislukt.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>U kunt deze pagina nu sluiten en doorgaan met FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>De server heeft een fout teruggestuurd:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Kan de vrije schijfruimte voor %x niet bepalen.</target> + <source>Cannot find %x.</source> <target>Kan %x niet vinden.</target> @@ -597,18 +636,12 @@ Werkelijk: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Kan de symbolische koppeling %x niet verwijderen.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Kan de vrije schijfruimte voor %x niet bepalen.</target> - <source>Incorrect command line:</source> <target>Onjuiste opdrachtregel:</target> <source>The server does not support authentication via %x.</source> <target>De server ondersteunt geen verificatie via %x.</target> -<source>Unable to access %x.</source> -<target>Geen toegang tot %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Werkelijk: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Fout bij het openen van SFTP kanaalnummer %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Slepen && neerzetten</target> @@ -764,6 +779,24 @@ De opdracht wordt geactiveerd als: <source>&Retry</source> <target>&Probeer opnieuw</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Laden...</target> @@ -867,7 +900,7 @@ De opdracht wordt geactiveerd als: <target>Lokale vergelijking instellingen</target> <source>Local synchronization settings</source> -<target>Lokale synchronisatie-instellingen</target> +<target>Lokale synchronisatie instellingen</target> <source>Local filter</source> <target>Lokaal filter</target> @@ -909,7 +942,7 @@ De opdracht wordt geactiveerd als: <target>Opslaan als &batchverwerking...</target> <source>Show &log</source> -<target></target> +<target>Bekijk &logbestand</target> <source>Start &comparison</source> <target>Start &vergelijking</target> @@ -971,9 +1004,6 @@ De opdracht wordt geactiveerd als: <source>Access online storage</source> <target>Toegang tot online opslag</target> -<source>Swap sides</source> -<target>Verwisselen van zijden</target> - <source>Close search bar</source> <target>Sluit de zoekbalk</target> @@ -998,6 +1028,9 @@ De opdracht wordt geactiveerd als: <source>View type:</source> <target>Type bekijken:</target> +<source>Save as default</source> +<target>Opslaan als standaard</target> + <source>Select view:</source> <target>Selecteer weergave:</target> @@ -1055,6 +1088,15 @@ De opdracht wordt geactiveerd als: <source>Handle daylight saving time</source> <target>Zomertijd gebruiken</target> +<source>Ignore errors</source> +<target>Negeer fouten</target> + +<source>Retry count:</source> +<target>Probeer opnieuw te tellen:</target> + +<source>Delay (in seconds):</source> +<target>Vertraging (in seconden):</target> + <source>Performance improvements:</source> <target>Prestatieverbeteringen:</target> @@ -1129,17 +1171,11 @@ De opdracht wordt geactiveerd als: <source>Last x days:</source> <target>Laatste x dagen:</target> -<source>Ignore errors</source> -<target>Negeer fouten</target> - -<source>Retry count:</source> -<target>Probeer opnieuw te tellen:</target> - -<source>Delay (in seconds):</source> -<target>Vertraging (in seconden):</target> +<source>&Override default log path:</source> +<target>&Overschrijf standaard logboekpad:</target> -<source>Run a command after synchronization:</source> -<target>Voer een opdracht uit na de synchronisatie:</target> +<source>Run a command:</source> +<target>Voer een opdracht uit:</target> <source>OK</source> <target>OK</target> @@ -1181,7 +1217,7 @@ De opdracht wordt geactiveerd als: <target>Gebruikersnaam:</target> <source>Private key file:</source> -<target>Prive-sleutel bestand:</target> +<target>Prive sleutelbestand:</target> <source>&Show password</source> <target>&Toon wachtwoord</target> @@ -1189,6 +1225,9 @@ De opdracht wordt geactiveerd als: <source>Directory on server:</source> <target>Map op de server:</target> +<source>Access timeout (in seconds):</source> +<target>Toegang timeout (in seconden):</target> + <source>SFTP channels per connection:</source> <target>SFTP kanalen per verbinding:</target> @@ -1267,12 +1306,6 @@ De opdracht wordt geactiveerd als: <source>Stop synchronization at first error</source> <target>Synchronisatie bij eerste fout stoppen</target> -<source>Save log:</source> -<target>Logbestand opslaan:</target> - -<source>Limit number of log files:</source> -<target>Beperk het aantal logbestanden:</target> - <source>How can I schedule a batch job?</source> <target>Hoe kan ik een batchverwerking plannen?</target> @@ -1309,8 +1342,11 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Alle permanent verborgen dialogen en waarschuwingsberichten opnieuw weergeven</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Standaard logboek pad:</target> + +<source>&Delete logs after x days:</source> +<target>&Verwijder logboeken na x dagen:</target> <source>Customize context menu:</source> <target>Contextmenu aanpassen:</target> @@ -1321,8 +1357,8 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>&Default</source> <target>&Standaard</target> -<source>Feedback and suggestions are welcome</source> -<target>Terugkoppeling en suggesties zijn welkom</target> +<source>Feedback and suggestions are welcome:</source> +<target>Terugkoppeling en suggesties zijn welkom:</target> <source>Home page</source> <target>Startpagina</target> @@ -1348,8 +1384,8 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Source code written in C++ using:</source> <target>Broncode werd in C++ geschreven met:</target> -<source>Published under the GNU General Public License</source> -<target>Gepubliceerd onder de GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Gepubliceerd onder de GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Hartelijk dank voor de lokalisatie:</target> @@ -1379,7 +1415,7 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <target>Markeer configuraties die niet meer dan het volgende aantal dagen zijn uitgevoerd:</target> <source>Synchronization Settings</source> -<target>Synchronisatie-instellingen</target> +<target>Synchronisatie instellingen</target> <source>Access Online Storage</source> <target>Toegang Online Opslag</target> @@ -1406,7 +1442,7 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Geen vermeldingen in het logboek</target> <source>Select all</source> <target>Alles selecteren</target> @@ -1432,6 +1468,9 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Overview</source> <target>Overzicht</target> +<source>Swap sides</source> +<target>Verwisselen van zijden</target> + <source>Show "%x"</source> <target>Toon "%x"</target> @@ -1606,9 +1645,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Show filtered or temporarily excluded files</source> <target>Toon gefilterde of tijdelijk uitgesloten bestanden</target> -<source>Save as default</source> -<target>Opslaan als standaard</target> - <source>Filter</source> <target>Filter</target> @@ -1954,12 +1990,6 @@ Dit garandeert een consistente status zelfs in het geval van een ernstige fout. <source>Checking recycle bin failed for folder %x.</source> <target>Controle van de prullenbak voor map %x is mislukt.</target> -<source>The following XML elements could not be read:</source> -<target>De volgende XML-elementen kunnen niet worden gelezen:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Configuratiebestand %x is onvolledig. De ontbrekende elementen worden ingesteld op hun standaardwaarden.</target> - <source>Prepare installation</source> <target>Installatie voorbereiden</target> diff --git a/FreeFileSync/Build/Languages/english_uk.lng b/FreeFileSync/Build/Languages/english_uk.lng index 23785c8e..9b2c4f75 100755 --- a/FreeFileSync/Build/Languages/english_uk.lng +++ b/FreeFileSync/Build/Languages/english_uk.lng @@ -7,15 +7,6 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> -<source>No log entries</source> -<target></target> - -<source>Remove old log files after x days:</source> -<target></target> - -<source>Show &log</source> -<target></target> - <source>Both sides have changed since last synchronization.</source> <target>Both sides have changed since last synchronisation.</target> @@ -169,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Items differ in attributes only</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>The name %x is used by more than one item in the folder.</target> + <source>Resolving symbolic link %x</source> <target>Resolving symbolic link %x</target> @@ -196,9 +190,6 @@ <source>File time tolerance</source> <target>File time tolerance</target> -<source>Folder access timeout</source> -<target>Folder access timeout</target> - <source>Run with background priority</source> <target>Run with background priority</target> @@ -343,8 +334,11 @@ <source>Update attributes on right</source> <target>Update attributes on right</target> -<source>Warning</source> -<target>Warning</target> +<source>Errors:</source> +<target>Errors:</target> + +<source>Warnings:</source> +<target>Warnings:</target> <source>Items processed:</source> <target>Elements processed:</target> @@ -355,6 +349,9 @@ <source>Total time:</source> <target>Total time:</target> +<source>Warning</source> +<target>Warning</target> + <source>Stopped</source> <target>Stopped</target> @@ -364,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Error parsing file %x, row %y, column %z.</target> +<source>Services</source> +<target>Services</target> + +<source>Show All</source> +<target>Show All</target> + +<source>Hide Others</source> +<target>Hide Others</target> + +<source>Hide %x</source> +<target>Hide %x</target> + +<source>Quit %x</source> +<target>Quit %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Cannot set directory locks for the following folders:</target> @@ -379,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Cannot read directory %x.</target> -<source>/sec</source> -<target>/sec</target> +<source>%x/sec</source> +<target>%x/sec</target> -<source>%x items/sec</source> -<target>%x items/sec</target> +<source>%x items</source> +<target>%x items</target> <source>Show in Explorer</source> <target>Show in Explorer</target> @@ -532,11 +544,11 @@ <source>Generating database...</source> <target>Generating database...</target> -<source>Searching for excess file versions:</source> -<target>Searching for excess file versions:</target> +<source>Searching for old file versions:</source> +<target>Searching for old file versions:</target> -<source>Removing excess file versions:</source> -<target>Removing excess file versions:</target> +<source>Removing old file versions:</source> +<target>Removing old file versions:</target> <source>Unable to create time stamp for versioning:</source> <target>Unable to create time stamp for versioning:</target> @@ -594,6 +606,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Unable to move %x to the recycle bin.</target> +<source>Unable to access %x.</source> +<target>Unable to access %x.</target> + +<source>Authentication completed.</source> +<target>Authentication completed.</target> + +<source>Authentication failed.</source> +<target>Authentication failed.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>You may close this page now and continue with FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>The server returned an error:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Cannot determine free disk space for %x.</target> + <source>Cannot find %x.</source> <target>Cannot find %x.</target> @@ -606,18 +636,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Cannot delete symbolic link %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Cannot determine free disk space for %x.</target> - <source>Incorrect command line:</source> <target>Incorrect command line:</target> <source>The server does not support authentication via %x.</source> <target>The server does not support authentication via %x.</target> -<source>Unable to access %x.</source> -<target>Unable to access %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -642,24 +666,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Failed to open SFTP channel number %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Drag && drop</target> @@ -773,6 +779,24 @@ The command is triggered if: <source>&Retry</source> <target>&Retry</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Loading...</target> @@ -917,6 +941,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>Save as &batch job...</target> +<source>Show &log</source> +<target>Show &log</target> + <source>Start &comparison</source> <target>Start &comparison</target> @@ -977,9 +1004,6 @@ The command is triggered if: <source>Access online storage</source> <target>Access online storage</target> -<source>Swap sides</source> -<target>Swap sides</target> - <source>Close search bar</source> <target>Close search bar</target> @@ -1004,6 +1028,9 @@ The command is triggered if: <source>View type:</source> <target>View type:</target> +<source>Save as default</source> +<target>Save as default</target> + <source>Select view:</source> <target>Select view:</target> @@ -1061,6 +1088,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>Handle daylight saving time</target> +<source>Ignore errors</source> +<target>Ignore errors</target> + +<source>Retry count:</source> +<target>Retry count:</target> + +<source>Delay (in seconds):</source> +<target>Delay (in seconds):</target> + <source>Performance improvements:</source> <target>Performance improvements:</target> @@ -1135,17 +1171,11 @@ The command is triggered if: <source>Last x days:</source> <target>Last x days:</target> -<source>Ignore errors</source> -<target>Ignore errors</target> - -<source>Retry count:</source> -<target>Retry count:</target> +<source>&Override default log path:</source> +<target>&Override default log path:</target> -<source>Delay (in seconds):</source> -<target>Delay (in seconds):</target> - -<source>Run a command after synchronization:</source> -<target>Run a command after synchronization:</target> +<source>Run a command:</source> +<target>Run a command:</target> <source>OK</source> <target>OK</target> @@ -1195,6 +1225,9 @@ The command is triggered if: <source>Directory on server:</source> <target>Directory on server:</target> +<source>Access timeout (in seconds):</source> +<target>Access timeout (in seconds):</target> + <source>SFTP channels per connection:</source> <target>SFTP channels per connection:</target> @@ -1273,12 +1306,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>Stop synchronisation at first error</target> -<source>Save log:</source> -<target>Save log:</target> - -<source>Limit number of log files:</source> -<target>Limit number of log files:</target> - <source>How can I schedule a batch job?</source> <target>How can I schedule a batch job?</target> @@ -1315,6 +1342,12 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Show all permanently hidden dialogues and warning messages again</target> +<source>Default log path:</source> +<target>Default log path:</target> + +<source>&Delete logs after x days:</source> +<target>&Delete logs after x days:</target> + <source>Customize context menu:</source> <target>Customise context menu:</target> @@ -1324,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>&Default</target> -<source>Feedback and suggestions are welcome</source> -<target>Feedback and suggestions are welcome</target> +<source>Feedback and suggestions are welcome:</source> +<target>Feedback and suggestions are welcome:</target> <source>Home page</source> <target>Home page</target> @@ -1351,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Source code written in C++ using:</target> -<source>Published under the GNU General Public License</source> -<target>Published under the GNU General Public Licence</target> +<source>Published under the GNU General Public License:</source> +<target>Published under the GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Many thanks for localisation:</target> @@ -1408,6 +1441,9 @@ This guarantees a consistent state even in case of a serious error. <source>Info</source> <target>Info</target> +<source>No log entries</source> +<target>No log entries</target> + <source>Select all</source> <target>Select all</target> @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>Overview</target> +<source>Swap sides</source> +<target>Swap sides</target> + <source>Show "%x"</source> <target>Show "%x"</target> @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>Show filtered or temporarily excluded files</target> -<source>Save as default</source> -<target>Save as default</target> - <source>Filter</source> <target>Filter</target> @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>Checking recycle bin failed for folder %x.</target> -<source>The following XML elements could not be read:</source> -<target>The following XML elements could not be read:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Configuration file %x is incomplete. The missing elements will be set to their default values.</target> - <source>Prepare installation</source> <target>Prepare installation</target> diff --git a/FreeFileSync/Build/Languages/french.lng b/FreeFileSync/Build/Languages/french.lng index fe53e352..ca08d5b3 100755 --- a/FreeFileSync/Build/Languages/french.lng +++ b/FreeFileSync/Build/Languages/french.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Seuls les attributs des éléments diffèrent</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Le nom %x est utilisé par plusieurs éléments du dossier.</target> + <source>Resolving symbolic link %x</source> <target>Résolution du lien symbolique %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Tolérance horaire</target> -<source>Folder access timeout</source> -<target>Dépassement du délai d'accès au dossier</target> - <source>Run with background priority</source> <target>Exécution avec la priorité de tâche de fond.</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Mise à jour des attributs à droite</target> -<source>Warning</source> -<target>Attention</target> +<source>Errors:</source> +<target>Erreurs :</target> + +<source>Warnings:</source> +<target>Avertissements :</target> <source>Items processed:</source> <target>Élements traités :</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Durée totale :</target> +<source>Warning</source> +<target>Attention</target> + <source>Stopped</source> <target>Arrêté</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Erreur lors de l'analyse du fichier %x, ligne %y, colonne %z.</target> +<source>Services</source> +<target>Services</target> + +<source>Show All</source> +<target>Tout afficher</target> + +<source>Hide Others</source> +<target>Masquer les autres</target> + +<source>Hide %x</source> +<target>Masquer %x</target> + +<source>Quit %x</source> +<target>Quitter %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Impossible de verrouiller les répertoires des dossiers suivants :</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Impossible de lire le répertoire %x.</target> -<source>/sec</source> -<target>/sec</target> +<source>%x/sec</source> +<target>%x/sec</target> -<source>%x items/sec</source> -<target>%x éléments/sec</target> +<source>%x items</source> +<target>%x éléments</target> <source>Show in Explorer</source> <target>Montrer dans l'explorateur</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Génération de la base de données ...</target> -<source>Searching for excess file versions:</source> -<target>Recherche des versions de fichiers excédentaires :</target> +<source>Searching for old file versions:</source> +<target>Recherche d'anciennes versions :</target> -<source>Removing excess file versions:</source> -<target>Suppression des versions de fichiers excédentaires :</target> +<source>Removing old file versions:</source> +<target>Suppression d'anciennes versions :</target> <source>Unable to create time stamp for versioning:</source> <target>Impossible de générer l'horodatage pour la gestion des versions :</target> @@ -585,6 +606,24 @@ Trouvé : %y octets <source>Unable to move %x to the recycle bin.</source> <target>Impossible de déplacer %x dans la Corbeille.</target> +<source>Unable to access %x.</source> +<target>Impossible d'accéder à %x.</target> + +<source>Authentication completed.</source> +<target>Identification terminée.</target> + +<source>Authentication failed.</source> +<target>Echec de l'identification.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Vous pouvez fermer cette page maintenant et poursuivre avec FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Le serveur a renvoyé une erreur :</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Impossible de calculer l'espace libre du disque %x.</target> + <source>Cannot find %x.</source> <target>Impossible de trouver %x.</target> @@ -597,18 +636,12 @@ Trouvé : %y octets <source>Cannot delete symbolic link %x.</source> <target>Impossible de supprimer le lien symbolique %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Impossible de calculer l'espace libre du disque %x.</target> - <source>Incorrect command line:</source> <target>Ligne de commande incorrecte :</target> <source>The server does not support authentication via %x.</source> <target>Le serveur refuse l'authentification par %x.</target> -<source>Unable to access %x.</source> -<target>Impossible d'accéder à %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Trouvé : %y octets <source>Failed to open SFTP channel number %x.</source> <target>Impossible d'ouvrir le port SFTP %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x octet</pluralform> -<pluralform>%x octets</pluralform> -</target> - -<source>%x MB</source> -<target>%x Mo</target> - -<source>%x KB</source> -<target>%x Ko</target> - -<source>%x GB</source> -<target>%x Go</target> - <source>Drag && drop</source> <target>Glisser && Déposer</target> @@ -764,6 +779,24 @@ La commande est déclenchée si : <source>&Retry</source> <target>&Réessayer</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x octet</pluralform> +<pluralform>%x octets</pluralform> +</target> + +<source>%x MB</source> +<target>%x Mo</target> + +<source>%x KB</source> +<target>%x Ko</target> + +<source>%x GB</source> +<target>%x Go</target> + <source>Loading...</source> <target>Chargement ...</target> @@ -909,7 +942,7 @@ La commande est déclenchée si : <target>Enregistrer en tant que fichier de &commandes ...</target> <source>Show &log</source> -<target></target> +<target>Afficher &journal</target> <source>Start &comparison</source> <target>Démarrer la &comparaison</target> @@ -971,9 +1004,6 @@ La commande est déclenchée si : <source>Access online storage</source> <target>Accès au stockage en ligne</target> -<source>Swap sides</source> -<target>Permuter les côtés</target> - <source>Close search bar</source> <target>Fermer la barre de recherche</target> @@ -998,6 +1028,9 @@ La commande est déclenchée si : <source>View type:</source> <target>Type de vue :</target> +<source>Save as default</source> +<target>Sauvegarde par défaut</target> + <source>Select view:</source> <target>Choisir la vue :</target> @@ -1055,6 +1088,15 @@ La commande est déclenchée si : <source>Handle daylight saving time</source> <target>Gérer l'heure d'été</target> +<source>Ignore errors</source> +<target>Ignorer les erreurs</target> + +<source>Retry count:</source> +<target>Nombre de tentatives :</target> + +<source>Delay (in seconds):</source> +<target>Délai (en secondes) :</target> + <source>Performance improvements:</source> <target>Amélioration des performances :</target> @@ -1129,17 +1171,11 @@ La commande est déclenchée si : <source>Last x days:</source> <target>Derniers x jours :</target> -<source>Ignore errors</source> -<target>Ignorer les erreurs</target> - -<source>Retry count:</source> -<target>Nombre de tentatives :</target> - -<source>Delay (in seconds):</source> -<target>Délai (en secondes) :</target> +<source>&Override default log path:</source> +<target>&Remplacer le chemin par défaut du journal :</target> -<source>Run a command after synchronization:</source> -<target>Exécuter une commande après la synchronisation :</target> +<source>Run a command:</source> +<target>Éxecuter une commande :</target> <source>OK</source> <target>OK</target> @@ -1189,6 +1225,9 @@ La commande est déclenchée si : <source>Directory on server:</source> <target>Répertoire sur le serveur :</target> +<source>Access timeout (in seconds):</source> +<target>Délai d'accès (en secondes) :</target> + <source>SFTP channels per connection:</source> <target>Ports SFTP par connexion :</target> @@ -1267,12 +1306,6 @@ La commande est déclenchée si : <source>Stop synchronization at first error</source> <target>Arrêter la synchronisation à la première erreur</target> -<source>Save log:</source> -<target>Sauvegarde du fichier log :</target> - -<source>Limit number of log files:</source> -<target>Limiter le nombre de journaux :</target> - <source>How can I schedule a batch job?</source> <target>Comment planifier un fichier de commandes ?</target> @@ -1309,8 +1342,11 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Réafficher en permanence les boîtes de dialogue et les avertissements</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Chemin du journal par défaut :</target> + +<source>&Delete logs after x days:</source> +<target>&Supprimer les journaux après x jours :</target> <source>Customize context menu:</source> <target>Personnaliser le menu contextuel :</target> @@ -1321,8 +1357,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>&Default</source> <target>&Défaut</target> -<source>Feedback and suggestions are welcome</source> -<target>Vos commentaires et vos suggestions sont les bienvenus</target> +<source>Feedback and suggestions are welcome:</source> +<target>Commentaires et suggestions sont les bienvenus :</target> <source>Home page</source> <target>Page d'accueil</target> @@ -1348,8 +1384,8 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Source code written in C++ using:</source> <target>Code source écrit en C++ utilisant :</target> -<source>Published under the GNU General Public License</source> -<target>Publié sous licence GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Publié sous licence publique générale GNU :</target> <source>Many thanks for localization:</source> <target>Un grand merci pour la traduction à :</target> @@ -1406,7 +1442,7 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Aucune entrée journal</target> <source>Select all</source> <target>Tout sélectionner</target> @@ -1432,6 +1468,9 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Overview</source> <target>Aperçu</target> +<source>Swap sides</source> +<target>Permuter les côtés</target> + <source>Show "%x"</source> <target>Afficher "%x"</target> @@ -1606,9 +1645,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Show filtered or temporarily excluded files</source> <target>Afficher les fichiers filtrés ou temporairement exclus</target> -<source>Save as default</source> -<target>Sauvegarde par défaut</target> - <source>Filter</source> <target>Filtre</target> @@ -1954,12 +1990,6 @@ Cela garantit la cohérence du système de fichiers en cas d'erreur grave. <source>Checking recycle bin failed for folder %x.</source> <target>Échec de la vérification de la Corbeille pour le dossier %x.</target> -<source>The following XML elements could not be read:</source> -<target>Les éléments XML suivants ne peuvent être lus :</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Le fichier de configuration %x est incomplet. Les éléments manquants sont fixés à leur valeur par défaut.</target> - <source>Prepare installation</source> <target>Préparation de l'installation</target> diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng index 4b79745a..093be509 100755 --- a/FreeFileSync/Build/Languages/german.lng +++ b/FreeFileSync/Build/Languages/german.lng @@ -7,21 +7,6 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> -<source>Access timeout (in seconds):</source> -<target>Zeitlimit für Zugriff(in Sekunden):</target> - -<source>The server returned an error:</source> -<target>Der Server hat einen Fehler zurückgegeben:</target> - -<source>You may close this page now and continue with FreeFileSync.</source> -<target>Sie können diese Seite nun schließen und mit FreeFileSync fortfahren.</target> - -<source>Authentication failed.</source> -<target>Authentifizierung fehlgeschlagen.</target> - -<source>Authentication completed.</source> -<target>Authentifizierung abgeschlossen.</target> - <source>Both sides have changed since last synchronization.</source> <target>Beide Seiten wurden seit der letzten Synchronisation verändert.</target> @@ -175,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Die Elemente unterscheiden sich nur in Attributen</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Der Name %x wird von mehr als einem Element im Ordner verwendet.</target> + <source>Resolving symbolic link %x</source> <target>Folge der symbolischen Verknüpfung %x</target> @@ -346,8 +334,11 @@ <source>Update attributes on right</source> <target>Aktualisiere Attribute des rechten Elements</target> -<source>Warning</source> -<target>Warnung</target> +<source>Errors:</source> +<target>Fehler:</target> + +<source>Warnings:</source> +<target>Warnungen:</target> <source>Items processed:</source> <target>Verarbeitete Elemente:</target> @@ -358,6 +349,9 @@ <source>Total time:</source> <target>Gesamtzeit:</target> +<source>Warning</source> +<target>Warnung</target> + <source>Stopped</source> <target>Gestoppt</target> @@ -367,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Fehler beim Auswerten der Datei %x, Zeile %y, Spalte %z.</target> +<source>Services</source> +<target>Dienste</target> + +<source>Show All</source> +<target>Alle einblenden</target> + +<source>Hide Others</source> +<target>Andere ausblenden</target> + +<source>Hide %x</source> +<target>%x ausblenden</target> + +<source>Quit %x</source> +<target>%x beenden</target> + <source>Cannot set directory locks for the following folders:</source> <target>Die Verzeichnissperren können für die folgenden Ordner nicht gesetzt werden:</target> @@ -382,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Das Verzeichnis %x kann nicht gelesen werden.</target> -<source>/sec</source> -<target>/sek</target> +<source>%x/sec</source> +<target>%x/sek</target> -<source>%x items/sec</source> -<target>%x Elemente/sek</target> +<source>%x items</source> +<target>%x Elemente</target> <source>Show in Explorer</source> <target>Im Explorer anzeigen</target> @@ -535,11 +544,11 @@ <source>Generating database...</source> <target>Erzeuge Synchronisationsdatenbank...</target> -<source>Searching for excess file versions:</source> -<target>Suche überzählige Dateiversionen:</target> +<source>Searching for old file versions:</source> +<target>Suche alte Dateiversionen:</target> -<source>Removing excess file versions:</source> -<target>Entferne überzählige Dateiversionen:</target> +<source>Removing old file versions:</source> +<target>Entferne alte Dateiversionen:</target> <source>Unable to create time stamp for versioning:</source> <target>Der Zeitstempel für die Versionierung kann nicht erstellt werden:</target> @@ -600,6 +609,18 @@ Tatsächlich: %y bytes <source>Unable to access %x.</source> <target>Auf %x kann nicht zugegriffen werden.</target> +<source>Authentication completed.</source> +<target>Authentifizierung abgeschlossen.</target> + +<source>Authentication failed.</source> +<target>Authentifizierung fehlgeschlagen.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Sie können diese Seite nun schließen und mit FreeFileSync fortfahren.</target> + +<source>The server returned an error:</source> +<target>Der Server hat einen Fehler zurückgegeben:</target> + <source>Cannot determine free disk space for %x.</source> <target>Der freie Speicherplatz für %x konnte nicht ermittelt werden.</target> @@ -983,9 +1004,6 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Access online storage</source> <target>Auf Onlinespeicher zugreifen</target> -<source>Swap sides</source> -<target>Seiten vertauschen</target> - <source>Close search bar</source> <target>Suchleiste schließen</target> @@ -1010,6 +1028,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>View type:</source> <target>Ansichtstyp:</target> +<source>Save as default</source> +<target>Als Standard speichern</target> + <source>Select view:</source> <target>Ansicht wählen:</target> @@ -1153,8 +1174,8 @@ Die Befehlszeile wird ausgelöst, wenn: <source>&Override default log path:</source> <target>&Standardprotokollpfad überschreiben:</target> -<source>Run a command after synchronization:</source> -<target>Befehl nach Synchronisation ausführen:</target> +<source>Run a command:</source> +<target>Befehl ausführen:</target> <source>OK</source> <target>OK</target> @@ -1204,6 +1225,9 @@ Die Befehlszeile wird ausgelöst, wenn: <source>Directory on server:</source> <target>Verzeichnis auf Server:</target> +<source>Access timeout (in seconds):</source> +<target>Zeitlimit für Zugriff(in Sekunden):</target> + <source>SFTP channels per connection:</source> <target>SFTP Kanäle je Verbindung:</target> @@ -1321,8 +1345,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Default log path:</source> <target>Standardprotokollpfad:</target> -<source>Remove old log files after x days:</source> -<target>Alte Protokolldateien nach x Tagen entfernen:</target> +<source>&Delete logs after x days:</source> +<target>&Protokolldateien nach x Tagen löschen:</target> <source>Customize context menu:</source> <target>Kontextmenü anpassen:</target> @@ -1333,8 +1357,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>&Default</source> <target>&Standard</target> -<source>Feedback and suggestions are welcome</source> -<target>Feedback und Vorschläge sind willkommen</target> +<source>Feedback and suggestions are welcome:</source> +<target>Hinweise und Vorschläge sind willkommen:</target> <source>Home page</source> <target>Homepage</target> @@ -1360,8 +1384,8 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Source code written in C++ using:</source> <target>Der Quellcode wurde in C++ geschrieben mit:</target> -<source>Published under the GNU General Public License</source> -<target>Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz</target> +<source>Published under the GNU General Public License:</source> +<target>Veröffentlicht unter der Allgemeinen Öffentlichen GNU-Lizenz:</target> <source>Many thanks for localization:</source> <target>Vielen Dank für die Lokalisation:</target> @@ -1444,6 +1468,9 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Overview</source> <target>Übersicht</target> +<source>Swap sides</source> +<target>Seiten vertauschen</target> + <source>Show "%x"</source> <target>Zeige "%x"</target> @@ -1618,9 +1645,6 @@ Dadurch wird ein konsistenter Datenstand auch bei schweren Fehlern garantiert. <source>Show filtered or temporarily excluded files</source> <target>Gefilterte oder temporär ausgeschlossene Dateien zeigen</target> -<source>Save as default</source> -<target>Als Standard speichern</target> - <source>Filter</source> <target>Filter</target> diff --git a/FreeFileSync/Build/Languages/greek.lng b/FreeFileSync/Build/Languages/greek.lng index c09db39b..ab0c8dfb 100755 --- a/FreeFileSync/Build/Languages/greek.lng +++ b/FreeFileSync/Build/Languages/greek.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Τα στοιχεία διαφέρουν μόνο ως προς τα χαρακτηριστικά τους</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Το όνομα %x χρησιμοποιείται για περισσότερα από ένα αρχεία στον φάκελο.</target> + <source>Resolving symbolic link %x</source> <target>Επίλυση του συμβολικού συνδέσμου %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Ανοχή στην ημερομηνία αρχείου</target> -<source>Folder access timeout</source> -<target>Χρονικό όριο πρόσβασης στο φάκελο</target> - <source>Run with background priority</source> <target>Εκτέλεση με προτεραιότητα παρασκηνίου</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Ενημέρωση των χαρακτηριστικών στα δεξιά</target> -<source>Warning</source> -<target>Προειδοποίηση</target> +<source>Errors:</source> +<target>Σφάλματα:</target> + +<source>Warnings:</source> +<target>Προειδοποιήσεις:</target> <source>Items processed:</source> <target>Επεξεργάστηκαν στοιχεία:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Συνολική διάρκεια:</target> +<source>Warning</source> +<target>Προειδοποίηση</target> + <source>Stopped</source> <target>Διακοπή</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Σφάλμα κατά την ανάλυση του αρχείου %x, γραμμή %y, στήλη %z.</target> +<source>Services</source> +<target>Υπηρεσίες</target> + +<source>Show All</source> +<target>Εμφάνιση Όλων</target> + +<source>Hide Others</source> +<target>Απόκρυψη Άλλων</target> + +<source>Hide %x</source> +<target>Απόκρυψη %x</target> + +<source>Quit %x</source> +<target>Έξοδος από %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Δεν μπορούν να κλειδωθούν οι ακόλουθοι υποκατάλογοι:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Δεν μπορεί να γίνει ανάγνωση του καταλόγου %x.</target> -<source>/sec</source> -<target>/δευτερόλεπτο</target> +<source>%x/sec</source> +<target>%x/δευτερόλεπτο</target> -<source>%x items/sec</source> -<target>%x στοιχεία/δευτερόλεπτο</target> +<source>%x items</source> +<target>%x στοιχεία</target> <source>Show in Explorer</source> <target>Εμφάνιση στην Εξερεύνηση</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Δημιουργία βάσης δεδομένων...</target> -<source>Searching for excess file versions:</source> -<target>Αναζήτηση υπεράριθμων παλιών εκδόσεων:</target> +<source>Searching for old file versions:</source> +<target>Αναζήτηση παλιών εκδόσεων των αρχείων:</target> -<source>Removing excess file versions:</source> -<target>Αφαίρεση υπεράριθμων παλιών εκδόσεων:</target> +<source>Removing old file versions:</source> +<target>Διαγραφή παλιών εκδόσεων των αρχείων:</target> <source>Unable to create time stamp for versioning:</source> <target>Δεν ήταν δυνατή η δημιουργία χρονικής σήμανσης για τη διατήρηση παλιών εκδόσεων:</target> @@ -585,6 +606,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Δεν ήταν δυνατή η μεταφορά του %x στον κάδο ανακύκλωσης.</target> +<source>Unable to access %x.</source> +<target>Δεν είναι δυνατή η πρόσβαση στο %x.</target> + +<source>Authentication completed.</source> +<target>Ολοκληρώθηκε η επαλήθευση ταυτότητας.</target> + +<source>Authentication failed.</source> +<target>Η επαλήθευση ταυτότητας απέτυχε.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Τώρα μπορείτε να κλείσετε αυτή τη σελίδα και να συνεχίσετε το FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Ο διακομιστής επέστρεψε το λάθος:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Δεν μπορεί να υπολογιστεί ο ελεύθερος χώρος του δίσκου %x.</target> + <source>Cannot find %x.</source> <target>Το %x δεν μπορεί να βρεθεί.</target> @@ -597,17 +636,11 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Δεν μπορεί να διαγραφεί ο συμβολικός σύνδεσμος %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Δεν μπορεί να υπολογιστεί ο ελεύθερος χώρος του δίσκου %x.</target> - <source>Incorrect command line:</source> <target>Εσφαλμένη γραμμή εντολών:</target> <source>The server does not support authentication via %x.</source> -<target>Ο εξυπηρετητής δεν υποστηρίζει πιστοποίηση μέσω %x.</target> - -<source>Unable to access %x.</source> -<target>Δεν είναι δυνατή η πρόσβαση στο %x.</target> +<target>Ο εξυπηρετητής δεν υποστηρίζει επαλήθευση ταυτότητας μέσω %x.</target> <source> <pluralform>Operation timed out after 1 second.</pluralform> @@ -633,24 +666,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Αποτυχία ανοίγματος του SFTP καναλιού με αριθμό %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Μεταφορά && Απόθεση</target> @@ -764,6 +779,24 @@ The command is triggered if: <source>&Retry</source> <target>&Επανάληψη</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Φόρτωση...</target> @@ -909,7 +942,7 @@ The command is triggered if: <target>Αποθήκευση ως δέσ&μη ενεργειών...</target> <source>Show &log</source> -<target></target> +<target>Εμφάνιση αρχείου &καταγραφής</target> <source>Start &comparison</source> <target>Έ&ναρξη σύγκρισης</target> @@ -971,9 +1004,6 @@ The command is triggered if: <source>Access online storage</source> <target>Πρόσβαση σε online χώρο αποθήκευσης</target> -<source>Swap sides</source> -<target>Ανταλλαγή πλευρών</target> - <source>Close search bar</source> <target>Κλείσιμο γραμμής αναζήτησης</target> @@ -998,6 +1028,9 @@ The command is triggered if: <source>View type:</source> <target>Τύπος εμφάνισης:</target> +<source>Save as default</source> +<target>Αποθήκευση ως προεπιλογής</target> + <source>Select view:</source> <target>Επιλογή εμφάνισης:</target> @@ -1055,6 +1088,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>Διαχείριση θερινής ώρας</target> +<source>Ignore errors</source> +<target>Αγνόηση σφαλμάτων</target> + +<source>Retry count:</source> +<target>Αριθμός προσπαθειών:</target> + +<source>Delay (in seconds):</source> +<target>Καθυστέρηση (σε δευτερόλεπτα):</target> + <source>Performance improvements:</source> <target>Βελτιώσεις της απόδοσης:</target> @@ -1129,17 +1171,11 @@ The command is triggered if: <source>Last x days:</source> <target>Τις τελευταίες x μέρες:</target> -<source>Ignore errors</source> -<target>Αγνόηση σφαλμάτων</target> - -<source>Retry count:</source> -<target>Αριθμός προσπαθειών:</target> - -<source>Delay (in seconds):</source> -<target>Καθυστέρηση (σε δευτερόλεπτα):</target> +<source>&Override default log path:</source> +<target>&Παράκαμψη προεπιλεγμένης διαδρομής για το αρχείο καταγραφής:</target> -<source>Run a command after synchronization:</source> -<target>Εκτέλεση εντολής μετά το συγχρονισμό:</target> +<source>Run a command:</source> +<target>Εκτέλεση της εντολής:</target> <source>OK</source> <target>OK</target> @@ -1166,7 +1202,7 @@ The command is triggered if: <target>&Ρητά SSL/TLS</target> <source>Authentication:</source> -<target>Πιστοποίηση:</target> +<target>Επαλήθευση ταυτότητας:</target> <source>&Password</source> <target>&Κωδικός πρόσβασης</target> @@ -1189,6 +1225,9 @@ The command is triggered if: <source>Directory on server:</source> <target>Υποκατάλογος στον διακομιστή:</target> +<source>Access timeout (in seconds):</source> +<target>Χρονικό όριο σύνδεσης (σε δευτερόλεπτα):</target> + <source>SFTP channels per connection:</source> <target>Αριθμός καναλιών SFTP ανά σύνδεση:</target> @@ -1267,12 +1306,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>Διακοπή του συγχρονισμού με το πρώτο σφάλμα</target> -<source>Save log:</source> -<target>Αποθήκευση αρχείου καταγραφής:</target> - -<source>Limit number of log files:</source> -<target>Περιορισμός αριθμού αρχείων καταγραφής:</target> - <source>How can I schedule a batch job?</source> <target>Πώς μπορώ να προγραμματίσω μια εργασία με δέσμη ενεργειών;</target> @@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Επανεμφάνιση όλων των μόνιμα κρυμμένων ειδοποιήσεων και προειδοποιητικών μηνυμάτων</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Προεπιλεγμένη διαδρομή για το αρχείο καταγραφής:</target> + +<source>&Delete logs after x days:</source> +<target>&Διαγραφή των αρχείων καταγραφής μετά από x ημέρες:</target> <source>Customize context menu:</source> <target>Προσαρμογή μενού περιβάλλοντος:</target> @@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>&Προεπιλογή</target> -<source>Feedback and suggestions are welcome</source> -<target>Τα σχόλια και οι προτάσεις σας είναι ευπρόσδεκτα</target> +<source>Feedback and suggestions are welcome:</source> +<target>Σχόλια και προτάσεις είναι ευπρόσδεκτα:</target> <source>Home page</source> <target>Αρχική σελίδα</target> @@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Ο πηγαίος κώδικας γράφτηκε σε C++ χρησιμοποιώντας τα:</target> -<source>Published under the GNU General Public License</source> -<target>Διανέμεται υπό την Γενική Άδεια Δημόσιας Χρήσης GNU</target> +<source>Published under the GNU General Public License:</source> +<target>Δημοσιευμένο υπό την Γενική Άδεια Δημόσιας Χρήσης GNU:</target> <source>Many thanks for localization:</source> <target>Πολλές ευχαριστίες για τις μεταφράσεις:</target> @@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error. <target>Πληροφορίες</target> <source>No log entries</source> -<target></target> +<target>Καμία καταγραφή</target> <source>Select all</source> <target>Επιλογή όλων</target> @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>Σύνοψη</target> +<source>Swap sides</source> +<target>Ανταλλαγή πλευρών</target> + <source>Show "%x"</source> <target>Εμφάνιση της γραμμής "%x"</target> @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>Εμφάνιση φιλτραρισμένων ή προσωρινά εξαιρεθέντων αρχείων</target> -<source>Save as default</source> -<target>Αποθήκευση ως προεπιλογής</target> - <source>Filter</source> <target>Φίλτρο</target> @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>Ο έλεγχος του κάδου ανακύκλωσης απέτυχε για τον υποκατάλογο %x.</target> -<source>The following XML elements could not be read:</source> -<target>Τα ακόλουθα στοιχεία XML δεν ήταν δυνατό να αναγνωσθούν:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Το αρχείο παραμέτρων %x είναι ατελές. Τα στοιχεία που λείπουν θα αντικατασταθούν με τις προεπιλεγμένες τιμές τους.</target> - <source>Prepare installation</source> <target>Προετοιμασία εγκατάστασης</target> diff --git a/FreeFileSync/Build/Languages/hebrew.lng b/FreeFileSync/Build/Languages/hebrew.lng index 7885afd1..8327f635 100755 --- a/FreeFileSync/Build/Languages/hebrew.lng +++ b/FreeFileSync/Build/Languages/hebrew.lng @@ -7,15 +7,6 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> -<source>No log entries</source> -<target></target> - -<source>Remove old log files after x days:</source> -<target></target> - -<source>Show &log</source> -<target></target> - <source>Both sides have changed since last synchronization.</source> <target>שני הצדדים שונו מאז הסנכרון האחרון.</target> @@ -169,6 +160,9 @@ <source>Items differ in attributes only</source> <target>פריטים שונים בתכונות בלבד</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>בשם %x נעשה שימוש יותר מפעם אחת בתיקייה.</target> + <source>Resolving symbolic link %x</source> <target>פותר קישור סימבולי %x</target> @@ -196,9 +190,6 @@ <source>File time tolerance</source> <target>אפיצות זמן קובץ</target> -<source>Folder access timeout</source> -<target>זמן קצוב לגישה לתיייה</target> - <source>Run with background priority</source> <target>בצע עם עדיפות רקע</target> @@ -343,8 +334,11 @@ <source>Update attributes on right</source> <target>עדכן תכונות בצד שמאל</target> -<source>Warning</source> -<target>אזהרה</target> +<source>Errors:</source> +<target>שגיאות:</target> + +<source>Warnings:</source> +<target>אזהרות:</target> <source>Items processed:</source> <target>אלמנטים עובדו:</target> @@ -355,6 +349,9 @@ <source>Total time:</source> <target>זמן כולל:</target> +<source>Warning</source> +<target>אזהרה</target> + <source>Stopped</source> <target>נעצר</target> @@ -364,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>שגיאה בפענוח קובץ %x, שורה %y, טור %z.</target> +<source>Services</source> +<target>שרותים</target> + +<source>Show All</source> +<target>הראה הכל</target> + +<source>Hide Others</source> +<target>מסתיר אחרים</target> + +<source>Hide %x</source> +<target>מסתיר %x</target> + +<source>Quit %x</source> +<target>יוצא %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>אין אפשרות להגדיר נעילות מחיצה עבור התיקיות הבאות:</target> @@ -379,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>לא ניתן לקרוא מחיצה %x.</target> -<source>/sec</source> -<target>/שנ</target> +<source>%x/sec</source> +<target>%x /שניות</target> -<source>%x items/sec</source> -<target>%x פריטים לשניה</target> +<source>%x items</source> +<target>%x פריטים</target> <source>Show in Explorer</source> <target>הראה בסייר הקבצים</target> @@ -532,11 +544,11 @@ <source>Generating database...</source> <target>מייצר מסד נתונים...</target> -<source>Searching for excess file versions:</source> -<target>מחפש גרסאות קובץ עודפות:</target> +<source>Searching for old file versions:</source> +<target>מחפש גרסאות קובץ ישנות:</target> -<source>Removing excess file versions:</source> -<target>מסיר גרסאות קובץ עודפות:</target> +<source>Removing old file versions:</source> +<target>מסיר גרסאות קובץ ישנות:</target> <source>Unable to create time stamp for versioning:</source> <target>לא ניתן ליצור תג זמן לגרסאות:</target> @@ -594,6 +606,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>לא יכול להעביר את %x לסל המחזור.</target> +<source>Unable to access %x.</source> +<target>לא ניתן לגשת אל %x.</target> + +<source>Authentication completed.</source> +<target>האימות הושלם.</target> + +<source>Authentication failed.</source> +<target>האימות נכשל.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>אתה יכול לסגור דף זה עכשיו ולהמשיך עם FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>השרת החזיר שגיאה:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>לא ניתן לקבוע שטח דיסק פנוי עבור %x.</target> + <source>Cannot find %x.</source> <target>לא מוצא %x.</target> @@ -606,18 +636,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>לא ניתן למחוק קישור סימבולי %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>לא ניתן לקבוע שטח דיסק פנוי עבור %x.</target> - <source>Incorrect command line:</source> <target>שורת פקודה לא תקינה:</target> <source>The server does not support authentication via %x.</source> <target>השרת אינו תומך באימות באמצעות %x.</target> -<source>Unable to access %x.</source> -<target>לא ניתן לגשת אל %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -642,24 +666,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>נכשלה פתיחת ערוץ SFTP מספר %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>בית 1</pluralform> -<pluralform>%x בתים</pluralform> -</target> - -<source>%x MB</source> -<target>%x מגה בייט</target> - -<source>%x KB</source> -<target>%x קילו בייט</target> - -<source>%x GB</source> -<target>%x גיגה בייט</target> - <source>Drag && drop</source> <target>גרור ושחרר</target> @@ -773,6 +779,24 @@ The command is triggered if: <source>&Retry</source> <target>&נסה שנית</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>בית 1</pluralform> +<pluralform>%x בתים</pluralform> +</target> + +<source>%x MB</source> +<target>%x מגה בייט</target> + +<source>%x KB</source> +<target>%x קילו בייט</target> + +<source>%x GB</source> +<target>%x גיגה בייט</target> + <source>Loading...</source> <target>טוען...</target> @@ -917,6 +941,9 @@ The command is triggered if: <source>Save as &batch job...</source> <target>שמור כעבודת &אצווה...</target> +<source>Show &log</source> +<target>הצג &יומנים:</target> + <source>Start &comparison</source> <target>התחל &והשווה</target> @@ -977,9 +1004,6 @@ The command is triggered if: <source>Access online storage</source> <target>גש לאחסון מכוון</target> -<source>Swap sides</source> -<target>החלף צדדים</target> - <source>Close search bar</source> <target>סגור סרגל חיפוש</target> @@ -1004,6 +1028,9 @@ The command is triggered if: <source>View type:</source> <target>הצג סוג:</target> +<source>Save as default</source> +<target>שמור כברירת מחדל</target> + <source>Select view:</source> <target>בחר תצוגה:</target> @@ -1061,6 +1088,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>התמודד עם שעון קיץ</target> +<source>Ignore errors</source> +<target>התעלם משגיאות</target> + +<source>Retry count:</source> +<target>מונה נסיונות חוזרים:</target> + +<source>Delay (in seconds):</source> +<target>השהייה (בשניות):</target> + <source>Performance improvements:</source> <target>שיפורים בביצועים:</target> @@ -1135,17 +1171,11 @@ The command is triggered if: <source>Last x days:</source> <target>x ימים אחרונים:</target> -<source>Ignore errors</source> -<target>התעלם משגיאות</target> - -<source>Retry count:</source> -<target>מונה נסיונות חוזרים:</target> +<source>&Override default log path:</source> +<target>&דרוס נתיב ברירת מחדל של יומן:</target> -<source>Delay (in seconds):</source> -<target>השהייה (בשניות):</target> - -<source>Run a command after synchronization:</source> -<target>הרץ פקודה לאחר סינכרון:</target> +<source>Run a command:</source> +<target>הפעל פקודה:</target> <source>OK</source> <target>אשר</target> @@ -1195,6 +1225,9 @@ The command is triggered if: <source>Directory on server:</source> <target>מחיצה על שרת:</target> +<source>Access timeout (in seconds):</source> +<target>זמן קצוב לתפוגה (בשניות):</target> + <source>SFTP channels per connection:</source> <target>ערוצי SFTP לחיבור:</target> @@ -1273,12 +1306,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>עצור סנכרון עם הופעת שגיאה ראשונה</target> -<source>Save log:</source> -<target>שמור יומן:</target> - -<source>Limit number of log files:</source> -<target>הגבל מספר של קבצי יומן:</target> - <source>How can I schedule a batch job?</source> <target>כיצד לתזמן משימת אצווה?</target> @@ -1315,6 +1342,12 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>הראה שוב את כל הדיאלוגים והודאות האזהרה המוסתרים באופן קבוע</target> +<source>Default log path:</source> +<target>נתיב יומן ברירת מחדל:</target> + +<source>&Delete logs after x days:</source> +<target>&מחק יומנים לאחר x ימים:</target> + <source>Customize context menu:</source> <target>התאמה אישית של תפריט הקשר:</target> @@ -1324,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>&ברירת מחדל</target> -<source>Feedback and suggestions are welcome</source> -<target>משוב והצעות יתקבלו בברכה</target> +<source>Feedback and suggestions are welcome:</source> +<target>משוב והצעות יתקבלו בברכה:</target> <source>Home page</source> <target>דף הבית</target> @@ -1351,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>קוד מקור נכתב ב- C++ באמצעות:</target> -<source>Published under the GNU General Public License</source> -<target>מפורסם במסגרת GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>פורסם תחת הרישיון הציבורי הכללי של GNU:</target> <source>Many thanks for localization:</source> <target>תודות עבור תרגום שפות:</target> @@ -1408,6 +1441,9 @@ This guarantees a consistent state even in case of a serious error. <source>Info</source> <target>מידע</target> +<source>No log entries</source> +<target>אין רשומות יומן</target> + <source>Select all</source> <target>בחר הכל</target> @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>מבט כללי</target> +<source>Swap sides</source> +<target>החלף צדדים</target> + <source>Show "%x"</source> <target>הראה "%x"</target> @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>הראה קבצים שסוננו או לא נכללו זמנית</target> -<source>Save as default</source> -<target>שמור כברירת מחדל</target> - <source>Filter</source> <target>מסנן</target> @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>בודק כשלון בתא מחזור עבור תיקייה %x.</target> -<source>The following XML elements could not be read:</source> -<target>לא ניתן לקרוא את רכיבי XML הבאים:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>קובץ תצורה %x אינו שלם. הרכיבים החסרים יוגדרו עם ערכי ברירת המחדל.</target> - <source>Prepare installation</source> <target>מכין התקנה</target> diff --git a/FreeFileSync/Build/Languages/hindi.lng b/FreeFileSync/Build/Languages/hindi.lng index 67a3bfcc..f338466b 100755 --- a/FreeFileSync/Build/Languages/hindi.lng +++ b/FreeFileSync/Build/Languages/hindi.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>आइटम्स के केवल गुण ही भिन्न हैं।</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>%x नाम का उपयोग फ़ोल्डर में एक से अधिक आइटम द्वारा किया गया है।</target> + <source>Resolving symbolic link %x</source> <target>%x प्रतीकात्मक कड़ी को हल किया जा रहा है</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>फ़ाइल समय टॉलरेंस (छूट)</target> -<source>Folder access timeout</source> -<target>निर्देशिका पहुँच काल समापन</target> - <source>Run with background priority</source> <target>पृष्ठभूमि प्राथमिकता के साथ चलाएँ</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>दाईं तरफ के गुण अद्यतन करें</target> -<source>Warning</source> -<target>चेतावनी</target> +<source>Errors:</source> +<target>त्रुटियाँ:</target> + +<source>Warnings:</source> +<target>चेतावनियाँ:</target> <source>Items processed:</source> <target>संसाधित आइटम्स:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>कुल समय:</target> +<source>Warning</source> +<target>चेतावनी</target> + <source>Stopped</source> <target>रुका</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>फ़ाइल %x, पंक्ति %y, स्तंभ %z पदच्छेदन में त्रुटि।</target> +<source>Services</source> +<target>सेवाएँ</target> + +<source>Show All</source> +<target>सब दिखाओ</target> + +<source>Hide Others</source> +<target>अन्य छुपाएं</target> + +<source>Hide %x</source> +<target>%x छुपाएं</target> + +<source>Quit %x</source> +<target>%x छोड़ें</target> + <source>Cannot set directory locks for the following folders:</source> <target>निम्न निर्देशिकाओं के लिए निर्देशिका अवरोध सेट नहीं कर सकते:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>निर्देशिका %x को पढ़ा नहीं जा सकता।</target> -<source>/sec</source> -<target>/सेकंड</target> +<source>%x/sec</source> +<target>%x/सेकंड</target> -<source>%x items/sec</source> -<target>%x आइटम्स/सेकंड</target> +<source>%x items</source> +<target>%x आइटम्स</target> <source>Show in Explorer</source> <target>एक्सप्लोरर में दिखाएं</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>डेटाबेस बनाया जा रहा है...</target> -<source>Searching for excess file versions:</source> -<target>अतिरिक्त फ़ाइल संस्करणों की खोज हो रही है:</target> +<source>Searching for old file versions:</source> +<target>पुराने फ़ाइल संस्करणों की खोज हो रही है:</target> -<source>Removing excess file versions:</source> -<target>अतिरिक्त फ़ाइल संस्करणों को हटाया जा रहा है:</target> +<source>Removing old file versions:</source> +<target>पुराने फ़ाइल संस्करणों को हटाया जा रहा है:</target> <source>Unable to create time stamp for versioning:</source> <target>संस्करण के लिए समय मोहर बनाने में असमर्थ:</target> @@ -585,6 +606,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>%x को रीसायकल बिन ले जाने में असमर्थ।</target> +<source>Unable to access %x.</source> +<target>%x तक पहुँचने में असमर्थ।</target> + +<source>Authentication completed.</source> +<target>प्रमाणन पूर्ण।</target> + +<source>Authentication failed.</source> +<target>प्रमाणन विफल।</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>आप अब इस पृष्ठ को बंद कर FreeFileSync जारी रख सकते हैं।</target> + +<source>The server returned an error:</source> +<target>सर्वर ने एक त्रुटि वापस कर दी:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>%x के लिए खाली डिस्क जगह निर्धारित नहीं कर सकते।</target> + <source>Cannot find %x.</source> <target>%x नहीं मिला।</target> @@ -597,18 +636,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>प्रतीकात्मक कड़ी %x हटाया नहीं जा सकता।</target> -<source>Cannot determine free disk space for %x.</source> -<target>%x के लिए खाली डिस्क जगह निर्धारित नहीं कर सकते।</target> - <source>Incorrect command line:</source> <target>अनुचित आदेश-पंक्ति:</target> <source>The server does not support authentication via %x.</source> <target>सर्वर पर %x द्वारा प्रमाणन समर्थित नहीं है।</target> -<source>Unable to access %x.</source> -<target>%x तक पहुँचने में असमर्थ।</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>SFTP चैनल संख्या %x को खोलने में विफल।</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 बाइट</pluralform> -<pluralform>%x बाइट्स</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>ड्रॅग एण्ड ड्रॉप</target> @@ -764,6 +779,24 @@ The command is triggered if: <source>&Retry</source> <target>पुनः प्रयास (&R)</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 बाइट</pluralform> +<pluralform>%x बाइट्स</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>लोड हो रहा है...</target> @@ -909,7 +942,7 @@ The command is triggered if: <target>बॅच जॉब के रूप में सहेजें (&b)...</target> <source>Show &log</source> -<target></target> +<target>लॉग दिखाएं (&l)</target> <source>Start &comparison</source> <target>तुलना शुरू करें (&c)</target> @@ -971,9 +1004,6 @@ The command is triggered if: <source>Access online storage</source> <target>ऑनलाइन संग्रहण पहुँच प्राप्त करें</target> -<source>Swap sides</source> -<target>पक्ष गमागम करें</target> - <source>Close search bar</source> <target>खोज पट्टी बंद करें</target> @@ -998,6 +1028,9 @@ The command is triggered if: <source>View type:</source> <target>दृश्य प्रकार:</target> +<source>Save as default</source> +<target>डिफ़ॉल्ट के रूप में सहेजें</target> + <source>Select view:</source> <target>दृश्य चुनें:</target> @@ -1055,6 +1088,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>दिवालोक बचत समय (डेलाईट सेविंग टाइम) प्रहस्तन करें</target> +<source>Ignore errors</source> +<target>त्रुटियों को अनदेखा करें</target> + +<source>Retry count:</source> +<target>पुनः प्रयास गणनांक:</target> + +<source>Delay (in seconds):</source> +<target>विलंब (सेकंड में):</target> + <source>Performance improvements:</source> <target>प्रदर्शन सुधार:</target> @@ -1129,17 +1171,11 @@ The command is triggered if: <source>Last x days:</source> <target>पिछले x दिन:</target> -<source>Ignore errors</source> -<target>त्रुटियों को अनदेखा करें</target> - -<source>Retry count:</source> -<target>पुनः प्रयास गणनांक:</target> - -<source>Delay (in seconds):</source> -<target>विलंब (सेकंड में):</target> +<source>&Override default log path:</source> +<target>डिफ़ॉल्ट लॉग पथ अधिभूत करें (&O):</target> -<source>Run a command after synchronization:</source> -<target>सिंक्रनाइज़ेशन के बाद कोई आदेश चलाएँ:</target> +<source>Run a command:</source> +<target>आदेश चलाएँ:</target> <source>OK</source> <target>ठीक</target> @@ -1189,6 +1225,9 @@ The command is triggered if: <source>Directory on server:</source> <target>सर्वर पर निर्देशिका:</target> +<source>Access timeout (in seconds):</source> +<target>पहुँच समय बाह्य (सेकंड्स में):</target> + <source>SFTP channels per connection:</source> <target>SFTP चैनल्स प्रति कनेक्शन:</target> @@ -1267,12 +1306,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>पहली त्रुटि पर सिंक्रनाइज़ेशन रोकें</target> -<source>Save log:</source> -<target>लॉग सहेजें:</target> - -<source>Limit number of log files:</source> -<target>लॉग फ़ाइलों की संख्या सीमित करें:</target> - <source>How can I schedule a batch job?</source> <target>मैं कोई बॅच कार्य कैसे अनुसूचित करूँ?</target> @@ -1309,8 +1342,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>स्थायी रूप से छिपाये संवाद बॉक्सेस और चेतावनी संदेश फिर से दिखाएं</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>डिफ़ॉल्ट लॉग पथ:</target> + +<source>&Delete logs after x days:</source> +<target>x दिनों के बाद लॉग हटाएं (&D):</target> <source>Customize context menu:</source> <target>प्रासंगिक मेनू अनुकूलित करें:</target> @@ -1321,8 +1357,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>डिफ़ॉल्ट (&D)</target> -<source>Feedback and suggestions are welcome</source> -<target>प्रतिक्रिया और सुझावों का स्वागत है</target> +<source>Feedback and suggestions are welcome:</source> +<target>प्रतिक्रिया और सुझाव का स्वागत है:</target> <source>Home page</source> <target>मुख पृष्ठ</target> @@ -1348,8 +1384,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>स्रोत कोड C++ में लिखा गया इनके प्रयोग से:</target> -<source>Published under the GNU General Public License</source> -<target>जीएनयू जनरल पब्लिक लाइसेंस (GNU General Public License) के अंतर्गत प्रकाशित</target> +<source>Published under the GNU General Public License:</source> +<target>जीएनयू जनरल पब्लिक लाइसेंस (GNU General Public License) के तहत प्रकाशित:</target> <source>Many thanks for localization:</source> <target>स्थानीयकरण के लिए अनेक आभार:</target> @@ -1406,7 +1442,7 @@ This guarantees a consistent state even in case of a serious error. <target>जानकारी</target> <source>No log entries</source> -<target></target> +<target>कोई लॉग प्रविष्टियां नहीं</target> <source>Select all</source> <target>सभी चुने</target> @@ -1432,6 +1468,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>अवलोकन</target> +<source>Swap sides</source> +<target>पक्ष गमागम करें</target> + <source>Show "%x"</source> <target>"%x" दिखाएं</target> @@ -1553,7 +1592,7 @@ This guarantees a consistent state even in case of a serious error. <target>नहीं सहेजें (&n)</target> <source>Hide configuration</source> -<target>कॉन्फ़िगरेशन छुपाएँ</target> +<target>कॉन्फ़िगरेशन छुपाएं</target> <source>Highlight...</source> <target>हाइलाइट...</target> @@ -1606,9 +1645,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>फ़िल्टर किए या अस्थायी रूप से अपवर्जित फ़ाइल्स दिखाएं</target> -<source>Save as default</source> -<target>डिफ़ॉल्ट के रूप में सहेजें</target> - <source>Filter</source> <target>फ़िल्टर</target> @@ -1954,12 +1990,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>निर्देशिका %x के लिए रीसायकल बिन की जाँच विफल।</target> -<source>The following XML elements could not be read:</source> -<target>निम्न XML तत्वों को पढा नहीं जा सका:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>कॉन्फ़िगरेशन फ़ाइल %x अधूरी है। अनुपस्थित तत्व उनके डिफ़ॉल्ट मानों से सेट होंगे।</target> - <source>Prepare installation</source> <target>स्थापना तैयारी</target> diff --git a/FreeFileSync/Build/Languages/hungarian.lng b/FreeFileSync/Build/Languages/hungarian.lng index 5135b1d6..c22560bf 100755 --- a/FreeFileSync/Build/Languages/hungarian.lng +++ b/FreeFileSync/Build/Languages/hungarian.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Az elemek csak attribútumaikban különböznek</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>A %x nevet nem csak egy elem viseli a könyvtárban.</target> + <source>Resolving symbolic link %x</source> <target>%x szimbolikus hivatkozás feloldása</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Állományok időkülönbségének tűréshatára</target> -<source>Folder access timeout</source> -<target>Könyvtár hozzáférés időtúllépés</target> - <source>Run with background priority</source> <target>Futtatás háttér prioritással</target> @@ -299,7 +299,7 @@ <target>Mindkét oldal azonos</target> <source>Conflict/item cannot be categorized</source> -<target>Az ütközés vagy elem nem kategorizálható</target> +<target>Az ütközés/elem nem kategorizálható</target> <source>Copy new item to left</source> <target>Új elem másolása a bal oldalra</target> @@ -320,10 +320,10 @@ <target>Jobb oldali állomány mozgatása</target> <source>Update left item</source> -<target>Bal oldali elemek frissítése</target> +<target>Bal oldali elem frissítése</target> <source>Update right item</source> -<target>Jobb oldali elemek frissítése</target> +<target>Jobb oldali elem frissítése</target> <source>Do nothing</source> <target>Nincs mit végrehajtani</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Attribútumok frissítése a jobb oldalon</target> -<source>Warning</source> -<target>Figyelmeztetés</target> +<source>Errors:</source> +<target>Hiba:</target> + +<source>Warnings:</source> +<target>Figyelmeztetés:</target> <source>Items processed:</source> <target>Feldolgozott elemek száma:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Összes időszükséglet:</target> +<source>Warning</source> +<target>Figyelmeztetés</target> + <source>Stopped</source> <target>Leállítva</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Hiba történt a feldolgozás közben: %x állomány, %y sor, %z oszlop.</target> +<source>Services</source> +<target>Szervizek</target> + +<source>Show All</source> +<target>Összest Mutassa</target> + +<source>Hide Others</source> +<target>Rejtse a többit</target> + +<source>Hide %x</source> +<target>Rejt %x</target> + +<source>Quit %x</source> +<target>Leállít %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Nem lehet beállítani a könyvtárak lockolását a következő könyvtárakhoz:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>%x könyvtár nem olvasható.</target> -<source>/sec</source> -<target>/mp</target> +<source>%x/sec</source> +<target>%x/sec</target> -<source>%x items/sec</source> -<target>%x elem/mp</target> +<source>%x items</source> +<target>%x elem</target> <source>Show in Explorer</source> <target>Mutassa az Intézőben</target> @@ -392,7 +413,7 @@ <target>Sikeresen végrehajtva</target> <source>Completed with warnings</source> -<target>Figyelmeztetések mellett végrehajtva</target> +<target>Figyelmeztetéssel végrehajtva</target> <source>Completed with errors</source> <target>Hibák mellett végrehajtva</target> @@ -401,7 +422,7 @@ <target>Nem elérhető a Kötet Árnyék-másolat szolgáltatás.</target> <source>Please run the 64-bit version of FreeFileSync to create shadow copies on this system.</source> -<target>Árnyék másolatok készítéséhez ezen a rendszeren futtassa a FreeFileSync 64 bites verzióját.</target> +<target>Ezen a rendszeren árnyék másolatok készítéséhez futtassa a FreeFileSync 64 bites verzióját.</target> <source>Cannot determine volume name for %x.</source> <target>%x számára nem lehet a kötet-nevet meghatározni.</target> @@ -488,7 +509,7 @@ <target>Adja meg a célkönyvtárat a verziókövetéshez.</target> <source>The following items have unresolved conflicts and will not be synchronized:</source> -<target>A következő tételek feloldatlan ütközést tartalmaznak, így nem lesznek szinkronizálva:</target> +<target>A következő elemek feloldatlan ütközést tartalmaznak, így nem lesznek szinkronizálva:</target> <source>The following folders are significantly different. Please check that the correct folders are selected for synchronization.</source> <target>Az érintett könyvtárak lényegileg különböznek. Kérem ellenőrizze, a megfelelő könyvtárakat választotta-e ki szinkronizálásra.</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Adatbázis generálása...</target> -<source>Searching for excess file versions:</source> -<target>A felesleges fájl-verziók keresése:</target> +<source>Searching for old file versions:</source> +<target>Régi állomány-változatok keresése:</target> -<source>Removing excess file versions:</source> -<target>A felesleges fájl-verziók törlése:</target> +<source>Removing old file versions:</source> +<target>Régi állomány-változatok törlése:</target> <source>Unable to create time stamp for versioning:</source> <target>Nem képes időbélyegzés létrehozására a verzióképzéshez:</target> @@ -585,6 +606,24 @@ Tényleges: %y bájt <source>Unable to move %x to the recycle bin.</source> <target>%x nem helyezhető Lomtárba.</target> +<source>Unable to access %x.</source> +<target>%x nem elérhető.</target> + +<source>Authentication completed.</source> +<target>Hitelesítés befejezve.</target> + +<source>Authentication failed.</source> +<target>Hitelesítés sikertelen.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Most bezárhatja ezt az oldalt és folytathatja a FreeFileSync-et.</target> + +<source>The server returned an error:</source> +<target>A szerver hibát adott vissza:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>%x-en nem határozható meg a szabad tárterület.</target> + <source>Cannot find %x.</source> <target>%x nem található.</target> @@ -597,18 +636,12 @@ Tényleges: %y bájt <source>Cannot delete symbolic link %x.</source> <target>%x szimbolikus hivatkozás nem törölhető.</target> -<source>Cannot determine free disk space for %x.</source> -<target>%x-en nem határozható meg a szabad tárterület.</target> - <source>Incorrect command line:</source> <target>Hibás parancssor:</target> <source>The server does not support authentication via %x.</source> <target>A szerver nem támogatja a %x általi autentikációt.</target> -<source>Unable to access %x.</source> -<target>%x nem elérhető.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Tényleges: %y bájt <source>Failed to open SFTP channel number %x.</source> <target>Hiba a %x számú SFTP csatorna megnyitása során.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 bájt</pluralform> -<pluralform>%x bájt</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x kB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Húzd && Ejtsd</target> @@ -764,6 +779,24 @@ A parancs végrehajtódik, ha: <source>&Retry</source> <target>&Ismét</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 bájt</pluralform> +<pluralform>%x bájt</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x kB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Tölti...</target> @@ -846,7 +879,7 @@ A parancs végrehajtódik, ha: <target>Relatív elérési útvonal</target> <source>Item name</source> -<target>Tétel neve</target> +<target>Elem neve</target> <source>Size</source> <target>Méret</target> @@ -909,7 +942,7 @@ A parancs végrehajtódik, ha: <target>Mentse &kötegelt feladatként...</target> <source>Show &log</source> -<target></target> +<target>Mutassa a &logokat</target> <source>Start &comparison</source> <target>Kezdje az &összehasonlítást</target> @@ -971,9 +1004,6 @@ A parancs végrehajtódik, ha: <source>Access online storage</source> <target>Online tároló elérése</target> -<source>Swap sides</source> -<target>Cserélje fel az oldalakat</target> - <source>Close search bar</source> <target>Zárja be a keresési sávot</target> @@ -998,6 +1028,9 @@ A parancs végrehajtódik, ha: <source>View type:</source> <target>Nézet típusa:</target> +<source>Save as default</source> +<target>Mentse mint alapértelmezést</target> + <source>Select view:</source> <target>Válasszon nézetet:</target> @@ -1055,6 +1088,15 @@ A parancs végrehajtódik, ha: <source>Handle daylight saving time</source> <target>Kezelje a nyári időszámítás különbségét</target> +<source>Ignore errors</source> +<target>Hagyja figyelmen kívül a hibákat</target> + +<source>Retry count:</source> +<target>Visszatérések száma:</target> + +<source>Delay (in seconds):</source> +<target>Késés (másodpercben):</target> + <source>Performance improvements:</source> <target>Teljesítmény növelése:</target> @@ -1129,17 +1171,11 @@ A parancs végrehajtódik, ha: <source>Last x days:</source> <target>legutóbbi x nap:</target> -<source>Ignore errors</source> -<target>Hagyja figyelmen kívül a hibákat</target> - -<source>Retry count:</source> -<target>Visszatérések száma:</target> - -<source>Delay (in seconds):</source> -<target>Késés (másodpercben):</target> +<source>&Override default log path:</source> +<target>&Felülírja az alapértelmezett log útvonalat:</target> -<source>Run a command after synchronization:</source> -<target>Futtasson egy parancsot a szinkronizálás után:</target> +<source>Run a command:</source> +<target>Futtasson egy parancsot:</target> <source>OK</source> <target>OK</target> @@ -1189,6 +1225,9 @@ A parancs végrehajtódik, ha: <source>Directory on server:</source> <target>Könyvtár az alábbi szerveren:</target> +<source>Access timeout (in seconds):</source> +<target>Hozzáférési várakozási idő (másodpercben):</target> + <source>SFTP channels per connection:</source> <target>SFTP csatornák száma kapcsolatonként:</target> @@ -1223,7 +1262,7 @@ A parancs végrehajtódik, ha: <target>Bájt</target> <source>Items</source> -<target>Tétel</target> +<target>Elem</target> <source>Synchronizing...</source> <target>Szinkronizálás folyamatban...</target> @@ -1267,12 +1306,6 @@ A parancs végrehajtódik, ha: <source>Stop synchronization at first error</source> <target>Állítsa le a szinkronizálást az első hibánál</target> -<source>Save log:</source> -<target>Mentse a következő naplóállományt:</target> - -<source>Limit number of log files:</source> -<target>A log állományok számának korlátja:</target> - <source>How can I schedule a batch job?</source> <target>Hogyan tudok kötegelt feldolgozást ütemezni?</target> @@ -1309,8 +1342,11 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Mutassa újra az összes ideiglenesen rejtett párbeszédablakot és figyelmeztetést</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Alapértelmezett logolási útvonal:</target> + +<source>&Delete logs after x days:</source> +<target>&Törölje a logokat x nap után:</target> <source>Customize context menu:</source> <target>Környezeti menü testreszabása:</target> @@ -1321,8 +1357,8 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>&Default</source> <target>&Alapértelmezett</target> -<source>Feedback and suggestions are welcome</source> -<target>Várjuk a visszajelzéseket és az ötleteket</target> +<source>Feedback and suggestions are welcome:</source> +<target>Visszajelzést és javalatokat szívesen látjuk:</target> <source>Home page</source> <target>Honlap</target> @@ -1337,7 +1373,7 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <target>Ha szereted a FreeFileSync-et:</target> <source>Support with a donation</source> -<target>Támogassa adománnyal</target> +<target>Támogasd adománnyal</target> <source>The auto updater was disabled by the administrator.</source> <target>Az automatikus frissítést a rendszergazda kapcsolta ki.</target> @@ -1348,8 +1384,8 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Source code written in C++ using:</source> <target>A programot C++-ban fejlesztették a következők felhasználásával:</target> -<source>Published under the GNU General Public License</source> -<target>Közzétéve a GNU General Public License alatt</target> +<source>Published under the GNU General Public License:</source> +<target>Közzétéve GNU GPL alatt:</target> <source>Many thanks for localization:</source> <target>Köszönet a lokalizációért:</target> @@ -1406,7 +1442,7 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <target>Információ</target> <source>No log entries</source> -<target></target> +<target>Nincs log bejegyzés</target> <source>Select all</source> <target>Összeset kiválasztja</target> @@ -1432,6 +1468,9 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Overview</source> <target>Áttekintés</target> +<source>Swap sides</source> +<target>Cserélje fel az oldalakat</target> + <source>Show "%x"</source> <target>"%x" mutatása</target> @@ -1606,9 +1645,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Show filtered or temporarily excluded files</source> <target>Mutassa a szűrt vagy ideiglenesen kizárt állományokat</target> -<source>Save as default</source> -<target>Mentse mint alapértelmezést</target> - <source>Filter</source> <target>Szűrő</target> @@ -1954,12 +1990,6 @@ Ez garantálja a konzisztens állapotot egy komoly hiba esetén is. <source>Checking recycle bin failed for folder %x.</source> <target>%x könyvtár vonatkozásában a lomtár ellenőrzése meghiúsult.</target> -<source>The following XML elements could not be read:</source> -<target>A következő XML elemek nem olvashatók:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>%x konfigurációs állomány nem teljes. A hiányzó elemeket az alapértelmezés szerinti értékekkel helyettesíti.</target> - <source>Prepare installation</source> <target>Telepítés előkészítése</target> diff --git a/FreeFileSync/Build/Languages/italian.lng b/FreeFileSync/Build/Languages/italian.lng index 7064f93f..d5633191 100755 --- a/FreeFileSync/Build/Languages/italian.lng +++ b/FreeFileSync/Build/Languages/italian.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Gli oggetti differiscono solo negli attributi</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Il nome %x è utilizzato da più di un elemento nella cartella.</target> + <source>Resolving symbolic link %x</source> <target>Risoluzione collegamento %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>File tolleranza temporale</target> -<source>Folder access timeout</source> -<target>Timeout di accesso alle cartelle</target> - <source>Run with background priority</source> <target>Eseguire con priorità in background</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Aggiorna attributi a destra</target> -<source>Warning</source> -<target>Attenzione</target> +<source>Errors:</source> +<target>Errori:</target> + +<source>Warnings:</source> +<target>Avvertenze:</target> <source>Items processed:</source> <target>Oggetti processati:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Tempo totale:</target> +<source>Warning</source> +<target>Attenzione</target> + <source>Stopped</source> <target>Arrestato</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Errore nel parsing del file %x, riga %y, colonna %z.</target> +<source>Services</source> +<target>Servizi</target> + +<source>Show All</source> +<target>Mostra tutto</target> + +<source>Hide Others</source> +<target>Nascondi gli altri</target> + +<source>Hide %x</source> +<target>Nascondi %x</target> + +<source>Quit %x</source> +<target>Esci da %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Impossibile impostare i blocchi di directory per le seguenti cartelle:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Impossibile leggere la directory %x.</target> -<source>/sec</source> -<target>/sec</target> +<source>%x/sec</source> +<target>%x/ sec</target> -<source>%x items/sec</source> -<target>%x oggetti/sec</target> +<source>%x items</source> +<target>%x articoli</target> <source>Show in Explorer</source> <target>Mostra in Esplora Risorse</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Generazione database...</target> -<source>Searching for excess file versions:</source> -<target>Ricerca delle versioni di file in eccesso:</target> +<source>Searching for old file versions:</source> +<target>Ricerca di vecchie versioni di file:</target> -<source>Removing excess file versions:</source> -<target>Rimozione delle versioni di file in eccesso:</target> +<source>Removing old file versions:</source> +<target>Rimozione delle versioni precedenti dei file:</target> <source>Unable to create time stamp for versioning:</source> <target>Impossibile creare l'impronta per il controllo delle versioni:</target> @@ -585,6 +606,24 @@ Attuale: %y byte <source>Unable to move %x to the recycle bin.</source> <target>Impossibile spostare %x nel cestino.</target> +<source>Unable to access %x.</source> +<target>Impossibile accedere %x.</target> + +<source>Authentication completed.</source> +<target>Autenticazione completata.</target> + +<source>Authentication failed.</source> +<target>Autenticazione fallita.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Puoi chiudere questa pagina ora e continuare con FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Il server ha restituito un errore:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Impossibile determinare lo spazio libero su disco per %x.</target> + <source>Cannot find %x.</source> <target>Impossibile trovare %x.</target> @@ -597,18 +636,12 @@ Attuale: %y byte <source>Cannot delete symbolic link %x.</source> <target>Impossibile eliminare collegamento %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Impossibile determinare lo spazio libero su disco per %x.</target> - <source>Incorrect command line:</source> <target>Linea di comando non corretta:</target> <source>The server does not support authentication via %x.</source> <target>Il server non supporta l'autenticazione tramite %x.</target> -<source>Unable to access %x.</source> -<target>Impossibile accedere %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Attuale: %y byte <source>Failed to open SFTP channel number %x.</source> <target>Impossibile aprire canale SFTP numero %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x byte</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Trascina e rilascia</target> @@ -764,6 +779,24 @@ Il comando è attivato se: <source>&Retry</source> <target>&Riprova</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x byte</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Caricamento...</target> @@ -909,7 +942,7 @@ Il comando è attivato se: <target>Salva come &processo batch...</target> <source>Show &log</source> -<target></target> +<target>Mostrare &log</target> <source>Start &comparison</source> <target>Avvio &confronto</target> @@ -971,9 +1004,6 @@ Il comando è attivato se: <source>Access online storage</source> <target>Accesso all'archiviazione online</target> -<source>Swap sides</source> -<target>Inverti i lati</target> - <source>Close search bar</source> <target>Chiudi barra di ricerca</target> @@ -998,6 +1028,9 @@ Il comando è attivato se: <source>View type:</source> <target>Visualizza tipo:</target> +<source>Save as default</source> +<target>Salva come predefinito</target> + <source>Select view:</source> <target>Seleziona vista:</target> @@ -1055,6 +1088,15 @@ Il comando è attivato se: <source>Handle daylight saving time</source> <target>Maneggiare l'ora legale</target> +<source>Ignore errors</source> +<target>Ignora errori</target> + +<source>Retry count:</source> +<target>Riprova conteggio:</target> + +<source>Delay (in seconds):</source> +<target>Ritardo (in secondi):</target> + <source>Performance improvements:</source> <target>Miglioramenti delle prestazioni:</target> @@ -1129,17 +1171,11 @@ Il comando è attivato se: <source>Last x days:</source> <target>Ultimi x giorni:</target> -<source>Ignore errors</source> -<target>Ignora errori</target> - -<source>Retry count:</source> -<target>Riprova conteggio:</target> - -<source>Delay (in seconds):</source> -<target>Ritardo (in secondi):</target> +<source>&Override default log path:</source> +<target>&Sostituisci il percorso di registrazione predefinito:</target> -<source>Run a command after synchronization:</source> -<target>Esegui un comando dopo la sincronizzazione:</target> +<source>Run a command:</source> +<target>Esegui un comando:</target> <source>OK</source> <target>OK</target> @@ -1189,6 +1225,9 @@ Il comando è attivato se: <source>Directory on server:</source> <target>Directory su server:</target> +<source>Access timeout (in seconds):</source> +<target>Timeout di accesso (in secondi):</target> + <source>SFTP channels per connection:</source> <target>Canali SFTP per il collegamento:</target> @@ -1267,12 +1306,6 @@ Il comando è attivato se: <source>Stop synchronization at first error</source> <target>Interrompere la sincronizzazione al primo errore</target> -<source>Save log:</source> -<target>Salva log:</target> - -<source>Limit number of log files:</source> -<target>Limita il numero di file di registro:</target> - <source>How can I schedule a batch job?</source> <target>Come posso programmare un lavoro batch?</target> @@ -1309,8 +1342,11 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Mostra di nuovo tutti i dialoghi nascosti in modo permanente e i messaggi di allarme</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Percorso di log predefinito:</target> + +<source>&Delete logs after x days:</source> +<target>&Elimina i log dopo x giorni:</target> <source>Customize context menu:</source> <target>Personalizzare menu contestuale:</target> @@ -1321,8 +1357,8 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>&Default</source> <target>&Predefinito</target> -<source>Feedback and suggestions are welcome</source> -<target>Ogni commento o suggerimento è ben accetto</target> +<source>Feedback and suggestions are welcome:</source> +<target>Feedback e suggerimenti sono benvenuti:</target> <source>Home page</source> <target>Pagina Iniziale</target> @@ -1348,8 +1384,8 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Source code written in C++ using:</source> <target>Codice sorgente scritto in C++ utilizzando:</target> -<source>Published under the GNU General Public License</source> -<target>Pubblicato con licenza GNU General Public</target> +<source>Published under the GNU General Public License:</source> +<target>Pubblicato sotto la GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Ringraziamenti per la traduzione:</target> @@ -1406,7 +1442,7 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Nessuna voce di registro</target> <source>Select all</source> <target>Seleziona tutto</target> @@ -1432,6 +1468,9 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Overview</source> <target>Anteprima</target> +<source>Swap sides</source> +<target>Inverti i lati</target> + <source>Show "%x"</source> <target>Mostra "%x"</target> @@ -1606,9 +1645,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Show filtered or temporarily excluded files</source> <target>Mostra file filtrati o temporaneamente esclusi</target> -<source>Save as default</source> -<target>Salva come predefinito</target> - <source>Filter</source> <target>Filtro</target> @@ -1954,12 +1990,6 @@ Questo garantisce uno stato consistente anche in caso di errore grave. <source>Checking recycle bin failed for folder %x.</source> <target>Controllo cestino non riuscito per la cartella %x.</target> -<source>The following XML elements could not be read:</source> -<target>I seguenti elementi XML non possono essere letti:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>File di configurazione %x incompleta. Gli elementi mancanti saranno impostati sui valori predefiniti.</target> - <source>Prepare installation</source> <target>Preparare l'installazione</target> diff --git a/FreeFileSync/Build/Languages/japanese.lng b/FreeFileSync/Build/Languages/japanese.lng index c1ca09e4..388bc96b 100755 --- a/FreeFileSync/Build/Languages/japanese.lng +++ b/FreeFileSync/Build/Languages/japanese.lng @@ -110,7 +110,7 @@ <target>選択された構成を実行しないで編集用に開きます.</target> <source>Path to an alternate GlobalSettings.xml file.</source> -<target></target> +<target>代替 GlobalSettings.xml ファイルへのパス.</target> <source>Installation files are corrupted. Please reinstall FreeFileSync.</source> <target>インストール ファイルが破損しています、FreeFileSync を再インストールしてください.</target> @@ -159,6 +159,9 @@ <source>Items differ in attributes only</source> <target>属性のみ異なる項目</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>名前 %x は、フォルダ内複数の項目で使用されています.</target> + <source>Resolving symbolic link %x</source> <target>シンボリックリンク %x を解決中</target> @@ -186,9 +189,6 @@ <source>File time tolerance</source> <target>ファイル時間の許容範囲</target> -<source>Folder access timeout</source> -<target>フォルダ アクセスのタイムアウト</target> - <source>Run with background priority</source> <target>優先度バックグラウンドで実行</target> @@ -332,8 +332,11 @@ <source>Update attributes on right</source> <target>右の属性を更新</target> -<source>Warning</source> -<target>警告</target> +<source>Errors:</source> +<target>エラー:</target> + +<source>Warnings:</source> +<target>警告:</target> <source>Items processed:</source> <target>処理された要素:</target> @@ -344,6 +347,9 @@ <source>Total time:</source> <target>合計時間:</target> +<source>Warning</source> +<target>警告</target> + <source>Stopped</source> <target>停止</target> @@ -353,6 +359,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>ファイル %x の構文解析エラー, 行 %y, 列 %z.</target> +<source>Services</source> +<target>サービス</target> + +<source>Show All</source> +<target>すべて表示</target> + +<source>Hide Others</source> +<target>他を非表示</target> + +<source>Hide %x</source> +<target>%x を非表示</target> + +<source>Quit %x</source> +<target>%x を終了</target> + <source>Cannot set directory locks for the following folders:</source> <target>次のフォルダにあるディレクトリはロックできません:</target> @@ -367,11 +388,11 @@ <source>Cannot read directory %x.</source> <target>ディレクトリ %x を読み取れません.</target> -<source>/sec</source> -<target>/秒</target> +<source>%x/sec</source> +<target>%x/秒</target> -<source>%x items/sec</source> -<target>%x 項目/秒</target> +<source>%x items</source> +<target>%x 項目</target> <source>Show in Explorer</source> <target>エクスプローラで表示</target> @@ -520,11 +541,11 @@ <source>Generating database...</source> <target>データベースを作成中...</target> -<source>Searching for excess file versions:</source> -<target>ファイルの余剰なバージョンを検索:</target> +<source>Searching for old file versions:</source> +<target>古いファイルバージョンの検索:</target> -<source>Removing excess file versions:</source> -<target>ファイルの余剰なバージョンを除去:</target> +<source>Removing old file versions:</source> +<target>古いファイルバージョンの削除:</target> <source>Unable to create time stamp for versioning:</source> <target>バージョン管理のタイムスタンプを作成できません:</target> @@ -582,6 +603,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>%x をゴミ箱に移動できません.</target> +<source>Unable to access %x.</source> +<target>%x にアクセスできません.</target> + +<source>Authentication completed.</source> +<target>認証が完了しました.</target> + +<source>Authentication failed.</source> +<target>認証に失敗しました.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>このページを閉じて FreeFileSync. を続行することができます.</target> + +<source>The server returned an error:</source> +<target>サーバはエラーを返しました:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>%x の空きディスク領域を検出できません.</target> + <source>Cannot find %x.</source> <target>%x がみつかりません.</target> @@ -594,18 +633,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>シンボリック リンク %x を削除できません.</target> -<source>Cannot determine free disk space for %x.</source> -<target>%x の空きディスク領域を検出できません.</target> - <source>Incorrect command line:</source> <target>不正なコマンドライン:</target> <source>The server does not support authentication via %x.</source> <target>このサーバは %x による認証に対応していません.</target> -<source>Unable to access %x.</source> -<target>%x にアクセスできません.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -628,23 +661,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>SFTP チャンネル %x を開けません.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x バイト</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>ドラッグ && ドロップ</target> @@ -688,7 +704,7 @@ Actual: %y bytes <target>3. 'スタート'をクリック.</target> <source>To get started just import a "ffs_batch" file.</source> -<target></target> +<target>"ffs_batch" をインポート後、すぐに開始.</target> <source>Folders to watch:</source> <target>監視するフォルダ:</target> @@ -758,6 +774,23 @@ The command is triggered if: <source>&Retry</source> <target>再試行(&R)</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x バイト</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>読み込み中...</target> @@ -902,7 +935,7 @@ The command is triggered if: <target>一括ジョブで保存(&B)...</target> <source>Show &log</source> -<target></target> +<target>ログを表示(&L)</target> <source>Start &comparison</source> <target>比較を開始(&C)</target> @@ -964,9 +997,6 @@ The command is triggered if: <source>Access online storage</source> <target>オンライン ストレージにアクセス</target> -<source>Swap sides</source> -<target>パネルを入れ替え</target> - <source>Close search bar</source> <target>検索バーを閉じる</target> @@ -991,6 +1021,9 @@ The command is triggered if: <source>View type:</source> <target>表示形式:</target> +<source>Save as default</source> +<target>既定として保存</target> + <source>Select view:</source> <target>選択表示:</target> @@ -1048,6 +1081,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>夏時間の取り扱い</target> +<source>Ignore errors</source> +<target>エラーを無視</target> + +<source>Retry count:</source> +<target>再試行回数:</target> + +<source>Delay (in seconds):</source> +<target>遅延 (秒で指定):</target> + <source>Performance improvements:</source> <target>パフォーマンス向上:</target> @@ -1122,17 +1164,11 @@ The command is triggered if: <source>Last x days:</source> <target>x 日以降:</target> -<source>Ignore errors</source> -<target>エラーを無視</target> - -<source>Retry count:</source> -<target>再試行回数:</target> - -<source>Delay (in seconds):</source> -<target>遅延 (秒で指定):</target> +<source>&Override default log path:</source> +<target>既定のログ保存パスに上書き(&Q):</target> -<source>Run a command after synchronization:</source> -<target>同期処理後に実行するコマンド:</target> +<source>Run a command:</source> +<target>コマンドを実行:</target> <source>OK</source> <target>OK</target> @@ -1182,6 +1218,9 @@ The command is triggered if: <source>Directory on server:</source> <target>サーバ上のディレクトリ:</target> +<source>Access timeout (in seconds):</source> +<target>アクセスのタイムアウト(秒):</target> + <source>SFTP channels per connection:</source> <target>接続当たりの SFTP チャンネル数:</target> @@ -1260,12 +1299,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>最初のエラーで同期処理を停止</target> -<source>Save log:</source> -<target>ログ保存:</target> - -<source>Limit number of log files:</source> -<target>ログファイル数の制限:</target> - <source>How can I schedule a batch job?</source> <target>一括ジョブ スケジュールの作成方法</target> @@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>非表示にしたすべてのダイアログと警告メッセージを再表示</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>既定のログ保存パス:</target> + +<source>&Delete logs after x days:</source> +<target>x 日経過後にログを削除(&D):</target> <source>Customize context menu:</source> <target>コンテキストメニューのカスタマイズ:</target> @@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>デフォルト(&D)</target> -<source>Feedback and suggestions are welcome</source> -<target>フィードバック、提案などはこちらから</target> +<source>Feedback and suggestions are welcome:</source> +<target>フィードバック、提案はいつでも歓迎します:</target> <source>Home page</source> <target>ホーム ページ</target> @@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>ソースコードは C++ で書かれています:</target> -<source>Published under the GNU General Public License</source> -<target>GNU 一般共有使用許諾に基づき公開</target> +<source>Published under the GNU General Public License:</source> +<target>GNU 一般公衆ライセンスの下で公開されています:</target> <source>Many thanks for localization:</source> <target>ローカライズのご協力に感謝します:</target> @@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error. <target>情報</target> <source>No log entries</source> -<target></target> +<target>ログ エントリなし</target> <source>Select all</source> <target>すべて選択</target> @@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>概要</target> +<source>Swap sides</source> +<target>パネルを入れ替え</target> + <source>Show "%x"</source> <target>"%x" で表示</target> @@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>フィルター済、または一時除外ファイルを表示</target> -<source>Save as default</source> -<target>既定として保存</target> - <source>Filter</source> <target>フィルター</target> @@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>フォルダ %x のゴミ箱のチェックに失敗.</target> -<source>The following XML elements could not be read:</source> -<target>次の XML 要素は読み取ることができません:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>構成設定ファイル %x は不完全です。存在しない要素が既定値としてセットされています.</target> - <source>Prepare installation</source> <target>インストールの準備</target> diff --git a/FreeFileSync/Build/Languages/korean.lng b/FreeFileSync/Build/Languages/korean.lng index 811c7c51..91efd696 100755 --- a/FreeFileSync/Build/Languages/korean.lng +++ b/FreeFileSync/Build/Languages/korean.lng @@ -142,7 +142,7 @@ </target> <source>File %x has an invalid date.</source> -<target>파일 %x 의 날짜가 유효하지 않습니다.</target> +<target>파일 %x의 날짜가 유효하지 않습니다.</target> <source>Date:</source> <target>날짜:</target> @@ -159,6 +159,9 @@ <source>Items differ in attributes only</source> <target>항목들이 속성에서만 차이가 있습니다.</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>이름 %x이(가) 같은 폴더 내 한 개 이상의 항목에서 사용되고 있습니다.</target> + <source>Resolving symbolic link %x</source> <target>심볼릭 링크 %x 해결</target> @@ -186,9 +189,6 @@ <source>File time tolerance</source> <target>파일 시간 허용</target> -<source>Folder access timeout</source> -<target>폴더 접근 제한</target> - <source>Run with background priority</source> <target>배경 우선 순위로 실행</target> @@ -332,8 +332,11 @@ <source>Update attributes on right</source> <target>우측 속성 업데이트</target> -<source>Warning</source> -<target>경고</target> +<source>Errors:</source> +<target>오류:</target> + +<source>Warnings:</source> +<target>경고:</target> <source>Items processed:</source> <target>처리된 항목:</target> @@ -344,6 +347,9 @@ <source>Total time:</source> <target>전체 시간:</target> +<source>Warning</source> +<target>경고</target> + <source>Stopped</source> <target>중단</target> @@ -353,6 +359,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>분석 오류 - 파일: %x; 행: %y; 열: %z.</target> +<source>Services</source> +<target>서비스</target> + +<source>Show All</source> +<target>모두 보이기</target> + +<source>Hide Others</source> +<target>기타 다른 항목 숨기기</target> + +<source>Hide %x</source> +<target>%x 숨기기</target> + +<source>Quit %x</source> +<target>%x 끝내기</target> + <source>Cannot set directory locks for the following folders:</source> <target>다음 폴더의 디렉터리 잠금을 설정할 수 없습니다:</target> @@ -367,10 +388,10 @@ <source>Cannot read directory %x.</source> <target>디렉터리 %x을(를) 읽을 수 없습니다.</target> -<source>/sec</source> -<target>/초</target> +<source>%x/sec</source> +<target>%x/초</target> -<source>%x items/sec</source> +<source>%x items</source> <target>%x 항목</target> <source>Show in Explorer</source> @@ -416,7 +437,7 @@ <target>파일 크기</target> <source>Two way</source> -<target>양측 방향 (Two Way)</target> +<target>양측 방향 (투 웨이)</target> <source>Mirror</source> <target>미러</target> @@ -520,11 +541,11 @@ <source>Generating database...</source> <target>데이터베이스 생성 중...</target> -<source>Searching for excess file versions:</source> -<target>초과 파일 버전 검색 중:</target> +<source>Searching for old file versions:</source> +<target>이전 파일 버전 검색 중:</target> -<source>Removing excess file versions:</source> -<target>초과 파일 버전 제거 중:</target> +<source>Removing old file versions:</source> +<target>이전 파일 버전 제거 중:</target> <source>Unable to create time stamp for versioning:</source> <target>버전 관리를 위한 타임 스탬프 생성 불가:</target> @@ -582,6 +603,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>휴지통으로 %x을(를) 이동할 수 없습니다.</target> +<source>Unable to access %x.</source> +<target>%x에 접근할 수 없습니다.</target> + +<source>Authentication completed.</source> +<target>인증 완료.</target> + +<source>Authentication failed.</source> +<target>인증 실패.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>지금 이 페이지를 닫고 FreeFileSync를 계속 진행할 수 있습니다.</target> + +<source>The server returned an error:</source> +<target>서버가 오류를 반환했습니다:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>%x에 대한 사용 가능한 디스크 공간을 확인할 수 없습니다.</target> + <source>Cannot find %x.</source> <target>%x을(를) 찾을 수 없습니다.</target> @@ -594,18 +633,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>심볼릭 링크 %x을(를) 삭제할 수 없습니다.</target> -<source>Cannot determine free disk space for %x.</source> -<target>%x에 대한 사용 가능한 디스크 공간을 확인할 수 없습니다.</target> - <source>Incorrect command line:</source> <target>부정확한 명령줄:</target> <source>The server does not support authentication via %x.</source> <target>서버가 %x을(를) 통한 인증을 지원하지 않습니다.</target> -<source>Unable to access %x.</source> -<target>%x에 접근할 수 없습니다.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -628,23 +661,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>SFTP 채널번호 %x을(를) 열지 못 했습니다.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x 바이트</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>끌어서 놓기(&&) [드래그-앤-드랍]</target> @@ -758,6 +774,23 @@ The command is triggered if: <source>&Retry</source> <target>다시 시도(&R)</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x 바이트</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>로드 중...</target> @@ -902,7 +935,7 @@ The command is triggered if: <target>일괄 작업으로 저장(&b)...</target> <source>Show &log</source> -<target></target> +<target>로그 표시(&l)</target> <source>Start &comparison</source> <target>비교 시작(&C)</target> @@ -964,9 +997,6 @@ The command is triggered if: <source>Access online storage</source> <target>온라인 저장소 접속</target> -<source>Swap sides</source> -<target>양측 위치 바꾸기</target> - <source>Close search bar</source> <target>검색 창 닫기</target> @@ -991,6 +1021,9 @@ The command is triggered if: <source>View type:</source> <target>유형 보기:</target> +<source>Save as default</source> +<target>기본 값으로 저장</target> + <source>Select view:</source> <target>보기 선택:</target> @@ -1048,6 +1081,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>서머타임 설정</target> +<source>Ignore errors</source> +<target>오류 무시</target> + +<source>Retry count:</source> +<target>재시도 횟수:</target> + +<source>Delay (in seconds):</source> +<target>지연 (초 단위):</target> + <source>Performance improvements:</source> <target>성능 향상:</target> @@ -1122,17 +1164,11 @@ The command is triggered if: <source>Last x days:</source> <target>최근 x일:</target> -<source>Ignore errors</source> -<target>오류 무시</target> - -<source>Retry count:</source> -<target>재시도 횟수:</target> - -<source>Delay (in seconds):</source> -<target>지연 (초 단위):</target> +<source>&Override default log path:</source> +<target>기본 로그 경로 다시 정의(&O):</target> -<source>Run a command after synchronization:</source> -<target>동기화 이후 명령 실행:</target> +<source>Run a command:</source> +<target>명령 실행:</target> <source>OK</source> <target>확인</target> @@ -1182,6 +1218,9 @@ The command is triggered if: <source>Directory on server:</source> <target>서버 디렉터리:</target> +<source>Access timeout (in seconds):</source> +<target>액세스 제한 시간(초):</target> + <source>SFTP channels per connection:</source> <target>연결 당 SFTP 채널 수:</target> @@ -1260,12 +1299,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>첫 오류 발생 시 동기화 중지</target> -<source>Save log:</source> -<target>로그 저장:</target> - -<source>Limit number of log files:</source> -<target>로그 파일 개수 제한:</target> - <source>How can I schedule a batch job?</source> <target>일괄 작업 예약 방법은?</target> @@ -1302,8 +1335,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>영구적으로 숨겨진 모든 대화 상자 및 경고 메세지 다시 보이기</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>기본 로그 경로:</target> + +<source>&Delete logs after x days:</source> +<target>x일 후에 로그 삭제(&D):</target> <source>Customize context menu:</source> <target>컨텍스트 메뉴 커스터마이즈 (사용자 정의):</target> @@ -1314,8 +1350,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>기본 설정/값(&D)</target> -<source>Feedback and suggestions are welcome</source> -<target>모든 의견 및 건의/제안을 환영합니다</target> +<source>Feedback and suggestions are welcome:</source> +<target>모든 의견 및 제안을 환영합니다:</target> <source>Home page</source> <target>홈페이지</target> @@ -1341,8 +1377,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>소스코드는 C++ 언어로 아래 툴을 사용하여 작성되었습니다:</target> -<source>Published under the GNU General Public License</source> -<target>GNU 일반 공용 라이센스에 의한 출시</target> +<source>Published under the GNU General Public License:</source> +<target>GNU 일반 공용 라이센스에 의한 출시:</target> <source>Many thanks for localization:</source> <target>현지화 작업에 깊은 감사 드립니다:</target> @@ -1399,7 +1435,7 @@ This guarantees a consistent state even in case of a serious error. <target>정보</target> <source>No log entries</source> -<target></target> +<target>로그 항목 없음</target> <source>Select all</source> <target>모두 선택</target> @@ -1425,6 +1461,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>개요</target> +<source>Swap sides</source> +<target>양측 위치 바꾸기</target> + <source>Show "%x"</source> <target>"%x" 표시</target> @@ -1595,9 +1634,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>필터링 또는 일시적으로 제외된 파일 보이기</target> -<source>Save as default</source> -<target>기본 값으로 저장</target> - <source>Filter</source> <target>필터</target> @@ -1938,12 +1974,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>폴더 %x에 관한 휴지통 실패 확인 중.</target> -<source>The following XML elements could not be read:</source> -<target>다음 XML 요소를 읽을 수 없습니다:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>구성 파일 %x이(가) 불완전합니다. 누락된 요소는 기본 값으로 설정됩니다.</target> - <source>Prepare installation</source> <target>설치 준비</target> diff --git a/FreeFileSync/Build/Languages/lithuanian.lng b/FreeFileSync/Build/Languages/lithuanian.lng index efb1c91f..e7029e08 100755 --- a/FreeFileSync/Build/Languages/lithuanian.lng +++ b/FreeFileSync/Build/Languages/lithuanian.lng @@ -161,6 +161,9 @@ <source>Items differ in attributes only</source> <target>Elementai skiriasi tik atributais</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Šis pavadinimas %x yra naudojamas daugiau nei vieną kartą.</target> + <source>Resolving symbolic link %x</source> <target>Ieškoma simbolinės nuorodos %x</target> @@ -188,9 +191,6 @@ <source>File time tolerance</source> <target>Failo laiko nuokrypis</target> -<source>Folder access timeout</source> -<target>Pasibaigė skirtas laikas aplankui pasiekti</target> - <source>Run with background priority</source> <target>Vykdyti kaip aukštesnio prioriteto poprograme</target> @@ -336,8 +336,11 @@ <source>Update attributes on right</source> <target>Atnaujinti atributus dešinėje</target> -<source>Warning</source> -<target>Perspėjimas</target> +<source>Errors:</source> +<target>Klaidos:</target> + +<source>Warnings:</source> +<target>Įspėimai:</target> <source>Items processed:</source> <target>Elementų apdorota:</target> @@ -348,6 +351,9 @@ <source>Total time:</source> <target>Visas laikas:</target> +<source>Warning</source> +<target>Perspėjimas</target> + <source>Stopped</source> <target>Sustabdyta</target> @@ -357,6 +363,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Klaida trinant failą %x, eilė %y, stulpelis %z.</target> +<source>Services</source> +<target>Paslaugos</target> + +<source>Show All</source> +<target>Rodyti Visus</target> + +<source>Hide Others</source> +<target>Slėpti Kitus</target> + +<source>Hide %x</source> +<target>Slėpti %x</target> + +<source>Quit %x</source> +<target>Išeiti %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Negalima užrakinti šiuos aplankus kataloge:</target> @@ -373,11 +394,11 @@ <source>Cannot read directory %x.</source> <target>Negalima nuskaityti katalogo %x.</target> -<source>/sec</source> -<target>/sek.</target> +<source>%x/sec</source> +<target>%x/sek</target> -<source>%x items/sec</source> -<target>%x elementų/sek.</target> +<source>%x items</source> +<target>%x elementų</target> <source>Show in Explorer</source> <target>Rodyti naršyklėje</target> @@ -526,11 +547,11 @@ <source>Generating database...</source> <target>Sukuriama duomenų bazė...</target> -<source>Searching for excess file versions:</source> -<target>Perviršio failų versijos paieška:</target> +<source>Searching for old file versions:</source> +<target>Ieškoma senų failo versijų:</target> -<source>Removing excess file versions:</source> -<target>Perviršio failų versijos pašąlinimas:</target> +<source>Removing old file versions:</source> +<target>Pašalinamos senos failo versijos:</target> <source>Unable to create time stamp for versioning:</source> <target>Nepavyko sukurti versijos laiko žymą:</target> @@ -588,6 +609,24 @@ Esamas: %y baitai <source>Unable to move %x to the recycle bin.</source> <target>%x į šiukšliadėžę perkelti nepavyko.</target> +<source>Unable to access %x.</source> +<target>Nepavyko prisijungti %x.</target> + +<source>Authentication completed.</source> +<target>Autentifikavimas baigtas.</target> + +<source>Authentication failed.</source> +<target>Nepavyko autentifikuoti.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Jūs dabar galite uždaryti šį langą ir testi FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Serveris gražino klaidą:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Negalima nustatyti laisvos disko dalies %x.</target> + <source>Cannot find %x.</source> <target>Negalima surasti %x.</target> @@ -600,18 +639,12 @@ Esamas: %y baitai <source>Cannot delete symbolic link %x.</source> <target>Negalima ištrinti virtualios nuorodos %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Negalima nustatyti laisvos disko dalies %x.</target> - <source>Incorrect command line:</source> <target>Netaisyklinga Komandinė eilutė:</target> <source>The server does not support authentication via %x.</source> <target>Serveris nepalaiko %x autentifikavimo.</target> -<source>Unable to access %x.</source> -<target>Nepavyko prisijungti %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -638,25 +671,6 @@ Esamas: %y baitai <source>Failed to open SFTP channel number %x.</source> <target>Nepavyko atidaryti SFTP kanalo %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x baitas</pluralform> -<pluralform>%x baitai</pluralform> -<pluralform>%x baitų</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Vilkti && Numesti</target> @@ -770,6 +784,25 @@ Komanda inicijuojama jei: <source>&Retry</source> <target>&Bandyti vėl</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x baitas</pluralform> +<pluralform>%x baitai</pluralform> +<pluralform>%x baitų</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Įkraunama...</target> @@ -916,7 +949,7 @@ Komanda inicijuojama jei: <target>Išsaugoti kaip &užduočių paketą...</target> <source>Show &log</source> -<target></target> +<target>Rodyti &žurnalą</target> <source>Start &comparison</source> <target>Pradėti &palyginimą</target> @@ -978,9 +1011,6 @@ Komanda inicijuojama jei: <source>Access online storage</source> <target>Internetinės saugyklos prieiga</target> -<source>Swap sides</source> -<target>Sukeisti puses</target> - <source>Close search bar</source> <target>Uždaryti paieškos įrankinę</target> @@ -1005,6 +1035,9 @@ Komanda inicijuojama jei: <source>View type:</source> <target>Rodymo būdai:</target> +<source>Save as default</source> +<target>Išsaugoti kaip pagrindinį</target> + <source>Select view:</source> <target>Pasirinkti rodymo būdą:</target> @@ -1062,6 +1095,15 @@ Komanda inicijuojama jei: <source>Handle daylight saving time</source> <target>Naudoti vasaros laiką</target> +<source>Ignore errors</source> +<target>Ignoruoti klaidas</target> + +<source>Retry count:</source> +<target>Skaičiuoti is naujo:</target> + +<source>Delay (in seconds):</source> +<target>Uždelsimas (sekundėmis):</target> + <source>Performance improvements:</source> <target>Veiklos gerinimai:</target> @@ -1136,17 +1178,11 @@ Komanda inicijuojama jei: <source>Last x days:</source> <target>Paskutinių x dienų:</target> -<source>Ignore errors</source> -<target>Ignoruoti klaidas</target> - -<source>Retry count:</source> -<target>Skaičiuoti is naujo:</target> - -<source>Delay (in seconds):</source> -<target>Uždelsimas (sekundėmis):</target> +<source>&Override default log path:</source> +<target>&Pakeisti pradinę žurnalo nuorodą:</target> -<source>Run a command after synchronization:</source> -<target>Po sinchronizavimo vykdyti komandą:</target> +<source>Run a command:</source> +<target>Vykdyti komandą:</target> <source>OK</source> <target>Gerai</target> @@ -1196,6 +1232,9 @@ Komanda inicijuojama jei: <source>Directory on server:</source> <target>Katalogas serveryje:</target> +<source>Access timeout (in seconds):</source> +<target>Prieigos laikas pasibaigs (sekundės):</target> + <source>SFTP channels per connection:</source> <target>SFTP kanalai per jungtį:</target> @@ -1274,12 +1313,6 @@ Komanda inicijuojama jei: <source>Stop synchronization at first error</source> <target>Stabdyti suvienodinimą, pirmai klaidai įvykus</target> -<source>Save log:</source> -<target>Išsaugoti žurnalą:</target> - -<source>Limit number of log files:</source> -<target>Žurnalinių failų riba:</target> - <source>How can I schedule a batch job?</source> <target>Kaip aš galiu užduočių paketą įtraukti į tvarkaraštį?</target> @@ -1316,8 +1349,11 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Vėl rodyti visus paslėptus dialogo langus ir įspėjamuosius pranešimus</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Pradinė žurnalo nuoroda:</target> + +<source>&Delete logs after x days:</source> +<target>&Ištrinti žurnalo įrašus po x dienų:</target> <source>Customize context menu:</source> <target>Pagrindinio meniu pasirinkimai:</target> @@ -1328,8 +1364,8 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>&Default</source> <target>&Numatyta</target> -<source>Feedback and suggestions are welcome</source> -<target>Nuomonė ir patarimai laukiami</target> +<source>Feedback and suggestions are welcome:</source> +<target>Nuomonės ir pasiūlymai yra laukiami:</target> <source>Home page</source> <target>Pagrindinis puslapis</target> @@ -1355,8 +1391,8 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Source code written in C++ using:</source> <target>Šaltinio kodas parašytas su C++ naudojant:</target> -<source>Published under the GNU General Public License</source> -<target>Platinama su GNU General Public licenzija</target> +<source>Published under the GNU General Public License:</source> +<target>Publikuota naudojant GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Labai dėkojame už vertimą:</target> @@ -1413,7 +1449,7 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <target>Informacija</target> <source>No log entries</source> -<target></target> +<target>Nėra žurnalo įrašų</target> <source>Select all</source> <target>Pažymėti visus</target> @@ -1439,6 +1475,9 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Overview</source> <target>Apžvalga</target> +<source>Swap sides</source> +<target>Sukeisti puses</target> + <source>Show "%x"</source> <target>Rodyti "%x"</target> @@ -1617,9 +1656,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Show filtered or temporarily excluded files</source> <target>Rodyti išfiltruotus ar laikinai išskirtus failus</target> -<source>Save as default</source> -<target>Išsaugoti kaip pagrindinį</target> - <source>Filter</source> <target>Filtras</target> @@ -1970,12 +2006,6 @@ Tai garantuos pastovią buseną, netgi įvykus rimtai klaidai. <source>Checking recycle bin failed for folder %x.</source> <target>Tikrinama šiukšliadėžė. Failas %x nerastas.</target> -<source>The following XML elements could not be read:</source> -<target>Šie XML elementai yra neperskaitomi:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Konfiguracinis failas %x yra neužbaigtas. Trūkstamiems elementams bus priskirtos pradinės rekšmės.</target> - <source>Prepare installation</source> <target>Ruošiamasi įdiegimui</target> diff --git a/FreeFileSync/Build/Languages/norwegian.lng b/FreeFileSync/Build/Languages/norwegian.lng index 134e2215..97e97cce 100755 --- a/FreeFileSync/Build/Languages/norwegian.lng +++ b/FreeFileSync/Build/Languages/norwegian.lng @@ -7,15 +7,6 @@ <plural_definition>n == 1 ? 0 : 1</plural_definition> </header> -<source>No log entries</source> -<target></target> - -<source>Remove old log files after x days:</source> -<target></target> - -<source>Show &log</source> -<target></target> - <source>Both sides have changed since last synchronization.</source> <target>Begge sider er endret siden siste synkronisering.</target> @@ -169,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Elementene har kun forskjellige attributter</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Navnet %x brukes av mer enn ett element i mappen.</target> + <source>Resolving symbolic link %x</source> <target>Løser symbolsk forbindelse %x</target> @@ -196,9 +190,6 @@ <source>File time tolerance</source> <target>Filtid toleranse</target> -<source>Folder access timeout</source> -<target>Tidsavbrudd for mappetilgang</target> - <source>Run with background priority</source> <target>Kjør med bakgrunns-prioritet</target> @@ -343,8 +334,11 @@ <source>Update attributes on right</source> <target>Oppdater attributter til høyre</target> -<source>Warning</source> -<target>Advarsel</target> +<source>Errors:</source> +<target>Feil:</target> + +<source>Warnings:</source> +<target>Advarsler:</target> <source>Items processed:</source> <target>Elementer behandlet:</target> @@ -355,6 +349,9 @@ <source>Total time:</source> <target>Samlet tid:</target> +<source>Warning</source> +<target>Advarsel</target> + <source>Stopped</source> <target>Stoppet</target> @@ -364,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Behandlingsfeil i filen %x, rad %y, kolonne %z.</target> +<source>Services</source> +<target>Tjenester</target> + +<source>Show All</source> +<target>Vis alt</target> + +<source>Hide Others</source> +<target>Skjul andre</target> + +<source>Hide %x</source> +<target>Skjul %x</target> + +<source>Quit %x</source> +<target>Avslutt %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Kan ikke angi kataloglås for følgende mapper:</target> @@ -379,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Kan ikke lese katalogen %x.</target> -<source>/sec</source> -<target>/sek</target> +<source>%x/sec</source> +<target>%x/sek</target> -<source>%x items/sec</source> -<target>%x elementer/sek</target> +<source>%x items</source> +<target>%x elementer</target> <source>Show in Explorer</source> <target>Vis i Explorer</target> @@ -532,11 +544,11 @@ <source>Generating database...</source> <target>Lager database...</target> -<source>Searching for excess file versions:</source> -<target>Søker etter overskytende filversjoner:</target> +<source>Searching for old file versions:</source> +<target>Leter etter gamle filversjoner:</target> -<source>Removing excess file versions:</source> -<target>Fjerne overskytende filversjoner:</target> +<source>Removing old file versions:</source> +<target>Fjerner gamle filversjoner:</target> <source>Unable to create time stamp for versioning:</source> <target>Kan ikke opprette tidsstempel til versjonen:</target> @@ -594,6 +606,24 @@ Faktisk: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Kunne ikke flytte %x til papirkurven.</target> +<source>Unable to access %x.</source> +<target>Får ikke tilgang til %x.</target> + +<source>Authentication completed.</source> +<target>Autentisering er ferdig.</target> + +<source>Authentication failed.</source> +<target>Autentisering mislyktes.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Du kan lukke denne siden nå og fortsette med FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Serveren returnerte en feilmelding:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Kan ikke fastslå ledig diskplass for %x.</target> + <source>Cannot find %x.</source> <target>Kan ikke finne %x.</target> @@ -606,18 +636,12 @@ Faktisk: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Kan ikke slette symbolsk lenke %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Kan ikke fastslå ledig diskplass for %x.</target> - <source>Incorrect command line:</source> <target>Ugyldig kommando:</target> <source>The server does not support authentication via %x.</source> <target>Tjeneren støtter ikke autentisering via %x.</target> -<source>Unable to access %x.</source> -<target>Får ikke tilgang til %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -642,24 +666,6 @@ Faktisk: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Kunne ikke åpne SFTP kanalnummer %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x byte</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Dra && slipp</target> @@ -773,6 +779,24 @@ Kommandoen utføres hvis: <source>&Retry</source> <target>&Prøv igjen</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x byte</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Laster...</target> @@ -917,6 +941,9 @@ Kommandoen utføres hvis: <source>Save as &batch job...</source> <target>Lagre som &batchfil...</target> +<source>Show &log</source> +<target>Vis &logg</target> + <source>Start &comparison</source> <target>Start &sammenligning</target> @@ -977,9 +1004,6 @@ Kommandoen utføres hvis: <source>Access online storage</source> <target>Få tilgang til nettlagring</target> -<source>Swap sides</source> -<target>Bytt sider</target> - <source>Close search bar</source> <target>Lukk søkelinja</target> @@ -1004,6 +1028,9 @@ Kommandoen utføres hvis: <source>View type:</source> <target>Visning:</target> +<source>Save as default</source> +<target>Lagre som standard</target> + <source>Select view:</source> <target>Velg visning:</target> @@ -1061,6 +1088,15 @@ Kommandoen utføres hvis: <source>Handle daylight saving time</source> <target>Behandle sommertid</target> +<source>Ignore errors</source> +<target>Ignorér feil</target> + +<source>Retry count:</source> +<target>Antall forsøk:</target> + +<source>Delay (in seconds):</source> +<target>Forsinkelse (i sekunder):</target> + <source>Performance improvements:</source> <target>Ytelsesforbedringer:</target> @@ -1135,17 +1171,11 @@ Kommandoen utføres hvis: <source>Last x days:</source> <target>Siste x dager:</target> -<source>Ignore errors</source> -<target>Ignorér feil</target> - -<source>Retry count:</source> -<target>Antall forsøk:</target> +<source>&Override default log path:</source> +<target>&Velg en annen loggbane:</target> -<source>Delay (in seconds):</source> -<target>Forsinkelse (i sekunder):</target> - -<source>Run a command after synchronization:</source> -<target>Kjør en kommando etter synkronisering:</target> +<source>Run a command:</source> +<target>Kjør en kommando:</target> <source>OK</source> <target>OK</target> @@ -1195,6 +1225,9 @@ Kommandoen utføres hvis: <source>Directory on server:</source> <target>Katalog på server:</target> +<source>Access timeout (in seconds):</source> +<target>Få tilgang til tidsavbrudd (i sekunder):</target> + <source>SFTP channels per connection:</source> <target>SFTP kanaler per tilkobling:</target> @@ -1273,12 +1306,6 @@ Kommandoen utføres hvis: <source>Stop synchronization at first error</source> <target>Stopp synkronisering ved første feil</target> -<source>Save log:</source> -<target>Lagre logg:</target> - -<source>Limit number of log files:</source> -<target>Begrens antall loggfiler:</target> - <source>How can I schedule a batch job?</source> <target>Hvordan kan jeg lage en batchfil?</target> @@ -1315,6 +1342,12 @@ Sikrer prosessen ved alvorlige feil. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Vis alle skjulte vinduer og advarsler igjen</target> +<source>Default log path:</source> +<target>Standard loggbane:</target> + +<source>&Delete logs after x days:</source> +<target>&Slett logger etter x dager:</target> + <source>Customize context menu:</source> <target>Tilpass kontekst-meny:</target> @@ -1324,8 +1357,8 @@ Sikrer prosessen ved alvorlige feil. <source>&Default</source> <target>&Standard</target> -<source>Feedback and suggestions are welcome</source> -<target>Tilbakemeldinger og forslag er ønsket</target> +<source>Feedback and suggestions are welcome:</source> +<target>Tilbakemelding og forslag er velkomne:</target> <source>Home page</source> <target>Hjemmeside</target> @@ -1351,8 +1384,8 @@ Sikrer prosessen ved alvorlige feil. <source>Source code written in C++ using:</source> <target>Kildekoden er skrevet i C++ med hjelp fra:</target> -<source>Published under the GNU General Public License</source> -<target>Utgitt under GNU General Public Licence</target> +<source>Published under the GNU General Public License:</source> +<target>Publisert under GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Takk for oversettelse:</target> @@ -1408,6 +1441,9 @@ Sikrer prosessen ved alvorlige feil. <source>Info</source> <target>Info</target> +<source>No log entries</source> +<target>Ingen loggfiler</target> + <source>Select all</source> <target>Velg alt</target> @@ -1432,6 +1468,9 @@ Sikrer prosessen ved alvorlige feil. <source>Overview</source> <target>Oversikt</target> +<source>Swap sides</source> +<target>Bytt sider</target> + <source>Show "%x"</source> <target>Vis "%x"</target> @@ -1606,9 +1645,6 @@ Sikrer prosessen ved alvorlige feil. <source>Show filtered or temporarily excluded files</source> <target>Vis filtrerte eller midlertidig ekskluderte filer</target> -<source>Save as default</source> -<target>Lagre som standard</target> - <source>Filter</source> <target>Filter</target> @@ -1954,12 +1990,6 @@ Sikrer prosessen ved alvorlige feil. <source>Checking recycle bin failed for folder %x.</source> <target>Det å sjekke papirkurven gikk galt for mappen %x.</target> -<source>The following XML elements could not be read:</source> -<target>De følgende XML-elementer kunne ikke leses:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Konfigurasjonsfil %x er ufullstendig. De manglende elementene vil bli satt til standardverdiene.</target> - <source>Prepare installation</source> <target>Forbered installering</target> diff --git a/FreeFileSync/Build/Languages/polish.lng b/FreeFileSync/Build/Languages/polish.lng index e3dd61a9..30a67068 100755 --- a/FreeFileSync/Build/Languages/polish.lng +++ b/FreeFileSync/Build/Languages/polish.lng @@ -161,6 +161,9 @@ <source>Items differ in attributes only</source> <target>Elementy różnią się wyłącznie atrybutami</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Nazwa %x jest używana przez więcej niż jeden element w folderze.</target> + <source>Resolving symbolic link %x</source> <target>Rozwiązywanie dowiązania symbolicznego %x</target> @@ -188,9 +191,6 @@ <source>File time tolerance</source> <target>Tolerancja czasu dla pliku</target> -<source>Folder access timeout</source> -<target>Limit czasu odpowiedzi podczas dostępu do katalogu</target> - <source>Run with background priority</source> <target>Uruchom w tle z priorytetem</target> @@ -336,8 +336,11 @@ <source>Update attributes on right</source> <target>Aktualizuj atrybuty po prawej stronie</target> -<source>Warning</source> -<target>Ostrzeżenie</target> +<source>Errors:</source> +<target>Błędy:</target> + +<source>Warnings:</source> +<target>Ostrzeżenia:</target> <source>Items processed:</source> <target>Przetworzone elementy:</target> @@ -348,6 +351,9 @@ <source>Total time:</source> <target>Całkowity czas:</target> +<source>Warning</source> +<target>Ostrzeżenie</target> + <source>Stopped</source> <target>Zatrzymana</target> @@ -357,6 +363,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Błąd podczas parsowania pliku %x, rząd %y, kolumna %z.</target> +<source>Services</source> +<target>Usługi</target> + +<source>Show All</source> +<target>Pokaż Wszystko</target> + +<source>Hide Others</source> +<target>Ukryj Inne</target> + +<source>Hide %x</source> +<target>Ukryj %x</target> + +<source>Quit %x</source> +<target>Zamknij %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Nie można zablokować katalogów dla poniższych folderów:</target> @@ -373,11 +394,11 @@ <source>Cannot read directory %x.</source> <target>Nie można odczytać katalogu %x.</target> -<source>/sec</source> -<target>/sekundę</target> +<source>%x/sec</source> +<target>%x/sek</target> -<source>%x items/sec</source> -<target>%x elementów/sek</target> +<source>%x items</source> +<target>%x elementów</target> <source>Show in Explorer</source> <target>Wyświetl w Eksploratorze</target> @@ -526,11 +547,11 @@ <source>Generating database...</source> <target>Generowanie bazy danych...</target> -<source>Searching for excess file versions:</source> -<target>Wyszukiwanie nadmiarowych wersji plików:</target> +<source>Searching for old file versions:</source> +<target>Wyszukiwanie starych wersji plików:</target> -<source>Removing excess file versions:</source> -<target>Usuwanie nadmiarowych wersji plików:</target> +<source>Removing old file versions:</source> +<target>Usuwanie starych wersji plików:</target> <source>Unable to create time stamp for versioning:</source> <target>Nie można utworzyć znacznika czasu dla wersjonowania:</target> @@ -588,6 +609,24 @@ Przesłany: %y bajtów <source>Unable to move %x to the recycle bin.</source> <target>Nie można przenieść %x do kosza.</target> +<source>Unable to access %x.</source> +<target>Brak dostępu do %x.</target> + +<source>Authentication completed.</source> +<target>Uwierzytelnianie zakończone.</target> + +<source>Authentication failed.</source> +<target>Uwierzytelnianie nie powiodło się.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Możesz teraz zamknąć tę stronę i kontynuować FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Serwer zwrócił błąd:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Nie można określić wolnego miejsca na %x.</target> + <source>Cannot find %x.</source> <target>Nie można odnaleźć %x.</target> @@ -600,18 +639,12 @@ Przesłany: %y bajtów <source>Cannot delete symbolic link %x.</source> <target>Nie można usunąć dowiązania symbolicznego %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Nie można określić wolnego miejsca na %x.</target> - <source>Incorrect command line:</source> <target>Niepoprawne polecenie:</target> <source>The server does not support authentication via %x.</source> <target>Serwer nie wspiera uwierzytelniania przez %x.</target> -<source>Unable to access %x.</source> -<target>Brak dostępu do %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -638,25 +671,6 @@ Przesłany: %y bajtów <source>Failed to open SFTP channel number %x.</source> <target>Nie można otworzyć kanału SFTP numer %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 bajt</pluralform> -<pluralform>%x bajty</pluralform> -<pluralform>%x bajtów</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Drag && Drop</target> @@ -770,6 +784,25 @@ Komenda jest wykonywana gdy: <source>&Retry</source> <target>&Powtórz</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 bajt</pluralform> +<pluralform>%x bajty</pluralform> +<pluralform>%x bajtów</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Wczytywanie...</target> @@ -916,7 +949,7 @@ Komenda jest wykonywana gdy: <target>Zapisz w trybie &wsadowym...</target> <source>Show &log</source> -<target></target> +<target>Pokaż &log</target> <source>Start &comparison</source> <target>Rozpo&cznij porównywanie</target> @@ -978,9 +1011,6 @@ Komenda jest wykonywana gdy: <source>Access online storage</source> <target>Dostęp do zasobu zdalnego</target> -<source>Swap sides</source> -<target>Zamień stronami</target> - <source>Close search bar</source> <target>Zamknij pasek wyszukiwania</target> @@ -1005,6 +1035,9 @@ Komenda jest wykonywana gdy: <source>View type:</source> <target>Typ widoku:</target> +<source>Save as default</source> +<target>Zapisz jako domyślne</target> + <source>Select view:</source> <target>Widok:</target> @@ -1062,6 +1095,15 @@ Komenda jest wykonywana gdy: <source>Handle daylight saving time</source> <target>Uwzględniaj przesunięcie czasu</target> +<source>Ignore errors</source> +<target>Ignoruj błędy</target> + +<source>Retry count:</source> +<target>Liczba prób:</target> + +<source>Delay (in seconds):</source> +<target>Opóźnienie (w sekundach):</target> + <source>Performance improvements:</source> <target>Polepszenie wydajności:</target> @@ -1136,17 +1178,11 @@ Komenda jest wykonywana gdy: <source>Last x days:</source> <target>Ostatnie x dni:</target> -<source>Ignore errors</source> -<target>Ignoruj błędy</target> - -<source>Retry count:</source> -<target>Liczba prób:</target> - -<source>Delay (in seconds):</source> -<target>Opóźnienie (w sekundach):</target> +<source>&Override default log path:</source> +<target>&Zastąp domyślną ścieżkę logu:</target> -<source>Run a command after synchronization:</source> -<target>Uruchom komendę po zakończonej synchronizacji:</target> +<source>Run a command:</source> +<target>Uruchom polecenie:</target> <source>OK</source> <target>OK</target> @@ -1196,6 +1232,9 @@ Komenda jest wykonywana gdy: <source>Directory on server:</source> <target>Katalog na serwerze:</target> +<source>Access timeout (in seconds):</source> +<target>Czas oczekiwania na dostęp (w sekundach):</target> + <source>SFTP channels per connection:</source> <target>Liczba kanałów dla połączenia SFTP:</target> @@ -1274,12 +1313,6 @@ Komenda jest wykonywana gdy: <source>Stop synchronization at first error</source> <target>Przerwij synchronizację przy pierwszym błędzie</target> -<source>Save log:</source> -<target>Zapisz logi:</target> - -<source>Limit number of log files:</source> -<target>Ogranicz liczbę plików logów:</target> - <source>How can I schedule a batch job?</source> <target>Jak zaplanować zadanie w trybie wsadowym?</target> @@ -1316,8 +1349,11 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Show all permanently hidden dialogs and warning messages again</source> <target>Przywróć wszystkie, stale ukryte dialogi i powiadomienia</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Domyślna ścieżka logu:</target> + +<source>&Delete logs after x days:</source> +<target>&Skasuj logi po x dniach:</target> <source>Customize context menu:</source> <target>Dostosuj menu kontekstowe:</target> @@ -1328,11 +1364,11 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>&Default</source> <target>&Domyślne</target> -<source>Feedback and suggestions are welcome</source> -<target>Wszelkie opinie i sugestie mile widziane</target> +<source>Feedback and suggestions are welcome:</source> +<target>Opinie i sugestie mile widziane:</target> <source>Home page</source> -<target>Strona domowa:</target> +<target>Strona domowa</target> <source>FreeFileSync Forum</source> <target>Forum FreeFileSync</target> @@ -1355,8 +1391,8 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Source code written in C++ using:</source> <target>Kod stworzony w C++ z wykorzystaniem:</target> -<source>Published under the GNU General Public License</source> -<target>Udostępnione na zasadach licencji GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Opublikowano na licencji GNU General Public Licence:</target> <source>Many thanks for localization:</source> <target>Podziękowania za tłumaczenia:</target> @@ -1413,7 +1449,7 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <target>Info</target> <source>No log entries</source> -<target></target> +<target>Brak wpisów w dzienniku</target> <source>Select all</source> <target>Zaznacz wszystko</target> @@ -1439,6 +1475,9 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Overview</source> <target>Przegląd</target> +<source>Swap sides</source> +<target>Zamień stronami</target> + <source>Show "%x"</source> <target>Pokaż "%x"</target> @@ -1617,9 +1656,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Show filtered or temporarily excluded files</source> <target>Pokaż pliki wyfiltrowane lub wykluczone tymczasowo</target> -<source>Save as default</source> -<target>Zapisz jako domyślne</target> - <source>Filter</source> <target>Filtr</target> @@ -1970,12 +2006,6 @@ program kopiuje zawartość do pliku tymczasowego (*.ffs_tmp), a następnie nadp <source>Checking recycle bin failed for folder %x.</source> <target>Sprawdzanie kosza systemowego dla katalogu %x zakończone niepowodzeniem.</target> -<source>The following XML elements could not be read:</source> -<target>Poniższy element XML nie może zostać odczytany:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Plik konfiguracyjny %x jest niekompletny. Zostaną ustawione domyślne wartości dla brakujących elementów.</target> - <source>Prepare installation</source> <target>Przygotuj instalację</target> diff --git a/FreeFileSync/Build/Languages/portuguese.lng b/FreeFileSync/Build/Languages/portuguese.lng index 83c0ad21..b713f6f1 100755 --- a/FreeFileSync/Build/Languages/portuguese.lng +++ b/FreeFileSync/Build/Languages/portuguese.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Itens diferem apenas nos atributos</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>O nome %x é usado por mais de um item na pasta.</target> + <source>Resolving symbolic link %x</source> <target>Resolver link simbólico %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Tolerância de tempo de ficheiro</target> -<source>Folder access timeout</source> -<target>Tempo limite de acesso à pasta</target> - <source>Run with background priority</source> <target>Correr com prioridade de fundo</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Actualizar atributos à direita</target> -<source>Warning</source> -<target>Atenção</target> +<source>Errors:</source> +<target>Erros:</target> + +<source>Warnings:</source> +<target>Avisos:</target> <source>Items processed:</source> <target>Elementos processados:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Tempo total:</target> +<source>Warning</source> +<target>Atenção</target> + <source>Stopped</source> <target>Parado</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Erro ao analisar ficheiro %x, linha %y, coluna %z.</target> +<source>Services</source> +<target>Serviços</target> + +<source>Show All</source> +<target>Mostrar Todos</target> + +<source>Hide Others</source> +<target>Ocultar Outros</target> + +<source>Hide %x</source> +<target>Ocultar %x</target> + +<source>Quit %x</source> +<target>Cerrar o %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Incapaz de definir bloqueios de directórios para as seguintes pastas:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Não é possível ler o directório %x.</target> -<source>/sec</source> -<target>/seg</target> +<source>%x/sec</source> +<target>%x/seg.</target> -<source>%x items/sec</source> -<target>%x itens/seg</target> +<source>%x items</source> +<target>%x itens</target> <source>Show in Explorer</source> <target>Mostrar no Explorer</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>A gerar base de dados...</target> -<source>Searching for excess file versions:</source> -<target>A pesquisar por versões em excesso de ficheiro:</target> +<source>Searching for old file versions:</source> +<target>A pesquisar por versões antigas do ficheiro:</target> -<source>Removing excess file versions:</source> -<target>A remover versões em excesso de ficheiro:</target> +<source>Removing old file versions:</source> +<target>A remover versões antigas do ficheiro:</target> <source>Unable to create time stamp for versioning:</source> <target>Não é possível criar data/hora para controlo de versões:</target> @@ -585,6 +606,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Não é possível mover %x para a reciclagem.</target> +<source>Unable to access %x.</source> +<target>Incapaz de aceder %x.</target> + +<source>Authentication completed.</source> +<target>Autenticação completada.</target> + +<source>Authentication failed.</source> +<target>Falha na autenticação.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Você pode cerrar esta página agora e continuar com o FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>O servidor retornou um erro:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Não é possível determinar o espaço livre no disco %x.</target> + <source>Cannot find %x.</source> <target>Não é possível encontrar %x.</target> @@ -597,18 +636,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Não é possível remover o link simbólico %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Não é possível determinar o espaço livre no disco %x.</target> - <source>Incorrect command line:</source> <target>Linha de comandos incorrecta:</target> <source>The server does not support authentication via %x.</source> <target>O servidor não suporta autenticação via %x.</target> -<source>Unable to access %x.</source> -<target>Incapaz de aceder %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Falha ao abrir canal SFTP número %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Arrastar && Largar</target> @@ -764,6 +779,24 @@ O comando é executado se: <source>&Retry</source> <target>&Tentar de Novo</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>A carregar...</target> @@ -909,7 +942,7 @@ O comando é executado se: <target>Guardar como &batch...</target> <source>Show &log</source> -<target></target> +<target>Mostrar ®isto</target> <source>Start &comparison</source> <target>Iniciar &comparação</target> @@ -971,9 +1004,6 @@ O comando é executado se: <source>Access online storage</source> <target>Aceder armazenagem local</target> -<source>Swap sides</source> -<target>Trocar lados</target> - <source>Close search bar</source> <target>Fechar barra de pesquisa</target> @@ -998,6 +1028,9 @@ O comando é executado se: <source>View type:</source> <target>Tipo de vista:</target> +<source>Save as default</source> +<target>Guardar como padrão</target> + <source>Select view:</source> <target>Seleccionar vista:</target> @@ -1055,6 +1088,15 @@ O comando é executado se: <source>Handle daylight saving time</source> <target>Lidar com horário de verão</target> +<source>Ignore errors</source> +<target>Ignorar erros</target> + +<source>Retry count:</source> +<target>Nº de tentativas:</target> + +<source>Delay (in seconds):</source> +<target>Atraso (em segundos):</target> + <source>Performance improvements:</source> <target>Melhorias de desempenho:</target> @@ -1129,17 +1171,11 @@ O comando é executado se: <source>Last x days:</source> <target>Últimos x dias:</target> -<source>Ignore errors</source> -<target>Ignorar erros</target> - -<source>Retry count:</source> -<target>Nº de tentativas:</target> - -<source>Delay (in seconds):</source> -<target>Atraso (em segundos):</target> +<source>&Override default log path:</source> +<target>&Substituir caminho padrão do registo:</target> -<source>Run a command after synchronization:</source> -<target>Executar um comando após a sincronização:</target> +<source>Run a command:</source> +<target>Executar um comando:</target> <source>OK</source> <target>OK</target> @@ -1189,6 +1225,9 @@ O comando é executado se: <source>Directory on server:</source> <target>Directório no servidor:</target> +<source>Access timeout (in seconds):</source> +<target>Tempo limite de acesso (em segundos):</target> + <source>SFTP channels per connection:</source> <target>Canais SFTP por conexão:</target> @@ -1267,12 +1306,6 @@ O comando é executado se: <source>Stop synchronization at first error</source> <target>Para sincronização ao primeiro erro</target> -<source>Save log:</source> -<target>Guardar registo:</target> - -<source>Limit number of log files:</source> -<target>Limitar número de ficheiros de registo:</target> - <source>How can I schedule a batch job?</source> <target>Como posso agendar um trabalho batch?</target> @@ -1309,8 +1342,11 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Mostrar todos os diálogos escondidos permanentemente e mensagens de aviso novamente</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Caminho padrão do registo:</target> + +<source>&Delete logs after x days:</source> +<target>&Eliminar registos após X dias:</target> <source>Customize context menu:</source> <target>Personalizar menu de contexto:</target> @@ -1321,8 +1357,8 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>&Default</source> <target>&Config. Iniciais</target> -<source>Feedback and suggestions are welcome</source> -<target>Comentários e sugestões são apreciados</target> +<source>Feedback and suggestions are welcome:</source> +<target>Comentários e sugestões são bem-vindos:</target> <source>Home page</source> <target>Sítio da web</target> @@ -1348,8 +1384,8 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Source code written in C++ using:</source> <target>Código fonte escrito em C++ utilizando:</target> -<source>Published under the GNU General Public License</source> -<target>Publicado sobre GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Publicado sob a Licença Pública Geral GNU:</target> <source>Many thanks for localization:</source> <target>Muito obrigado pela localização:</target> @@ -1406,7 +1442,7 @@ Isto garante um estado consistente mesmo em caso de falha grave. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Nenhuma entrada no registo</target> <source>Select all</source> <target>Seleccionar tudo</target> @@ -1432,6 +1468,9 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Overview</source> <target>Vista</target> +<source>Swap sides</source> +<target>Trocar lados</target> + <source>Show "%x"</source> <target>Mostrar "%x"</target> @@ -1606,9 +1645,6 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Show filtered or temporarily excluded files</source> <target>Mostrar ficheiros filtrados ou temporariamente excluídos</target> -<source>Save as default</source> -<target>Guardar como padrão</target> - <source>Filter</source> <target>Filtro</target> @@ -1954,12 +1990,6 @@ Isto garante um estado consistente mesmo em caso de falha grave. <source>Checking recycle bin failed for folder %x.</source> <target>Verificar a reciclagem falhou no directório %x.</target> -<source>The following XML elements could not be read:</source> -<target>Os seguintes elementos XML não puderam ser lidos:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>O ficheiro de instalação %x está incompleto. Os elementos em falta serão definidos para seus valores iniciais.</target> - <source>Prepare installation</source> <target>A preparar a instalação</target> diff --git a/FreeFileSync/Build/Languages/portuguese_br.lng b/FreeFileSync/Build/Languages/portuguese_br.lng index 9b336566..43c0904c 100755 --- a/FreeFileSync/Build/Languages/portuguese_br.lng +++ b/FreeFileSync/Build/Languages/portuguese_br.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Os itens diferem apenas nos atributos</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>O nome %x é usado por mais de um item na pasta.</target> + <source>Resolving symbolic link %x</source> <target>Resolvendo link simbólico %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Tolerância de tempo do arquivo</target> -<source>Folder access timeout</source> -<target>Tempo limite de acesso da pasta</target> - <source>Run with background priority</source> <target>Executar com prioridade de segundo plano</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Atualizar atributos à direita</target> -<source>Warning</source> -<target>Aviso</target> +<source>Errors:</source> +<target>Erros:</target> + +<source>Warnings:</source> +<target>Avisos:</target> <source>Items processed:</source> <target>Elementos processados:</target> @@ -346,8 +349,11 @@ <source>Total time:</source> <target>Tempo total:</target> +<source>Warning</source> +<target>Aviso</target> + <source>Stopped</source> -<target>Interrompido</target> +<target>Parado</target> <source>Cleaning up log files:</source> <target>Limpando arquivos de log:</target> @@ -355,8 +361,23 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Erro analisando o arquivo %x, linha %y, coluna %z.</target> +<source>Services</source> +<target>Serviços</target> + +<source>Show All</source> +<target>Mostrar Todos</target> + +<source>Hide Others</source> +<target>Ocultar Outros</target> + +<source>Hide %x</source> +<target>Ocultar %x</target> + +<source>Quit %x</source> +<target>Sair %x</target> + <source>Cannot set directory locks for the following folders:</source> -<target>Não é possível estabelecer bloqueio de diretório para as seguintes pastas:</target> +<target>Não é possível definir bloqueios de diretório para as seguintes pastas:</target> <source> <pluralform>1 thread</pluralform> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Não é possível ler o diretório %x.</target> -<source>/sec</source> -<target>/s</target> +<source>%x/sec</source> +<target>%x/s</target> -<source>%x items/sec</source> -<target>%x itens/s</target> +<source>%x items</source> +<target>%x itens</target> <source>Show in Explorer</source> <target>Mostrar no Explorer</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Gerando banco de dados...</target> -<source>Searching for excess file versions:</source> -<target>Procurando por versões de arquivos em excesso:</target> +<source>Searching for old file versions:</source> +<target>Procurando por versões antigas de arquivos:</target> -<source>Removing excess file versions:</source> -<target>Removendo versões de arquivos em excesso:</target> +<source>Removing old file versions:</source> +<target>Removendo versões antigas de arquivos:</target> <source>Unable to create time stamp for versioning:</source> <target>Não é possível criar a estampa de tempo para o controle de versões:</target> @@ -585,6 +606,24 @@ Atual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Não é possível mover %x para a Lixeira.</target> +<source>Unable to access %x.</source> +<target>Não foi possível acessar %x.</target> + +<source>Authentication completed.</source> +<target>Autenticação concluída.</target> + +<source>Authentication failed.</source> +<target>Autenticação falhou.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Você pode fechar esta página agora e continuar com o FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>O servidor retornou um erro:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Não é possível determinar o espaço livre em disco para %x.</target> + <source>Cannot find %x.</source> <target>Não é possível encontrar %x.</target> @@ -597,18 +636,12 @@ Atual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Não é possível excluir o link simbólico %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Não é possível determinar o espaço livre em disco para %x.</target> - <source>Incorrect command line:</source> <target>Linha de comando incorreta:</target> <source>The server does not support authentication via %x.</source> <target>O servidor não suporta autenticação via %x.</target> -<source>Unable to access %x.</source> -<target>Não foi possível acessar %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Atual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Falha ao abrir o canal SFTP número %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x kB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Arrastar && Soltar</target> @@ -764,6 +779,24 @@ O comando é disparado se: <source>&Retry</source> <target>&Tentar Novamente</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x kB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Carregando...</target> @@ -909,7 +942,7 @@ O comando é disparado se: <target>Salvar como &tarefa em lotes...</target> <source>Show &log</source> -<target></target> +<target>Mostrar &log</target> <source>Start &comparison</source> <target>Iniciar &Comparação</target> @@ -971,9 +1004,6 @@ O comando é disparado se: <source>Access online storage</source> <target>Acessar armazenamento online</target> -<source>Swap sides</source> -<target>Trocar lados</target> - <source>Close search bar</source> <target>Fechar barra de localização</target> @@ -998,6 +1028,9 @@ O comando é disparado se: <source>View type:</source> <target>Tipo de visualização:</target> +<source>Save as default</source> +<target>Salvar como padrão</target> + <source>Select view:</source> <target>Selecionar visualização:</target> @@ -1055,6 +1088,15 @@ O comando é disparado se: <source>Handle daylight saving time</source> <target>Como lidar com horário de verão</target> +<source>Ignore errors</source> +<target>Ignorar erros</target> + +<source>Retry count:</source> +<target>Número de tentativas:</target> + +<source>Delay (in seconds):</source> +<target>Atraso (em segundos):</target> + <source>Performance improvements:</source> <target>Melhorias de desempenho:</target> @@ -1129,17 +1171,11 @@ O comando é disparado se: <source>Last x days:</source> <target>Últimos x dias:</target> -<source>Ignore errors</source> -<target>Ignorar erros</target> - -<source>Retry count:</source> -<target>Número de tentativas:</target> - -<source>Delay (in seconds):</source> -<target>Atraso (em segundos):</target> +<source>&Override default log path:</source> +<target>&Substituir caminho de log padrão:</target> -<source>Run a command after synchronization:</source> -<target>Executar um comando após a sincronização:</target> +<source>Run a command:</source> +<target>Executar um comando:</target> <source>OK</source> <target>OK</target> @@ -1189,6 +1225,9 @@ O comando é disparado se: <source>Directory on server:</source> <target>Diretório no servidor:</target> +<source>Access timeout (in seconds):</source> +<target>Tempo limite de acesso (em segundos):</target> + <source>SFTP channels per connection:</source> <target>Canais SFTP por conexão:</target> @@ -1211,7 +1250,7 @@ O comando é disparado se: <target>&Não mostrar esta caixa de diálogo novamente</target> <source>Items found:</source> -<target>Elementos encontrados:</target> +<target>Itens encontrados:</target> <source>Time remaining:</source> <target>Tempo restante:</target> @@ -1267,12 +1306,6 @@ O comando é disparado se: <source>Stop synchronization at first error</source> <target>Interromper a sincronização ao primeiro erro</target> -<source>Save log:</source> -<target>Salvar arquivo de log:</target> - -<source>Limit number of log files:</source> -<target>Limitar número de arquivos de log:</target> - <source>How can I schedule a batch job?</source> <target>Como posso agendar uma tarefa em lotes?</target> @@ -1309,8 +1342,11 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Mostrar todas as caixas de diálogo e as mensagens de aviso permanentemente ocultadas</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Caminho de log padrão:</target> + +<source>&Delete logs after x days:</source> +<target>&Excluir logs após x days:</target> <source>Customize context menu:</source> <target>Personalizar menu de contexto:</target> @@ -1321,8 +1357,8 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>&Default</source> <target>&Config. Padrão</target> -<source>Feedback and suggestions are welcome</source> -<target>Críticas e sugestões são bem-vindas</target> +<source>Feedback and suggestions are welcome:</source> +<target>Comentários e sugestões são bem-vindos:</target> <source>Home page</source> <target>Página web</target> @@ -1348,8 +1384,8 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Source code written in C++ using:</source> <target>Código-fonte escrito em C++ utilizando:</target> -<source>Published under the GNU General Public License</source> -<target>Publicado sob a GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Publicado sob a Licença Pública Geral GNU:</target> <source>Many thanks for localization:</source> <target>Pela tradução, um agradecimento a:</target> @@ -1406,7 +1442,7 @@ Isto garante um estado consistente mesmo em caso de erro grave. <target>Informações</target> <source>No log entries</source> -<target></target> +<target>Nenhuma entrada de log</target> <source>Select all</source> <target>Selecionar todos</target> @@ -1432,6 +1468,9 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Overview</source> <target>Parâmetros</target> +<source>Swap sides</source> +<target>Trocar lados</target> + <source>Show "%x"</source> <target>Mostrar "%x"</target> @@ -1606,9 +1645,6 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Show filtered or temporarily excluded files</source> <target>Mostrar arquivos que foram filtrados ou excluídos temporariamente</target> -<source>Save as default</source> -<target>Salvar como padrão</target> - <source>Filter</source> <target>Filtro</target> @@ -1954,12 +1990,6 @@ Isto garante um estado consistente mesmo em caso de erro grave. <source>Checking recycle bin failed for folder %x.</source> <target>Verificando falha na Lixeira para pasta %x.</target> -<source>The following XML elements could not be read:</source> -<target>Os seguintes elementos XML não puderam ser lidos:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Arquivo de configuração %x incompleto. Os elementos faltantes serão configurados com seus valores padrões.</target> - <source>Prepare installation</source> <target>Preparando a instalação</target> diff --git a/FreeFileSync/Build/Languages/romanian.lng b/FreeFileSync/Build/Languages/romanian.lng index 7a970005..52a59d90 100755 --- a/FreeFileSync/Build/Languages/romanian.lng +++ b/FreeFileSync/Build/Languages/romanian.lng @@ -161,6 +161,9 @@ <source>Items differ in attributes only</source> <target>Elementele diferă doar prin atributele lor</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Numele %x e folosit de mai multe elemente din dosar.</target> + <source>Resolving symbolic link %x</source> <target>Rezolv legătura simbolică %x</target> @@ -188,9 +191,6 @@ <source>File time tolerance</source> <target>Toleranța timpului filei</target> -<source>Folder access timeout</source> -<target>Expirarea accesului la dosar</target> - <source>Run with background priority</source> <target>Rulează cu prioritate de fundal [background]</target> @@ -336,8 +336,11 @@ <source>Update attributes on right</source> <target>Actualizează atributele în partea dreaptă</target> -<source>Warning</source> -<target>Atenție</target> +<source>Errors:</source> +<target>Erori:</target> + +<source>Warnings:</source> +<target>Avertizări:</target> <source>Items processed:</source> <target>Elemente Procesate:</target> @@ -348,6 +351,9 @@ <source>Total time:</source> <target>Timp Total:</target> +<source>Warning</source> +<target>Atenție</target> + <source>Stopped</source> <target>Oprită</target> @@ -357,6 +363,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Eroare la parsarea filei %x, rîndul %y, coloana %z.</target> +<source>Services</source> +<target>Servicii</target> + +<source>Show All</source> +<target>Arată Tot</target> + +<source>Hide Others</source> +<target>Ascunde Restul</target> + +<source>Hide %x</source> +<target>Ascunde %x</target> + +<source>Quit %x</source> +<target>Închide %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Nu pot seta zăvorîrea [lock] pentru dosarele următoare:</target> @@ -373,11 +394,11 @@ <source>Cannot read directory %x.</source> <target>Nu pot citi dosarul %x.</target> -<source>/sec</source> -<target>/sec</target> +<source>%x/sec</source> +<target>%x/sec</target> -<source>%x items/sec</source> -<target>%x elemente/sec</target> +<source>%x items</source> +<target>%x elemente</target> <source>Show in Explorer</source> <target>Arată în Exploratorul de File</target> @@ -526,11 +547,11 @@ <source>Generating database...</source> <target>Generez baza de date...</target> -<source>Searching for excess file versions:</source> -<target>Caut versiunile în exces ale filelor:</target> +<source>Searching for old file versions:</source> +<target>Caut versiuni mai vechi ale filelor:</target> -<source>Removing excess file versions:</source> -<target>Înlătur versiunile în exces ale filelor:</target> +<source>Removing old file versions:</source> +<target>Înlătur versiunile vechi ale filelor:</target> <source>Unable to create time stamp for versioning:</source> <target>Nu pot crea marcajul de timp pentru versionare:</target> @@ -588,6 +609,24 @@ Actuală: %y baiți <source>Unable to move %x to the recycle bin.</source> <target>Nu pot muta %x în Reciclator.</target> +<source>Unable to access %x.</source> +<target>Nu pot accesa %x.</target> + +<source>Authentication completed.</source> +<target>Autentificare realizată.</target> + +<source>Authentication failed.</source> +<target>Autentificare eșuată.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Poți închide pagina asta acum și continua cu FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Serverul a returnat o eroare:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Nu pot determina spațiul liber de stocare pentru %x.</target> + <source>Cannot find %x.</source> <target>Nu pot găsi %x.</target> @@ -600,18 +639,12 @@ Actuală: %y baiți <source>Cannot delete symbolic link %x.</source> <target>Nu pot șterge legătura simbolică %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Nu pot determina spațiul liber de stocare pentru %x.</target> - <source>Incorrect command line:</source> <target>Linie de comandă incorectă:</target> <source>The server does not support authentication via %x.</source> <target>Serverul nu suportă autentificarea prin %x.</target> -<source>Unable to access %x.</source> -<target>Nu pot accesa %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -638,25 +671,6 @@ Actuală: %y baiți <source>Failed to open SFTP channel number %x.</source> <target>N-am putut deschide canalul SFTP cu numărul %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 bait</pluralform> -<pluralform>%x baiți</pluralform> -<pluralform>%x de baiți</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Trage și pune un dosar mai jos sau explorează către el</target> @@ -770,6 +784,25 @@ Comanda este declanșată dacă: <source>&Retry</source> <target>&Reîncearcă</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 bait</pluralform> +<pluralform>%x baiți</pluralform> +<pluralform>%x de baiți</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Deschid...</target> @@ -916,7 +949,7 @@ Comanda este declanșată dacă: <target>Salvea&ză ca Sarcină Set...</target> <source>Show &log</source> -<target></target> +<target>Arată &Jurnalul</target> <source>Start &comparison</source> <target>Pornește &Compararea</target> @@ -978,9 +1011,6 @@ Comanda este declanșată dacă: <source>Access online storage</source> <target>Accesează stocarea de pe internet [online]</target> -<source>Swap sides</source> -<target>Schimbă compartimentele stîng și drept între ele</target> - <source>Close search bar</source> <target>Închide bara de căutare</target> @@ -1005,6 +1035,9 @@ Comanda este declanșată dacă: <source>View type:</source> <target>Tipul Vederii:</target> +<source>Save as default</source> +<target>Salvează ca implicit</target> + <source>Select view:</source> <target>Selectează Vederea:</target> @@ -1062,6 +1095,15 @@ Comanda este declanșată dacă: <source>Handle daylight saving time</source> <target>Gestionarea timpului de vară (DST)</target> +<source>Ignore errors</source> +<target>Ignoră erorile</target> + +<source>Retry count:</source> +<target>Numărul reîncercărilor:</target> + +<source>Delay (in seconds):</source> +<target>Întîrziere (în secunde):</target> + <source>Performance improvements:</source> <target>Îmbunătățiri de Performanță:</target> @@ -1136,17 +1178,11 @@ Comanda este declanșată dacă: <source>Last x days:</source> <target>Ultimele x zile:</target> -<source>Ignore errors</source> -<target>Ignoră erorile</target> - -<source>Retry count:</source> -<target>Numărul reîncercărilor:</target> - -<source>Delay (in seconds):</source> -<target>Întîrziere (în secunde):</target> +<source>&Override default log path:</source> +<target>Ign&oră calea implicită a jurnalului:</target> -<source>Run a command after synchronization:</source> -<target>Rulează o comandă după sincronizare:</target> +<source>Run a command:</source> +<target>Rulează o comandă:</target> <source>OK</source> <target>OK</target> @@ -1196,6 +1232,9 @@ Comanda este declanșată dacă: <source>Directory on server:</source> <target>Dosarul de pe server:</target> +<source>Access timeout (in seconds):</source> +<target>Timp de expirare a accesului (în sec.):</target> + <source>SFTP channels per connection:</source> <target>Canale SFTP per conexiune:</target> @@ -1274,12 +1313,6 @@ Comanda este declanșată dacă: <source>Stop synchronization at first error</source> <target>Oprește sincronizarea la prima eroare întîlnită</target> -<source>Save log:</source> -<target>Salvează jurnalul:</target> - -<source>Limit number of log files:</source> -<target>Limitează numărul filelor de jurnalizare:</target> - <source>How can I schedule a batch job?</source> <target>Cum pot planifica o sarcină set?</target> @@ -1316,8 +1349,11 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Show all permanently hidden dialogs and warning messages again</source> <target>Sînt arătate din nou dialogurile și mesajele de eroare care au fost ascunse permanent</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Calea implicită a jurnalului:</target> + +<source>&Delete logs after x days:</source> +<target>Șter&ge jurnalul după x zile:</target> <source>Customize context menu:</source> <target>Personalizează meniul contextual:</target> @@ -1328,8 +1364,8 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>&Default</source> <target>Coloanele &Implicite</target> -<source>Feedback and suggestions are welcome</source> -<target>Opiniile și sugestiile despre program sînt binevenite.</target> +<source>Feedback and suggestions are welcome:</source> +<target>Părerile și sugestiile sînt binevenite:</target> <source>Home page</source> <target>Pagina Sitului</target> @@ -1355,8 +1391,8 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Source code written in C++ using:</source> <target>Cod sursă scris în C++ folosind:</target> -<source>Published under the GNU General Public License</source> -<target>Publicat sub licența GNU GPL</target> +<source>Published under the GNU General Public License:</source> +<target>Publicat sub Licența Publică Generală GNU:</target> <source>Many thanks for localization:</source> <target>Multe mulțumiri pentru localizare:</target> @@ -1413,7 +1449,7 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <target>Informații</target> <source>No log entries</source> -<target></target> +<target>Nu există intrări în jurnal</target> <source>Select all</source> <target>Selectează Tot</target> @@ -1439,6 +1475,9 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Overview</source> <target>Panoramă</target> +<source>Swap sides</source> +<target>Schimbă compartimentele stîng și drept între ele</target> + <source>Show "%x"</source> <target>Arată "%x"</target> @@ -1617,9 +1656,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Show filtered or temporarily excluded files</source> <target>Arată filele filtrate sau excluse temporar</target> -<source>Save as default</source> -<target>Salvează ca implicit</target> - <source>Filter</source> <target>Filtrare</target> @@ -1970,12 +2006,6 @@ Aceasta garantează consecvența stării filelor chiar și în cazul apariției <source>Checking recycle bin failed for folder %x.</source> <target>Verificarea disponibilității Reciclatorului a eșuat pentru dosarul %x.</target> -<source>The following XML elements could not be read:</source> -<target>Elementele XML următoare nu pot fi citite:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Fila configurație %x este incompletă. Elementele lipsă vor fi setate la valorile implicite.</target> - <source>Prepare installation</source> <target>Pregătirea Instalării</target> diff --git a/FreeFileSync/Build/Languages/russian.lng b/FreeFileSync/Build/Languages/russian.lng index 17566e71..d6777279 100755 --- a/FreeFileSync/Build/Languages/russian.lng +++ b/FreeFileSync/Build/Languages/russian.lng @@ -161,6 +161,9 @@ <source>Items differ in attributes only</source> <target>Элементы различаются только атрибутами</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Имя %x используется более чем одним элементом в папке.</target> + <source>Resolving symbolic link %x</source> <target>Разрешение символьной ссылки %x</target> @@ -188,9 +191,6 @@ <source>File time tolerance</source> <target>Допускаемая разница во времени изменения файла</target> -<source>Folder access timeout</source> -<target>Тайм-аут доступа к папкам</target> - <source>Run with background priority</source> <target>Запустить с фоновым приоритетом</target> @@ -336,8 +336,11 @@ <source>Update attributes on right</source> <target>Обновление атрибутов справа</target> -<source>Warning</source> -<target>Внимание</target> +<source>Errors:</source> +<target>Ошибки:</target> + +<source>Warnings:</source> +<target>Предупеждения:</target> <source>Items processed:</source> <target>Элементов обработано:</target> @@ -348,15 +351,33 @@ <source>Total time:</source> <target>Общее время:</target> +<source>Warning</source> +<target>Внимание</target> + <source>Stopped</source> <target>Остановлено</target> <source>Cleaning up log files:</source> -<target>Очистка лог-файлов (журналов):</target> +<target>Очистка файлов журнала:</target> <source>Error parsing file %x, row %y, column %z.</source> <target>Ошибка при разборе файла %x, строка %y, колонка %z.</target> +<source>Services</source> +<target>Службы</target> + +<source>Show All</source> +<target>Показать все</target> + +<source>Hide Others</source> +<target>Скрыть остальные</target> + +<source>Hide %x</source> +<target>Скрыть %x</target> + +<source>Quit %x</source> +<target>Выйти из %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Невозможно установить блокировки для следующих папок:</target> @@ -373,11 +394,11 @@ <source>Cannot read directory %x.</source> <target>Невозможно прочитать папку %x.</target> -<source>/sec</source> -<target>/с</target> +<source>%x/sec</source> +<target>%x/с</target> -<source>%x items/sec</source> -<target>%x элементов/с</target> +<source>%x items</source> +<target>%x элемент(ов)</target> <source>Show in Explorer</source> <target>Показать в Проводнике</target> @@ -526,11 +547,11 @@ <source>Generating database...</source> <target>Создание базы данных...</target> -<source>Searching for excess file versions:</source> -<target>Поиск лишних версий файлов:</target> +<source>Searching for old file versions:</source> +<target>Поиск старых версий файлов:</target> -<source>Removing excess file versions:</source> -<target>Удаление лишних версий файлов:</target> +<source>Removing old file versions:</source> +<target>Удаление старых версий файлов:</target> <source>Unable to create time stamp for versioning:</source> <target>Невозможно создать метку времени для архивирования файлов:</target> @@ -588,6 +609,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Невозможно переместить %x в "Корзину".</target> +<source>Unable to access %x.</source> +<target>Невозможно получить доступ к %x.</target> + +<source>Authentication completed.</source> +<target>Аутентификация завершена.</target> + +<source>Authentication failed.</source> +<target>Ошибка аутентификации.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Вы можете закрыть эту страницу сейчас и продолжить с FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Сервер возвратил ошибку:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Невозможно определить свободное место на диске для %x.</target> + <source>Cannot find %x.</source> <target>Невозможно найти %x.</target> @@ -600,18 +639,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Невозможно удалить символьную ссылку %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Невозможно определить свободное место на диске для %x.</target> - <source>Incorrect command line:</source> <target>Неверная командная строка:</target> <source>The server does not support authentication via %x.</source> <target>Сервер не поддерживает аутентификацию с помощью %x.</target> -<source>Unable to access %x.</source> -<target>Невозможно получить доступ к %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -638,25 +671,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Не удалось открыть SFTP канал номер %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x байт</pluralform> -<pluralform>%x байта</pluralform> -<pluralform>%x байт</pluralform> -</target> - -<source>%x MB</source> -<target>%x МБ</target> - -<source>%x KB</source> -<target>%x КБ</target> - -<source>%x GB</source> -<target>%x ГБ</target> - <source>Drag && drop</source> <target>Drag && drop</target> @@ -770,6 +784,25 @@ The command is triggered if: <source>&Retry</source> <target>&Повторить</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x байт</pluralform> +<pluralform>%x байта</pluralform> +<pluralform>%x байт</pluralform> +</target> + +<source>%x MB</source> +<target>%x МБ</target> + +<source>%x KB</source> +<target>%x КБ</target> + +<source>%x GB</source> +<target>%x ГБ</target> + <source>Loading...</source> <target>Загрузка...</target> @@ -838,7 +871,7 @@ The command is triggered if: <target>Последняя синхронизация</target> <source>Log</source> -<target>Лог (журнал)</target> +<target>Журнал</target> <source>Folder</source> <target>Папка</target> @@ -916,7 +949,7 @@ The command is triggered if: <target>Сохранить как пакетное &задание...</target> <source>Show &log</source> -<target></target> +<target>Показать &журнал</target> <source>Start &comparison</source> <target>Начать с&равнение</target> @@ -978,9 +1011,6 @@ The command is triggered if: <source>Access online storage</source> <target>Доступ к онлайн-хранилищу</target> -<source>Swap sides</source> -<target>Поменять направление</target> - <source>Close search bar</source> <target>Закрыть строку поиска</target> @@ -1005,6 +1035,9 @@ The command is triggered if: <source>View type:</source> <target>Тип просмотра:</target> +<source>Save as default</source> +<target>Сохранить по умолчанию</target> + <source>Select view:</source> <target>Выберите отображение:</target> @@ -1062,6 +1095,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>Ручной переход на летнее время</target> +<source>Ignore errors</source> +<target>Игнорировать ошибки</target> + +<source>Retry count:</source> +<target>Число повторений:</target> + +<source>Delay (in seconds):</source> +<target>Задержка (в секундах):</target> + <source>Performance improvements:</source> <target>Повышение производительности:</target> @@ -1136,17 +1178,11 @@ The command is triggered if: <source>Last x days:</source> <target>Последние x дней:</target> -<source>Ignore errors</source> -<target>Игнорировать ошибки</target> - -<source>Retry count:</source> -<target>Число повторений:</target> - -<source>Delay (in seconds):</source> -<target>Задержка (в секундах):</target> +<source>&Override default log path:</source> +<target>&Изменить путь журнала по умолчанию:</target> -<source>Run a command after synchronization:</source> -<target>Запустить команду после синхронизации:</target> +<source>Run a command:</source> +<target>Выполнить команду:</target> <source>OK</source> <target>OK</target> @@ -1196,6 +1232,9 @@ The command is triggered if: <source>Directory on server:</source> <target>Папка на сервере:</target> +<source>Access timeout (in seconds):</source> +<target>Тайм-аут доступа (в секундах):</target> + <source>SFTP channels per connection:</source> <target>Количество SFTP каналов на одно соединение:</target> @@ -1274,12 +1313,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>Остановить синхронизацию при первой ошибке</target> -<source>Save log:</source> -<target>Сохранять лог-файлы (журналы):</target> - -<source>Limit number of log files:</source> -<target>Ограничить количество лог-файлов (журналов):</target> - <source>How can I schedule a batch job?</source> <target>Как запланировать пакетное задание?</target> @@ -1316,8 +1349,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Показать все скрытые окна и сообщения с предупреждениями снова</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Путь журнала по умолчанию:</target> + +<source>&Delete logs after x days:</source> +<target>&Удаление журналов через x дней:</target> <source>Customize context menu:</source> <target>Кастомизация контекстного меню:</target> @@ -1328,8 +1364,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>&По умолчанию</target> -<source>Feedback and suggestions are welcome</source> -<target>Замечания и предложения приветствуются</target> +<source>Feedback and suggestions are welcome:</source> +<target>Обратная связь и предложения приветствуются:</target> <source>Home page</source> <target>Домашняя страница</target> @@ -1355,8 +1391,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Исходный код написан на C++ с использованием:</target> -<source>Published under the GNU General Public License</source> -<target>Издается под лицензией GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Опубликовано в соответствии с GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Большое спасибо за перевод:</target> @@ -1413,7 +1449,7 @@ This guarantees a consistent state even in case of a serious error. <target>Информация</target> <source>No log entries</source> -<target></target> +<target>Нет записей в журнале</target> <source>Select all</source> <target>Выделить все</target> @@ -1439,6 +1475,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>Главная</target> +<source>Swap sides</source> +<target>Поменять направление</target> + <source>Show "%x"</source> <target>Показать "%x"</target> @@ -1617,9 +1656,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>Показать отфильтрованные или временно исключенные файлы</target> -<source>Save as default</source> -<target>Сохранить по умолчанию</target> - <source>Filter</source> <target>Фильтр</target> @@ -1970,12 +2006,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>Проверка "Корзины" не удалась для папки %x.</target> -<source>The following XML elements could not be read:</source> -<target>Следующие XML-элементы не могут быть прочитаны:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Файл конфигурации %x является неполным. Недостающие элементы будут установлены в значения по умолчанию.</target> - <source>Prepare installation</source> <target>Подготовка установки</target> diff --git a/FreeFileSync/Build/Languages/slovak.lng b/FreeFileSync/Build/Languages/slovak.lng index 4d296d17..4e0894dd 100755 --- a/FreeFileSync/Build/Languages/slovak.lng +++ b/FreeFileSync/Build/Languages/slovak.lng @@ -7,6 +7,81 @@ <plural_definition>n==1 ? 0 : n>=2 && n<=4 ? 1 : 2</plural_definition> </header> +<source>No log entries</source> +<target></target> + +<source>Published under the GNU General Public License:</source> +<target></target> + +<source>Feedback and suggestions are welcome:</source> +<target></target> + +<source>&Delete logs after x days:</source> +<target></target> + +<source>Default log path:</source> +<target></target> + +<source>Access timeout (in seconds):</source> +<target></target> + +<source>Run a command:</source> +<target></target> + +<source>&Override default log path:</source> +<target></target> + +<source>Show &log</source> +<target></target> + +<source>The server returned an error:</source> +<target></target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target></target> + +<source>Authentication failed.</source> +<target></target> + +<source>Authentication completed.</source> +<target></target> + +<source>Removing old file versions:</source> +<target></target> + +<source>Searching for old file versions:</source> +<target></target> + +<source>%x items</source> +<target></target> + +<source>%x/sec</source> +<target></target> + +<source>Quit %x</source> +<target></target> + +<source>Hide %x</source> +<target></target> + +<source>Hide Others</source> +<target></target> + +<source>Show All</source> +<target></target> + +<source>Services</source> +<target></target> + +<source>Warnings:</source> +<target></target> + +<source>Errors:</source> +<target></target> + +<source>The name %x is used by more than one item in the folder.</source> +<target></target> + <source>Both sides have changed since last synchronization.</source> <target>Od poslednej synchronizácie došlo ku zmene obidvoch strán.</target> @@ -188,9 +263,6 @@ <source>File time tolerance</source> <target>Časová tolerancia súboru</target> -<source>Folder access timeout</source> -<target>Časový limit prístupu k priečinku</target> - <source>Run with background priority</source> <target>Vykonať s prioritou na pozadí</target> @@ -336,9 +408,6 @@ <source>Update attributes on right</source> <target>Aktualizovať atribúty napravo</target> -<source>Warning</source> -<target>Varovanie</target> - <source>Items processed:</source> <target>Spracovaných položiek:</target> @@ -348,6 +417,9 @@ <source>Total time:</source> <target>Celkový čas:</target> +<source>Warning</source> +<target>Varovanie</target> + <source>Stopped</source> <target>Zastavené</target> @@ -373,12 +445,6 @@ <source>Cannot read directory %x.</source> <target>Nie je možné načítať adresár %x.</target> -<source>/sec</source> -<target>/s</target> - -<source>%x items/sec</source> -<target>%x položiek/s</target> - <source>Show in Explorer</source> <target>Zobraziť v Prieskumníkovi</target> @@ -526,12 +592,6 @@ <source>Generating database...</source> <target>Vytváranie databázy...</target> -<source>Searching for excess file versions:</source> -<target>Hľadanie prebytočných verzií súborov:</target> - -<source>Removing excess file versions:</source> -<target>Odstránenie prebytočných verzií súborov:</target> - <source>Unable to create time stamp for versioning:</source> <target>Nie je možné vytvoriť časovú značku verzovania:</target> @@ -588,6 +648,12 @@ Aktuálne: %y b <source>Unable to move %x to the recycle bin.</source> <target>Nie je možné presunúť %x do Koša.</target> +<source>Unable to access %x.</source> +<target>Nie je možný prístup k %x.</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Nie je možné zistiť voľné miesto na disku %x.</target> + <source>Cannot find %x.</source> <target>Nie je možné nájsť %x.</target> @@ -600,18 +666,12 @@ Aktuálne: %y b <source>Cannot delete symbolic link %x.</source> <target>Nie je možné zmazať symbolický odkaz %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Nie je možné zistiť voľné miesto na disku %x.</target> - <source>Incorrect command line:</source> <target>Neplatný príkaz:</target> <source>The server does not support authentication via %x.</source> <target>Server nepodporuje overenie pomocou %x.</target> -<source>Unable to access %x.</source> -<target>Nie je možný prístup k %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -638,25 +698,6 @@ Aktuálne: %y b <source>Failed to open SFTP channel number %x.</source> <target>Nie je možné otvoriť kanál číslo %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x B</pluralform> -<pluralform>%x B</pluralform> -<pluralform>%x B</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Pretiahni sem && pusť</target> @@ -770,6 +811,25 @@ Príkaz bude spustení ak: <source>&Retry</source> <target>&Opakovať</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x B</pluralform> +<pluralform>%x B</pluralform> +<pluralform>%x B</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Načítanie...</target> @@ -915,9 +975,6 @@ Príkaz bude spustení ak: <source>Save as &batch job...</source> <target>Uložiť ako &dávku...</target> -<source>Show &log</source> -<target></target> - <source>Start &comparison</source> <target>Spustiť &porovnanie</target> @@ -978,9 +1035,6 @@ Príkaz bude spustení ak: <source>Access online storage</source> <target>Prístup k online úložisku</target> -<source>Swap sides</source> -<target>Zámena strán</target> - <source>Close search bar</source> <target>Zavrieť hľadanie</target> @@ -1005,6 +1059,9 @@ Príkaz bude spustení ak: <source>View type:</source> <target>Typ zobrazenia:</target> +<source>Save as default</source> +<target>Uložiť ako predvolené</target> + <source>Select view:</source> <target>Výber zobrazenia:</target> @@ -1062,6 +1119,15 @@ Príkaz bude spustení ak: <source>Handle daylight saving time</source> <target>Používať letný čas</target> +<source>Ignore errors</source> +<target>Ignorovať chyby</target> + +<source>Retry count:</source> +<target>Počet opakovaní:</target> + +<source>Delay (in seconds):</source> +<target>Oneskorenie (v sekundách):</target> + <source>Performance improvements:</source> <target>Zlepšenie výkonu:</target> @@ -1136,18 +1202,6 @@ Príkaz bude spustení ak: <source>Last x days:</source> <target>Posledných x dní:</target> -<source>Ignore errors</source> -<target>Ignorovať chyby</target> - -<source>Retry count:</source> -<target>Počet opakovaní:</target> - -<source>Delay (in seconds):</source> -<target>Oneskorenie (v sekundách):</target> - -<source>Run a command after synchronization:</source> -<target>Spustiť príkaz po synchronizácií:</target> - <source>OK</source> <target>OK</target> @@ -1274,12 +1328,6 @@ Príkaz bude spustení ak: <source>Stop synchronization at first error</source> <target>Ukončiť synchronizáciu pri prvej chybe</target> -<source>Save log:</source> -<target>Uložiť log:</target> - -<source>Limit number of log files:</source> -<target>Limit počtu log súborov:</target> - <source>How can I schedule a batch job?</source> <target>Ako nastaviť spustenie dávky v Plánovači?</target> @@ -1313,9 +1361,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Zobraziť znovu všetky trvale skryté dialógy a varovné hlásenia</target> -<source>Remove old log files after x days:</source> -<target></target> - <source>Customize context menu:</source> <target>Prispôsobiť kontextovú ponuku:</target> @@ -1325,9 +1370,6 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>&Predvolené</target> -<source>Feedback and suggestions are welcome</source> -<target>Komentáre a námety sú vždy vítané</target> - <source>Home page</source> <target>Domovská stránka</target> @@ -1352,9 +1394,6 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Zdrojový kód bol napísaný kompletne v C++ pomocou:</target> -<source>Published under the GNU General Public License</source> -<target>Vydané pod GNU General Public License (GPL)</target> - <source>Many thanks for localization:</source> <target>Poďakovanie za preklad FreeFileSync:</target> @@ -1409,9 +1448,6 @@ This guarantees a consistent state even in case of a serious error. <source>Info</source> <target>Info</target> -<source>No log entries</source> -<target></target> - <source>Select all</source> <target>Vybrať všetko</target> @@ -1436,6 +1472,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>Prehľad</target> +<source>Swap sides</source> +<target>Zámena strán</target> + <source>Show "%x"</source> <target>Zobraziť "%x"</target> @@ -1614,9 +1653,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>Zobraziť filtrované alebo dočasne vynechané súbory</target> -<source>Save as default</source> -<target>Uložiť ako predvolené</target> - <source>Filter</source> <target>Filter</target> @@ -1967,12 +2003,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>Zlyhala kontrola Koša pre priečinok %x.</target> -<source>The following XML elements could not be read:</source> -<target>Nie je možné načítať následujúce XML elementy:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Konfiguračný súbor %x je nekompletný. Chybajúce položky budú nahradené predvolenými hodnotami.</target> - <source>Prepare installation</source> <target>Príprava inštalácie</target> @@ -2043,7 +2073,7 @@ This guarantees a consistent state even in case of a serious error. <target>Upraviť vo FreeFileSync</target> <source>Instead of an ad, here's an animal.</source> -<target>Namiesto reklami je tu obrázok zvieraťa.</target> +<target>Namiesto reklamy je tu obrázok zvieraťa.</target> <source>The FreeFileSync portable version cannot install into a subfolder of %x.</source> <target>Prenosnú verziu FreeFileSync nie je možné inštalovať do podpriečinka %x.</target> diff --git a/FreeFileSync/Build/Languages/slovenian.lng b/FreeFileSync/Build/Languages/slovenian.lng index 8dc2eea4..f013812b 100755 --- a/FreeFileSync/Build/Languages/slovenian.lng +++ b/FreeFileSync/Build/Languages/slovenian.lng @@ -162,6 +162,9 @@ <source>Items differ in attributes only</source> <target>Postavke se razlikujejo samo po atributih</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Ime %x uporablja več kot ena postavka v mapi.</target> + <source>Resolving symbolic link %x</source> <target>Razrešujem simbolično povezavo %x</target> @@ -189,9 +192,6 @@ <source>File time tolerance</source> <target>Časovna toleranca datoteke</target> -<source>Folder access timeout</source> -<target>Časovna omejitev dostopa do mape</target> - <source>Run with background priority</source> <target>Zaženi s prioriteto v ozadju</target> @@ -338,8 +338,11 @@ <source>Update attributes on right</source> <target>Posodobi atribute na desni strani</target> -<source>Warning</source> -<target>Opozorilo</target> +<source>Errors:</source> +<target>Napake:</target> + +<source>Warnings:</source> +<target>Opozorila:</target> <source>Items processed:</source> <target>Obdelanih postavk:</target> @@ -350,6 +353,9 @@ <source>Total time:</source> <target>Celoten čas:</target> +<source>Warning</source> +<target>Opozorilo</target> + <source>Stopped</source> <target>Ustavljeno</target> @@ -359,6 +365,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Napaka pri razčlenjevanju datoteke %x, vrstica %y, stolpec %z.</target> +<source>Services</source> +<target>Storitve</target> + +<source>Show All</source> +<target>Pokaži vse</target> + +<source>Hide Others</source> +<target>Skrij druge</target> + +<source>Hide %x</source> +<target>Skrij %x</target> + +<source>Quit %x</source> +<target>Nehaj %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Ne morem nastaviti zaklepanje imenika za naslednje mape:</target> @@ -376,11 +397,11 @@ <source>Cannot read directory %x.</source> <target>Ne morem prebrati direktorija %x.</target> -<source>/sec</source> -<target>/sek</target> +<source>%x/sec</source> +<target>%x/sek</target> -<source>%x items/sec</source> -<target>%x postavk/sek</target> +<source>%x items</source> +<target>%x postavk</target> <source>Show in Explorer</source> <target>Prikaži v Raziskovalcu</target> @@ -529,11 +550,11 @@ <source>Generating database...</source> <target>Ustvarjam podatkovno bazo...</target> -<source>Searching for excess file versions:</source> -<target>Iskanje presežnih različic datotek:</target> +<source>Searching for old file versions:</source> +<target>Iskanje starih različic datotek:</target> -<source>Removing excess file versions:</source> -<target>Odstranjevanje presežnih različic datotek:</target> +<source>Removing old file versions:</source> +<target>Odstranjevanje starejših različic datotek:</target> <source>Unable to create time stamp for versioning:</source> <target>Časovnega žiga za oznčitev ni bilo mogoče ustvariti:</target> @@ -591,6 +612,24 @@ Dejansko: %y bajtov <source>Unable to move %x to the recycle bin.</source> <target>Ne morem premakniti %x v koš.</target> +<source>Unable to access %x.</source> +<target>Ne morem dostopati do %x.</target> + +<source>Authentication completed.</source> +<target>Preverjanje pristnosti končano.</target> + +<source>Authentication failed.</source> +<target>Preverjanje pristnosti ni uspelo.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>To stran lahko zdaj zaprete in nadaljujete z FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Strežnik je vrnil napako:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Ne morem določiti prostega prostora na disku za %x.</target> + <source>Cannot find %x.</source> <target>Ne najdem %x.</target> @@ -603,18 +642,12 @@ Dejansko: %y bajtov <source>Cannot delete symbolic link %x.</source> <target>Ne morem izbrisati simbolične povezave %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Ne morem določiti prostega prostora na disku za %x.</target> - <source>Incorrect command line:</source> <target>Napačna ukazna vrstica:</target> <source>The server does not support authentication via %x.</source> <target>Strežnik ne podpira preverjanja pristnosti preko %x.</target> -<source>Unable to access %x.</source> -<target>Ne morem dostopati do %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -643,26 +676,6 @@ Dejansko: %y bajtov <source>Failed to open SFTP channel number %x.</source> <target>Ne morem odpreti SFTP kanala številka %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x bajt</pluralform> -<pluralform>%x bajta</pluralform> -<pluralform>%x bajti</pluralform> -<pluralform>%x bajtov</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Povleci && spusti</target> @@ -776,6 +789,26 @@ Ukaz se sproži če: <source>&Retry</source> <target>&Ponovi</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x bajt</pluralform> +<pluralform>%x bajta</pluralform> +<pluralform>%x bajti</pluralform> +<pluralform>%x bajtov</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Nalagam...</target> @@ -923,7 +956,7 @@ Ukaz se sproži če: <target>Shrani kot paketno op&ravilo...</target> <source>Show &log</source> -<target></target> +<target>Pokaži &dnevnik</target> <source>Start &comparison</source> <target>Začni &primerjavo</target> @@ -985,9 +1018,6 @@ Ukaz se sproži če: <source>Access online storage</source> <target>Dostop do spletnega prostora za shranjevanje</target> -<source>Swap sides</source> -<target>Zamenjaj strani</target> - <source>Close search bar</source> <target>Zapri iskalno vrstico</target> @@ -1012,6 +1042,9 @@ Ukaz se sproži če: <source>View type:</source> <target>Vrsta prikaza:</target> +<source>Save as default</source> +<target>Shrani kot privzeto</target> + <source>Select view:</source> <target>Izberi prikaz:</target> @@ -1069,6 +1102,15 @@ Ukaz se sproži če: <source>Handle daylight saving time</source> <target>Upoštevaj poletni in zimski čas</target> +<source>Ignore errors</source> +<target>Prezri napake</target> + +<source>Retry count:</source> +<target>Število poskusov:</target> + +<source>Delay (in seconds):</source> +<target>Zakasnitev (v sekundah):</target> + <source>Performance improvements:</source> <target>Izboljšave zmogljivosti:</target> @@ -1143,17 +1185,11 @@ Ukaz se sproži če: <source>Last x days:</source> <target>Zadnji x dnevi:</target> -<source>Ignore errors</source> -<target>Prezri napake</target> - -<source>Retry count:</source> -<target>Število poskusov:</target> - -<source>Delay (in seconds):</source> -<target>Zakasnitev (v sekundah):</target> +<source>&Override default log path:</source> +<target>&Preglasi privzeto pot dnevnika:</target> -<source>Run a command after synchronization:</source> -<target>Zaženi ukaz po sinhronizaciji:</target> +<source>Run a command:</source> +<target>Zaženi ukaz:</target> <source>OK</source> <target>V redu</target> @@ -1203,6 +1239,9 @@ Ukaz se sproži če: <source>Directory on server:</source> <target>Imenik na strežniku:</target> +<source>Access timeout (in seconds):</source> +<target>Časovna omejitev dostopa (v sekundah):</target> + <source>SFTP channels per connection:</source> <target>SFTP kanali za povezavo:</target> @@ -1281,12 +1320,6 @@ Ukaz se sproži če: <source>Stop synchronization at first error</source> <target>Ustavi sinhronizacijo ob prvi napaki</target> -<source>Save log:</source> -<target>Shrani dnevnik:</target> - -<source>Limit number of log files:</source> -<target>Omejitve števila datotek dnevnika:</target> - <source>How can I schedule a batch job?</source> <target>Kako lahko načrtujem opravilo v paketu?</target> @@ -1323,8 +1356,11 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Prikaži vsa trajno skrita pogovorna okna in opozorilna sporočila</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Privzeta pot dnevnika:</target> + +<source>&Delete logs after x days:</source> +<target>&Izbriši dnevnike po x dneh:</target> <source>Customize context menu:</source> <target>Prilagodi kontekstni meni:</target> @@ -1335,8 +1371,8 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>&Default</source> <target>&Privzeto</target> -<source>Feedback and suggestions are welcome</source> -<target>Povratne informacije in predlogi so dobrodošli</target> +<source>Feedback and suggestions are welcome:</source> +<target>Povratne informacije in predlogi so dobrodošli:</target> <source>Home page</source> <target>Domača stran</target> @@ -1362,8 +1398,8 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Source code written in C++ using:</source> <target>Izvorna koda napisana v C++ z uporabo:</target> -<source>Published under the GNU General Public License</source> -<target>Objavljeno pod licenco GNU General Public</target> +<source>Published under the GNU General Public License:</source> +<target>Objavljeno pod licenco GNU General Public:</target> <source>Many thanks for localization:</source> <target>Zahvala prevajalcem za lokalizacijo:</target> @@ -1420,7 +1456,7 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Ni zapisov v dnevniku</target> <source>Select all</source> <target>Izberi vse</target> @@ -1446,6 +1482,9 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Overview</source> <target>Predogled</target> +<source>Swap sides</source> +<target>Zamenjaj strani</target> + <source>Show "%x"</source> <target>Prikaži "%x"</target> @@ -1628,9 +1667,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Show filtered or temporarily excluded files</source> <target>Pokaži filtrirane ali začasno izključene datoteke</target> -<source>Save as default</source> -<target>Shrani kot privzeto</target> - <source>Filter</source> <target>Filter</target> @@ -1986,12 +2022,6 @@ To zagotavlja dosledno stanje tudi v primeru resne napake. <source>Checking recycle bin failed for folder %x.</source> <target>Preverjanje koša za mapo %x ni uspelo.</target> -<source>The following XML elements could not be read:</source> -<target>Neaslednje XML postavke niso berljive:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Nastavitvena datoteka %x je nepopolna. Manjkajoči elementi bodo nastavljeni na privzete vrednosti.</target> - <source>Prepare installation</source> <target>Pripravljam namestitev</target> diff --git a/FreeFileSync/Build/Languages/spanish.lng b/FreeFileSync/Build/Languages/spanish.lng index d539e749..0bb0b241 100755 --- a/FreeFileSync/Build/Languages/spanish.lng +++ b/FreeFileSync/Build/Languages/spanish.lng @@ -101,7 +101,7 @@ <target>archivo de config. global:</target> <source>Any number of FreeFileSync "ffs_gui" and/or "ffs_batch" configuration files.</source> -<target>Cualquier número de archivos de configuración de FreeFileSync ("ffs_gui" ó "ffs_batch").</target> +<target>Cualquier número de archivos de configuración "ffs_gui" y/o "ffs_batch" de FreeFileSync.</target> <source>Any number of alternative directory pairs for at most one config file.</source> <target>Cualquier número de pares de directorios alternativos para un archivo de configuración como máximo.</target> @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Los elementos sólo se diferencian en los atributos</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>El nombre %x es utilizado por más de un elemento en la carpeta.</target> + <source>Resolving symbolic link %x</source> <target>Resolviendo vínculo simbólico %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Tolerancia en hora de archivo</target> -<source>Folder access timeout</source> -<target>Tiempo de espera de acceso a carpetas</target> - <source>Run with background priority</source> <target>Ejecutar con prioridad en segundo plano</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Actualizar atributos en la derecha</target> -<source>Warning</source> -<target>Atención</target> +<source>Errors:</source> +<target>Errores:</target> + +<source>Warnings:</source> +<target>Advertencias:</target> <source>Items processed:</source> <target>Elementos procesados:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Tiempo total:</target> +<source>Warning</source> +<target>Atención</target> + <source>Stopped</source> <target>Detenido</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Error analizando archivo %x, fila %y, columna %z.</target> +<source>Services</source> +<target>Servicios</target> + +<source>Show All</source> +<target>Mostrar todo</target> + +<source>Hide Others</source> +<target>Ocultar otros</target> + +<source>Hide %x</source> +<target>Ocultar %x</target> + +<source>Quit %x</source> +<target>Salir de %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>No se pudieron bloquear directorios para las carpetas siguientes:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>No se puede leer el directorio %x.</target> -<source>/sec</source> -<target>/seg</target> +<source>%x/sec</source> +<target>%x/seg</target> -<source>%x items/sec</source> -<target>%x elementos/seg</target> +<source>%x items</source> +<target>%x elementos</target> <source>Show in Explorer</source> <target>Mostrar en el Explorador</target> @@ -523,10 +544,10 @@ <source>Generating database...</source> <target>Generando base de datos...</target> -<source>Searching for excess file versions:</source> +<source>Searching for old file versions:</source> <target>Buscando versiones antiguas de archivos:</target> -<source>Removing excess file versions:</source> +<source>Removing old file versions:</source> <target>Eliminando versiones antiguas de archivos:</target> <source>Unable to create time stamp for versioning:</source> @@ -585,6 +606,24 @@ Reales: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>No es posible mover %x a la papelera de reciclaje.</target> +<source>Unable to access %x.</source> +<target>No se puede acceder a %x.</target> + +<source>Authentication completed.</source> +<target>Autenticación completada.</target> + +<source>Authentication failed.</source> +<target>Fallo de autenticación.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Puede cerrar esta página y continuar con FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>El servidor devolvió un error:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>No se puede determinar el espacio libre en disco para %x.</target> + <source>Cannot find %x.</source> <target>No se encuentra %x.</target> @@ -597,18 +636,12 @@ Reales: %y bytes <source>Cannot delete symbolic link %x.</source> <target>No se puede eliminar el vínculo simbólico %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>No se puede determinar el espacio libre en disco para %x.</target> - <source>Incorrect command line:</source> <target>Línea de comandos incorrecta:</target> <source>The server does not support authentication via %x.</source> <target>El servidor no admite autenticación via %x.</target> -<source>Unable to access %x.</source> -<target>No se puede acceder a %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Reales: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Fallo de apertura del canal SFTP número %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x Gb</target> - <source>Drag && drop</source> <target>Arrastrar y soltar</target> @@ -764,6 +779,24 @@ El comando es disparado si: <source>&Retry</source> <target>&Reintentar</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x Gb</target> + <source>Loading...</source> <target>Cargando...</target> @@ -909,7 +942,7 @@ El comando es disparado si: <target>Guardar como tarea por &lotes...</target> <source>Show &log</source> -<target></target> +<target>Mostrar re&gistro</target> <source>Start &comparison</source> <target>Iniciar la &comparación</target> @@ -971,9 +1004,6 @@ El comando es disparado si: <source>Access online storage</source> <target>Acceder a almacenamiento en línea</target> -<source>Swap sides</source> -<target>Intercambiar lados</target> - <source>Close search bar</source> <target>Cerrar la barra de búsqueda</target> @@ -998,6 +1028,9 @@ El comando es disparado si: <source>View type:</source> <target>Ver tipo:</target> +<source>Save as default</source> +<target>Guardar como predeterminado</target> + <source>Select view:</source> <target>Seleccionar vista:</target> @@ -1055,6 +1088,15 @@ El comando es disparado si: <source>Handle daylight saving time</source> <target>Respetar el horario de verano</target> +<source>Ignore errors</source> +<target>Ignorar errores</target> + +<source>Retry count:</source> +<target>Cuenta de reintentos:</target> + +<source>Delay (in seconds):</source> +<target>Retardo (en segundos):</target> + <source>Performance improvements:</source> <target>Mejoras en el rendimiento:</target> @@ -1129,17 +1171,11 @@ El comando es disparado si: <source>Last x days:</source> <target>Últimos x días:</target> -<source>Ignore errors</source> -<target>Ignorar errores</target> - -<source>Retry count:</source> -<target>Cuenta de reintentos:</target> - -<source>Delay (in seconds):</source> -<target>Retardo (en segundos):</target> +<source>&Override default log path:</source> +<target>&Reemplazar la ruta predeterminada del registro:</target> -<source>Run a command after synchronization:</source> -<target>Ejecutar un comando tras la sincronización:</target> +<source>Run a command:</source> +<target>Ejecutar un comando:</target> <source>OK</source> <target>OK</target> @@ -1189,6 +1225,9 @@ El comando es disparado si: <source>Directory on server:</source> <target>Directorio del servidor:</target> +<source>Access timeout (in seconds):</source> +<target>Tiempo de espera en acceso (segundos):</target> + <source>SFTP channels per connection:</source> <target>Canales SFTP por conexión:</target> @@ -1250,7 +1289,7 @@ El comando es disparado si: <target>Crear tarea por lotes para sincronización desatendida. Para iniciar, haga doble clic en este archivo o prográmelo en el planificador de tareas: %x</target> <source>Progress dialog:</source> -<target>Diálogo de progreson:</target> +<target>Diálogo de progreso:</target> <source>Run minimized</source> <target>Ejecutar minimizado</target> @@ -1267,12 +1306,6 @@ El comando es disparado si: <source>Stop synchronization at first error</source> <target>Detener la sincronización con el primer error</target> -<source>Save log:</source> -<target>Guardar registro:</target> - -<source>Limit number of log files:</source> -<target>Limitar el número de archivos de registro:</target> - <source>How can I schedule a batch job?</source> <target>¿Cómo puedo programar una tarea por lotes?</target> @@ -1309,8 +1342,11 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Volver a mostrar todos los diálogos y mensajes de advertencia que fueron permanentemente ocultados</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Ruta predeterminada del registro:</target> + +<source>&Delete logs after x days:</source> +<target>&Eliminar los registros tras x días:</target> <source>Customize context menu:</source> <target>Personalizar menú contextual:</target> @@ -1321,8 +1357,8 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>&Default</source> <target>&Configuración predeterminada</target> -<source>Feedback and suggestions are welcome</source> -<target>Tus comentarios y sugerencias son bienvenidos:</target> +<source>Feedback and suggestions are welcome:</source> +<target>Comentarios y sugerencias son bienvenidos:</target> <source>Home page</source> <target>Página de inicio</target> @@ -1348,8 +1384,8 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Source code written in C++ using:</source> <target>Código fuente C++ con soporte de:</target> -<source>Published under the GNU General Public License</source> -<target>Publicado bajo régimen de derechos GNU General Public License:</target> +<source>Published under the GNU General Public License:</source> +<target>Publicado con licencia pública general GNU:</target> <source>Many thanks for localization:</source> <target>Agradecimientos a los traductores:</target> @@ -1406,7 +1442,7 @@ Esto garantiza un estado coherente incluso en caso de error grave. <target>Info</target> <source>No log entries</source> -<target></target> +<target>No hay entradas de registro</target> <source>Select all</source> <target>Seleccionar todo</target> @@ -1432,6 +1468,9 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Overview</source> <target>Vista general</target> +<source>Swap sides</source> +<target>Intercambiar lados</target> + <source>Show "%x"</source> <target>Mostrar "%x"</target> @@ -1606,9 +1645,6 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Show filtered or temporarily excluded files</source> <target>Mostrar archivos excluidos temporalmente o filtrados</target> -<source>Save as default</source> -<target>Guardar como predeterminado</target> - <source>Filter</source> <target>Filtro</target> @@ -1954,12 +1990,6 @@ Esto garantiza un estado coherente incluso en caso de error grave. <source>Checking recycle bin failed for folder %x.</source> <target>Fallo al comprobar la papelera de reciclaje para la carpeta %x.</target> -<source>The following XML elements could not be read:</source> -<target>No se pueden leer los siguientes elementos XML:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>El archivo de configuración %x está incompleto. Los elementos ausentes se definirán con valores predeterminados.</target> - <source>Prepare installation</source> <target>Preparar la instalación</target> diff --git a/FreeFileSync/Build/Languages/swedish.lng b/FreeFileSync/Build/Languages/swedish.lng index ee21b7ee..91965af7 100755 --- a/FreeFileSync/Build/Languages/swedish.lng +++ b/FreeFileSync/Build/Languages/swedish.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Endast attribut skiljer objekten åt</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Namnet %x, används av mer än ett objekt, i mappen.</target> + <source>Resolving symbolic link %x</source> <target>Översätter den symboliska länken %x</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Tidsstämpeltolerans</target> -<source>Folder access timeout</source> -<target>Tidsgräns för mappåtkomst</target> - <source>Run with background priority</source> <target>Kör med bakgrundsprioritet</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Uppdatera attribut på höger sida</target> -<source>Warning</source> -<target>Varning</target> +<source>Errors:</source> +<target>Fel:</target> + +<source>Warnings:</source> +<target>Varningar:</target> <source>Items processed:</source> <target>Processade poster:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Total tid:</target> +<source>Warning</source> +<target>Varning</target> + <source>Stopped</source> <target>Stoppad</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Tolkningsfel på filen %x, rad %y, kolumn %z.</target> +<source>Services</source> +<target>Tjänster</target> + +<source>Show All</source> +<target>Visa all</target> + +<source>Hide Others</source> +<target>Dölj andra</target> + +<source>Hide %x</source> +<target>Dölj %x</target> + +<source>Quit %x</source> +<target>Avsluta %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Kan inte ange mapplås för följanda mappar:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>Kan inte läsa mappen %x.</target> -<source>/sec</source> -<target>/s</target> +<source>%x/sec</source> +<target>%x/sek</target> -<source>%x items/sec</source> -<target>%x objekt/sek.</target> +<source>%x items</source> +<target>%x objekt</target> <source>Show in Explorer</source> <target>Visa i Utforskaren</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Skapar databas...</target> -<source>Searching for excess file versions:</source> -<target>Söker efter överflödiga filversioner:</target> +<source>Searching for old file versions:</source> +<target>Söker efter gamla filversioner:</target> -<source>Removing excess file versions:</source> -<target>Tar bort överflödiga filversioner:</target> +<source>Removing old file versions:</source> +<target>Tar bort gamla filversioner:</target> <source>Unable to create time stamp for versioning:</source> <target>Kunde inte skapa tidsstämpel för versionshantering:</target> @@ -585,6 +606,24 @@ Aktuell: %y byte <source>Unable to move %x to the recycle bin.</source> <target>Kan inte att flytta %x till papperskorgen.</target> +<source>Unable to access %x.</source> +<target>Kan inte komma åt %x.</target> + +<source>Authentication completed.</source> +<target>Autentisering slutförd.</target> + +<source>Authentication failed.</source> +<target>Autentisering misslyckades.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Du kan nu stänga denna sida och fortsätta med FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Servern returnerade ett fel:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Kan inte avgöra ledigt utrymme på %x.</target> + <source>Cannot find %x.</source> <target>Kan inte hitta %x.</target> @@ -597,18 +636,12 @@ Aktuell: %y byte <source>Cannot delete symbolic link %x.</source> <target>Kan inte ta bort den symboliska länken %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Kan inte avgöra ledigt utrymme på %x.</target> - <source>Incorrect command line:</source> <target>Felaktig kommandorad:</target> <source>The server does not support authentication via %x.</source> <target>Servern stödjer inte autentisering via %x.</target> -<source>Unable to access %x.</source> -<target>Kan inte komma åt %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Aktuell: %y byte <source>Failed to open SFTP channel number %x.</source> <target>Kunde inte öppna SFTP-kanal nummer %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 byte</pluralform> -<pluralform>%x byte</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Dra && släpp</target> @@ -764,6 +779,24 @@ Kommandot triggas om: <source>&Retry</source> <target>&Försök igen</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 byte</pluralform> +<pluralform>%x byte</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Läser in...</target> @@ -909,7 +942,7 @@ Kommandot triggas om: <target>Spara som &batch-fil...</target> <source>Show &log</source> -<target></target> +<target>Visa &logg</target> <source>Start &comparison</source> <target>&Jämför</target> @@ -971,9 +1004,6 @@ Kommandot triggas om: <source>Access online storage</source> <target>Anslut fjärrlagring</target> -<source>Swap sides</source> -<target>Byt sida</target> - <source>Close search bar</source> <target>Stäng sökfältet</target> @@ -998,6 +1028,9 @@ Kommandot triggas om: <source>View type:</source> <target>Visningstyp:</target> +<source>Save as default</source> +<target>Spara som standard</target> + <source>Select view:</source> <target>Välj vy:</target> @@ -1055,6 +1088,15 @@ Kommandot triggas om: <source>Handle daylight saving time</source> <target>Hantera sommartid</target> +<source>Ignore errors</source> +<target>Ignorera fel</target> + +<source>Retry count:</source> +<target>Antal försök:</target> + +<source>Delay (in seconds):</source> +<target>Fördröjning (i sekunder):</target> + <source>Performance improvements:</source> <target>Prestandaförbättringar:</target> @@ -1129,17 +1171,11 @@ Kommandot triggas om: <source>Last x days:</source> <target>Senaste x dagarna:</target> -<source>Ignore errors</source> -<target>Ignorera fel</target> - -<source>Retry count:</source> -<target>Antal försök:</target> - -<source>Delay (in seconds):</source> -<target>Fördröjning (i sekunder):</target> +<source>&Override default log path:</source> +<target>&Åsidosätt standard loggsökväg:</target> -<source>Run a command after synchronization:</source> -<target>Kör ett kommando efter synkronisering:</target> +<source>Run a command:</source> +<target>Kör ett kommando:</target> <source>OK</source> <target>OK</target> @@ -1189,6 +1225,9 @@ Kommandot triggas om: <source>Directory on server:</source> <target>Målmapp på servern:</target> +<source>Access timeout (in seconds):</source> +<target>Åtkomsttidsgräns (i sekunder):</target> + <source>SFTP channels per connection:</source> <target>SFTP-kanaler per anslutning:</target> @@ -1267,12 +1306,6 @@ Kommandot triggas om: <source>Stop synchronization at first error</source> <target>Stoppa synkroniseringen vid första fel som uppstår</target> -<source>Save log:</source> -<target>Spara logg:</target> - -<source>Limit number of log files:</source> -<target>Begränsa antalet loggfiler:</target> - <source>How can I schedule a batch job?</source> <target>Hur schemalägger jag en batch-fil?</target> @@ -1309,8 +1342,11 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Visa alla permanent dolda dialoger och varningsmeddelanden igen</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Standard loggsökväg:</target> + +<source>&Delete logs after x days:</source> +<target>&Ta bort loggar efter x dagar:</target> <source>Customize context menu:</source> <target>Anpassad kontextmeny:</target> @@ -1321,8 +1357,8 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>&Default</source> <target>&Standard</target> -<source>Feedback and suggestions are welcome</source> -<target>Återkoppling och förslag är välkommna</target> +<source>Feedback and suggestions are welcome:</source> +<target>Återkoppling och förslag välkomnas:</target> <source>Home page</source> <target>Hemsida</target> @@ -1348,8 +1384,8 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Source code written in C++ using:</source> <target>Källkod skriven i C++ med hjälp av:</target> -<source>Published under the GNU General Public License</source> -<target>Publiserad under GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Publicerat under GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Tack för översättning:</target> @@ -1406,7 +1442,7 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <target>Info</target> <source>No log entries</source> -<target></target> +<target>Inga loggposter</target> <source>Select all</source> <target>Markera alla</target> @@ -1432,6 +1468,9 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Overview</source> <target>Översikt</target> +<source>Swap sides</source> +<target>Byt sida</target> + <source>Show "%x"</source> <target>Visa "%x"</target> @@ -1606,9 +1645,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Show filtered or temporarily excluded files</source> <target>Dölj filtrerade eller temporärt undantagna filer</target> -<source>Save as default</source> -<target>Spara som standard</target> - <source>Filter</source> <target>Filter</target> @@ -1954,12 +1990,6 @@ Detta garanterar ett konsekvent tillstånd även vid allvarliga fel. <source>Checking recycle bin failed for folder %x.</source> <target>Kontroll av papperskorgen misslyckades för %x.</target> -<source>The following XML elements could not be read:</source> -<target>Följande XML-element kunde inte läsas:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Konfigurationsfilen %x är inte komplett. De saknade elementen kommer att anges till standardvärdet.</target> - <source>Prepare installation</source> <target>Förbered installation</target> diff --git a/FreeFileSync/Build/Languages/turkish.lng b/FreeFileSync/Build/Languages/turkish.lng index e4c3a9fd..d06e670f 100755 --- a/FreeFileSync/Build/Languages/turkish.lng +++ b/FreeFileSync/Build/Languages/turkish.lng @@ -160,6 +160,9 @@ <source>Items differ in attributes only</source> <target>Yalnız öznitelikleri farklı olan ögeler</target> +<source>The name %x is used by more than one item in the folder.</source> +<target></target> + <source>Resolving symbolic link %x</source> <target>%x sembolik bağlantısı çözümleniyor</target> @@ -187,9 +190,6 @@ <source>File time tolerance</source> <target>Yok sayılacak dosya zamanı farkı</target> -<source>Folder access timeout</source> -<target>Klasör erişimi zaman aşımı</target> - <source>Run with background priority</source> <target>Artalan önceliği ile çalışsın</target> @@ -334,8 +334,11 @@ <source>Update attributes on right</source> <target>Sağdaki öznitelikler güncellensin</target> -<source>Warning</source> -<target>Uyarı</target> +<source>Errors:</source> +<target></target> + +<source>Warnings:</source> +<target></target> <source>Items processed:</source> <target>İşlenen öge:</target> @@ -346,6 +349,9 @@ <source>Total time:</source> <target>Toplam süre:</target> +<source>Warning</source> +<target>Uyarı</target> + <source>Stopped</source> <target>Durduruldu</target> @@ -355,6 +361,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>%x dosyası işlenirken sorun çıktı, satır %y, sütun %z.</target> +<source>Services</source> +<target></target> + +<source>Show All</source> +<target></target> + +<source>Hide Others</source> +<target></target> + +<source>Hide %x</source> +<target></target> + +<source>Quit %x</source> +<target></target> + <source>Cannot set directory locks for the following folders:</source> <target>Şu klasörler kilitlenemedi:</target> @@ -370,11 +391,11 @@ <source>Cannot read directory %x.</source> <target>%x klasörü okunamadı.</target> -<source>/sec</source> -<target>/saniye</target> +<source>%x/sec</source> +<target></target> -<source>%x items/sec</source> -<target>%x öge/saniye</target> +<source>%x items</source> +<target></target> <source>Show in Explorer</source> <target>Tarayıcıda Görüntüle</target> @@ -523,11 +544,11 @@ <source>Generating database...</source> <target>Veritabanı oluşturuluyor...</target> -<source>Searching for excess file versions:</source> -<target>Fazla dosya sürümleri aranıyor:</target> +<source>Searching for old file versions:</source> +<target></target> -<source>Removing excess file versions:</source> -<target>Fazla dosya sürümleri siliniyor:</target> +<source>Removing old file versions:</source> +<target></target> <source>Unable to create time stamp for versioning:</source> <target>Sürümlendirme için zaman damgası oluşturulamadı:</target> @@ -585,6 +606,24 @@ Gerçekleşen: %y bayt <source>Unable to move %x to the recycle bin.</source> <target>%x geri dönüşüm kutusuna atılamadı.</target> +<source>Unable to access %x.</source> +<target>%x üzerine erişilemedi.</target> + +<source>Authentication completed.</source> +<target></target> + +<source>Authentication failed.</source> +<target></target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target></target> + +<source>The server returned an error:</source> +<target></target> + +<source>Cannot determine free disk space for %x.</source> +<target>%x için boş disk alanı belirlenemedi.</target> + <source>Cannot find %x.</source> <target>%x bulunamadı.</target> @@ -597,18 +636,12 @@ Gerçekleşen: %y bayt <source>Cannot delete symbolic link %x.</source> <target>%x sembolik bağlantısı silinemedi.</target> -<source>Cannot determine free disk space for %x.</source> -<target>%x için boş disk alanı belirlenemedi.</target> - <source>Incorrect command line:</source> <target>Satırdaki komut geçersiz:</target> <source>The server does not support authentication via %x.</source> <target>Sunucu %x üzerinden kimlik doğrulamasını desteklemiyor.</target> -<source>Unable to access %x.</source> -<target>%x üzerine erişilemedi.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -633,24 +666,6 @@ Gerçekleşen: %y bayt <source>Failed to open SFTP channel number %x.</source> <target>%x SFTP kanal numarası açılamadı.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>1 bayt</pluralform> -<pluralform>%x bayt</pluralform> -</target> - -<source>%x MB</source> -<target>%x MB</target> - -<source>%x KB</source> -<target>%x KB</target> - -<source>%x GB</source> -<target>%x GB</target> - <source>Drag && drop</source> <target>Dosyaları sürükleyip buraya bırakın</target> @@ -764,6 +779,24 @@ Komut şu durumlarda yürütülür: <source>&Retry</source> <target>&Yeniden Dene</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>1 bayt</pluralform> +<pluralform>%x bayt</pluralform> +</target> + +<source>%x MB</source> +<target>%x MB</target> + +<source>%x KB</source> +<target>%x KB</target> + +<source>%x GB</source> +<target>%x GB</target> + <source>Loading...</source> <target>Yükleniyor...</target> @@ -971,9 +1004,6 @@ Komut şu durumlarda yürütülür: <source>Access online storage</source> <target>Çevrimiçi Depolama Erişimi</target> -<source>Swap sides</source> -<target>Sağ ve Sol Tarafları Değiştir</target> - <source>Close search bar</source> <target>Arama Çubuğunu Kapat</target> @@ -998,6 +1028,9 @@ Komut şu durumlarda yürütülür: <source>View type:</source> <target>Görünüm Kipi:</target> +<source>Save as default</source> +<target>Varsayılan Olarak Kaydet</target> + <source>Select view:</source> <target>Eşit Dosya Görünümü:</target> @@ -1055,6 +1088,15 @@ Komut şu durumlarda yürütülür: <source>Handle daylight saving time</source> <target>Yaz saati hakkında bilgiler</target> +<source>Ignore errors</source> +<target>Sorunlar yok sayılsın</target> + +<source>Retry count:</source> +<target>Deneme Sayısı:</target> + +<source>Delay (in seconds):</source> +<target>Bekleme (saniye):</target> + <source>Performance improvements:</source> <target>Başarım İyileştirmeleri:</target> @@ -1129,17 +1171,11 @@ Komut şu durumlarda yürütülür: <source>Last x days:</source> <target>Son x Gün:</target> -<source>Ignore errors</source> -<target>Sorunlar yok sayılsın</target> - -<source>Retry count:</source> -<target>Deneme Sayısı:</target> - -<source>Delay (in seconds):</source> -<target>Bekleme (saniye):</target> +<source>&Override default log path:</source> +<target></target> -<source>Run a command after synchronization:</source> -<target>Eşitleme sonrası yürütülecek komut:</target> +<source>Run a command:</source> +<target></target> <source>OK</source> <target>Tamam</target> @@ -1189,6 +1225,9 @@ Komut şu durumlarda yürütülür: <source>Directory on server:</source> <target>Sunucudaki Klasör:</target> +<source>Access timeout (in seconds):</source> +<target></target> + <source>SFTP channels per connection:</source> <target>Bir Bağlantı için SFTP Kanalı Sayısı:</target> @@ -1267,12 +1306,6 @@ Komut şu durumlarda yürütülür: <source>Stop synchronization at first error</source> <target>Çıkan ilk sorunda eşitleme durdurulsun</target> -<source>Save log:</source> -<target>İşlem Günlüğü Kaydedilsin:</target> - -<source>Limit number of log files:</source> -<target>Günlük dosyaları sayısı sınırlaması:</target> - <source>How can I schedule a batch job?</source> <target>Toplu İşlem Zamanlaması Hakkında Bilgiler</target> @@ -1309,7 +1342,10 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Show all permanently hidden dialogs and warning messages again</source> <target>Kalıcı olarak gizlenmiş tüm ileti ve uyarılar yeniden görüntülenir</target> -<source>Remove old log files after x days:</source> +<source>Default log path:</source> +<target></target> + +<source>&Delete logs after x days:</source> <target></target> <source>Customize context menu:</source> @@ -1321,8 +1357,8 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>&Default</source> <target>&Varsayılan</target> -<source>Feedback and suggestions are welcome</source> -<target>Öneri ve geri bildirimlerinizi bekleriz</target> +<source>Feedback and suggestions are welcome:</source> +<target></target> <source>Home page</source> <target>Ana Sayfa</target> @@ -1348,8 +1384,8 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Source code written in C++ using:</source> <target>Kaynak kodu C++ kullanılarak yazılmıştır:</target> -<source>Published under the GNU General Public License</source> -<target>GNU Genel Kamu Lisansı koşulları altında yayınlanmıştır</target> +<source>Published under the GNU General Public License:</source> +<target></target> <source>Many thanks for localization:</source> <target>Çeviriler için çok teşekkürler:</target> @@ -1432,6 +1468,9 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Overview</source> <target>Genel</target> +<source>Swap sides</source> +<target>Sağ ve Sol Tarafları Değiştir</target> + <source>Show "%x"</source> <target>"%x" paneli görüntülensin</target> @@ -1606,9 +1645,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Show filtered or temporarily excluded files</source> <target>Süzülmüş ya da geçici olarak katılmayan dosyaları görüntüler ya da gizler</target> -<source>Save as default</source> -<target>Varsayılan Olarak Kaydet</target> - <source>Filter</source> <target>Süzme</target> @@ -1954,12 +1990,6 @@ Bu yöntem, ciddi bir sorun çıkması durumunda bile işlemin tutarlı olarak y <source>Checking recycle bin failed for folder %x.</source> <target>%x klasörü için Geri Dönüşüm Kutusu denetlenemedi.</target> -<source>The following XML elements could not be read:</source> -<target>Şu XML bileşenleri okunamadı:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>%x yapılandırma dosyası tam değil. Eksik bileşenler için varsayılan değerler kullanılacak.</target> - <source>Prepare installation</source> <target>Kuruluma hazırlanıyor</target> diff --git a/FreeFileSync/Build/Languages/ukrainian.lng b/FreeFileSync/Build/Languages/ukrainian.lng index a095fe89..b3d31637 100755 --- a/FreeFileSync/Build/Languages/ukrainian.lng +++ b/FreeFileSync/Build/Languages/ukrainian.lng @@ -161,6 +161,9 @@ <source>Items differ in attributes only</source> <target>Елементи відрізняються тільки атрибутами</target> +<source>The name %x is used by more than one item in the folder.</source> +<target>Ім'я %x використовується більше ніж одним елементом у папці.</target> + <source>Resolving symbolic link %x</source> <target>Вирішення символьного посилання %x</target> @@ -188,9 +191,6 @@ <source>File time tolerance</source> <target>Толеранс часу файлу</target> -<source>Folder access timeout</source> -<target>Таймаут доступу до папки</target> - <source>Run with background priority</source> <target>Запустити з фоновим пріоритетом</target> @@ -336,8 +336,11 @@ <source>Update attributes on right</source> <target>Оновити атрибути праворуч</target> -<source>Warning</source> -<target>Увага</target> +<source>Errors:</source> +<target>Помилки:</target> + +<source>Warnings:</source> +<target>Попередження:</target> <source>Items processed:</source> <target>Елементів оброблено:</target> @@ -348,6 +351,9 @@ <source>Total time:</source> <target>Загальний час:</target> +<source>Warning</source> +<target>Увага</target> + <source>Stopped</source> <target>Зупинено</target> @@ -357,6 +363,21 @@ <source>Error parsing file %x, row %y, column %z.</source> <target>Помилка розбору файлу %x, рядок %y, колонка %z.</target> +<source>Services</source> +<target>Служби</target> + +<source>Show All</source> +<target>Показати Усі</target> + +<source>Hide Others</source> +<target>Сховати Інші</target> + +<source>Hide %x</source> +<target>Сховати %x</target> + +<source>Quit %x</source> +<target>Вихід %x</target> + <source>Cannot set directory locks for the following folders:</source> <target>Неможливо встановити блокування каталогів для таких папок:</target> @@ -373,11 +394,11 @@ <source>Cannot read directory %x.</source> <target>Не вдається прочитати папку %x.</target> -<source>/sec</source> -<target>/сек</target> +<source>%x/sec</source> +<target>%x/сек</target> -<source>%x items/sec</source> -<target>%x елемента/сек</target> +<source>%x items</source> +<target>%x елементів</target> <source>Show in Explorer</source> <target>Показати у Провіднику</target> @@ -526,11 +547,11 @@ <source>Generating database...</source> <target>Створення бази даних...</target> -<source>Searching for excess file versions:</source> -<target>Пошук надлишкових версій файлів:</target> +<source>Searching for old file versions:</source> +<target>Пошук старих версій файлів:</target> -<source>Removing excess file versions:</source> -<target>Видалення надлишкових версій файлів:</target> +<source>Removing old file versions:</source> +<target>Видалення старих версій файлів:</target> <source>Unable to create time stamp for versioning:</source> <target>Не вдається створити часової мітки для версій:</target> @@ -588,6 +609,24 @@ Actual: %y bytes <source>Unable to move %x to the recycle bin.</source> <target>Не вдається перемістити %x до корзини.</target> +<source>Unable to access %x.</source> +<target>Не вдалося отримати доступ до %x.</target> + +<source>Authentication completed.</source> +<target>Автентифікація виконана.</target> + +<source>Authentication failed.</source> +<target>Автентифікація не виконана.</target> + +<source>You may close this page now and continue with FreeFileSync.</source> +<target>Ви можете закрити цю сторінку зараз і продовжити з FreeFileSync.</target> + +<source>The server returned an error:</source> +<target>Сервер вернув помилку:</target> + +<source>Cannot determine free disk space for %x.</source> +<target>Не вдається визначити об'єм вільного місця для %x.</target> + <source>Cannot find %x.</source> <target>Не вдається знайти %x.</target> @@ -600,18 +639,12 @@ Actual: %y bytes <source>Cannot delete symbolic link %x.</source> <target>Не вдалося вилучити символьне посилання %x.</target> -<source>Cannot determine free disk space for %x.</source> -<target>Не вдається визначити об'єм вільного місця для %x.</target> - <source>Incorrect command line:</source> <target>Неправильний командний рядок:</target> <source>The server does not support authentication via %x.</source> <target>Сервер не підтримує аутентифікацію за допомогою %x.</target> -<source>Unable to access %x.</source> -<target>Не вдалося отримати доступ до %x.</target> - <source> <pluralform>Operation timed out after 1 second.</pluralform> <pluralform>Operation timed out after %x seconds.</pluralform> @@ -638,25 +671,6 @@ Actual: %y bytes <source>Failed to open SFTP channel number %x.</source> <target>Не вдалося відкрити SFTP канал номер %x.</target> -<source> -<pluralform>1 byte</pluralform> -<pluralform>%x bytes</pluralform> -</source> -<target> -<pluralform>%x байт</pluralform> -<pluralform>%x байти</pluralform> -<pluralform>%x байтів</pluralform> -</target> - -<source>%x MB</source> -<target>%x МБ</target> - -<source>%x KB</source> -<target>%x КБ</target> - -<source>%x GB</source> -<target>%x ГБ</target> - <source>Drag && drop</source> <target>Drag && drop</target> @@ -770,6 +784,25 @@ The command is triggered if: <source>&Retry</source> <target>&Повторити</target> +<source> +<pluralform>1 byte</pluralform> +<pluralform>%x bytes</pluralform> +</source> +<target> +<pluralform>%x байт</pluralform> +<pluralform>%x байти</pluralform> +<pluralform>%x байтів</pluralform> +</target> + +<source>%x MB</source> +<target>%x МБ</target> + +<source>%x KB</source> +<target>%x КБ</target> + +<source>%x GB</source> +<target>%x ГБ</target> + <source>Loading...</source> <target>Завантаження...</target> @@ -916,7 +949,7 @@ The command is triggered if: <target>Зберегти як &пакетне завдання...</target> <source>Show &log</source> -<target></target> +<target>Показати &журнал</target> <source>Start &comparison</source> <target>Запуск по&рівняння</target> @@ -978,9 +1011,6 @@ The command is triggered if: <source>Access online storage</source> <target>Доступ до online сховища</target> -<source>Swap sides</source> -<target>Поміняти місцями</target> - <source>Close search bar</source> <target>Закрити панель пошуку</target> @@ -1005,6 +1035,9 @@ The command is triggered if: <source>View type:</source> <target>Тип перегляду:</target> +<source>Save as default</source> +<target>Зберегти як замовчування</target> + <source>Select view:</source> <target>Вибрати перегляд:</target> @@ -1062,6 +1095,15 @@ The command is triggered if: <source>Handle daylight saving time</source> <target>Перехід на літній час вручну</target> +<source>Ignore errors</source> +<target>Ігнорувати помилки</target> + +<source>Retry count:</source> +<target>Кількість спроб:</target> + +<source>Delay (in seconds):</source> +<target>Затримка (секунд):</target> + <source>Performance improvements:</source> <target>Підвищення продуктивності:</target> @@ -1136,17 +1178,11 @@ The command is triggered if: <source>Last x days:</source> <target>Останні x днів:</target> -<source>Ignore errors</source> -<target>Ігнорувати помилки</target> - -<source>Retry count:</source> -<target>Кількість спроб:</target> - -<source>Delay (in seconds):</source> -<target>Затримка (секунд):</target> +<source>&Override default log path:</source> +<target>&Перевизначити шлях журналу за замовчуванням:</target> -<source>Run a command after synchronization:</source> -<target>Виконати команду після синхронізації:</target> +<source>Run a command:</source> +<target>Запустити команду:</target> <source>OK</source> <target>OK</target> @@ -1196,6 +1232,9 @@ The command is triggered if: <source>Directory on server:</source> <target>Папка на сервері:</target> +<source>Access timeout (in seconds):</source> +<target>Тайм-аут доступу (у секундах):</target> + <source>SFTP channels per connection:</source> <target>SFTP канали на з'єднання:</target> @@ -1274,12 +1313,6 @@ The command is triggered if: <source>Stop synchronization at first error</source> <target>Зупинити синхронізацію при першій помилці</target> -<source>Save log:</source> -<target>Зберегти журнал:</target> - -<source>Limit number of log files:</source> -<target>Обмеження кількості файлів журналу:</target> - <source>How can I schedule a batch job?</source> <target>Як можна запланувати пакетне завдання?</target> @@ -1316,8 +1349,11 @@ This guarantees a consistent state even in case of a serious error. <source>Show all permanently hidden dialogs and warning messages again</source> <target>Показати всі сховані діалоги і повідомлення з попередженнями знову</target> -<source>Remove old log files after x days:</source> -<target></target> +<source>Default log path:</source> +<target>Шлях до журналу за замовчуванням:</target> + +<source>&Delete logs after x days:</source> +<target>&Видалити журнали після x днів:</target> <source>Customize context menu:</source> <target>Налаштування контекстного меню:</target> @@ -1328,8 +1364,8 @@ This guarantees a consistent state even in case of a serious error. <source>&Default</source> <target>&За замовчуванням</target> -<source>Feedback and suggestions are welcome</source> -<target>Відгуки та пропозиції вітаються</target> +<source>Feedback and suggestions are welcome:</source> +<target>Зворотній зв'язок і пропозиції вітаються:</target> <source>Home page</source> <target>Домашня сторінка</target> @@ -1355,8 +1391,8 @@ This guarantees a consistent state even in case of a serious error. <source>Source code written in C++ using:</source> <target>Код програми написаний на C++ з використанням:</target> -<source>Published under the GNU General Public License</source> -<target>Видано за ліцензією GNU General Public License</target> +<source>Published under the GNU General Public License:</source> +<target>Опубліковано за GNU General Public License:</target> <source>Many thanks for localization:</source> <target>Подяка за локалізацію:</target> @@ -1413,7 +1449,7 @@ This guarantees a consistent state even in case of a serious error. <target>Інформація</target> <source>No log entries</source> -<target></target> +<target>Немає записів журналу</target> <source>Select all</source> <target>Виділити все</target> @@ -1439,6 +1475,9 @@ This guarantees a consistent state even in case of a serious error. <source>Overview</source> <target>Огляд</target> +<source>Swap sides</source> +<target>Поміняти місцями</target> + <source>Show "%x"</source> <target>Показати "%x"</target> @@ -1617,9 +1656,6 @@ This guarantees a consistent state even in case of a serious error. <source>Show filtered or temporarily excluded files</source> <target>Показати відфільтровані чи тимчасово виключені елементи</target> -<source>Save as default</source> -<target>Зберегти як замовчування</target> - <source>Filter</source> <target>Фільтр</target> @@ -1970,12 +2006,6 @@ This guarantees a consistent state even in case of a serious error. <source>Checking recycle bin failed for folder %x.</source> <target>Перевірка корзини для папки %x не вдалася.</target> -<source>The following XML elements could not be read:</source> -<target>Наступні XML елементи не вдалося прочитати:</target> - -<source>Configuration file %x is incomplete. The missing elements will be set to their default values.</source> -<target>Файл конфігурації %x неповний. Елементи, яких не вистачає, будуть заповнені значеннями за замовчуванням.</target> - <source>Prepare installation</source> <target>Підготовка встановлення</target> diff --git a/FreeFileSync/Build/styles.gtk_rc b/FreeFileSync/Build/styles.gtk_rc index 36eecb1b..b13985a0 100755 --- a/FreeFileSync/Build/styles.gtk_rc +++ b/FreeFileSync/Build/styles.gtk_rc @@ -1,7 +1,7 @@ style "no-inner-border" { - GtkButton::focus-padding = 0 - GtkButton::inner-border = {0, 0, 0, 0} + GtkButton::inner-border = {0, 0, 0, 0} /*remove excessive borders on Gnome*/ + /*GtkButton::focus-padding = 0 => keep default: minor difference + looks better on KDE */ } class "GtkButton" style "no-inner-border" diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile index bd274138..8c666383 100755 --- a/FreeFileSync/Source/Makefile +++ b/FreeFileSync/Source/Makefile @@ -39,11 +39,11 @@ CPP_FILES+=base/dir_lock.cpp CPP_FILES+=base/ffs_paths.cpp CPP_FILES+=base/file_hierarchy.cpp CPP_FILES+=base/generate_logfile.cpp -CPP_FILES+=base/hard_filter.cpp CPP_FILES+=base/icon_buffer.cpp CPP_FILES+=base/icon_loader.cpp CPP_FILES+=base/localization.cpp CPP_FILES+=base/parallel_scan.cpp +CPP_FILES+=base/path_filter.cpp CPP_FILES+=base/process_xml.cpp CPP_FILES+=base/perf_check.cpp CPP_FILES+=base/resolve_path.cpp @@ -96,24 +96,24 @@ CPP_FILES+=../../wx+/popup_dlg_generated.cpp CPP_FILES+=../../wx+/zlib_wrap.cpp CPP_FILES+=../../xBRZ/src/xbrz.cpp -OBJECT_LIST = $(CPP_FILES:%.cpp=../Obj/FFS_GCC_Make_Release/ffs/src/%.o) +OBJ_FILES = $(CPP_FILES:%=../Obj/FFS_GCC_Make_Release/ffs/src/%.o) -all: ../Build/$(APPNAME) +all: ../Build/Bin/$(APPNAME) -../Build/$(APPNAME): $(OBJECT_LIST) +../Build/Bin/$(APPNAME): $(OBJ_FILES) g++ -o $@ $^ $(LINKFLAGS) -../Obj/FFS_GCC_Make_Release/ffs/src/%.o : %.cpp +../Obj/FFS_GCC_Make_Release/ffs/src/%.o : % mkdir -p $(dir $@) g++ $(CXXFLAGS) -c $< -o $@ clean: rm -rf ../Obj/FFS_GCC_Make_Release - rm -f ../Build/$(APPNAME) + rm -f ../Build/Bin/$(APPNAME) install: mkdir -p $(BINDIR) - cp ../Build/$(APPNAME) $(BINDIR) + cp ../Build/Bin/$(APPNAME) $(BINDIR) mkdir -p $(APPSHAREDIR) cp -R ../Build/Languages/ \ @@ -121,9 +121,9 @@ install: ../Build/gong.wav \ ../Build/harp.wav \ ../Build/Resources.zip \ - "../Build/User Manual.pdf" \ $(APPSHAREDIR) mkdir -p $(DOCSHAREDIR) cp ../../Changelog.txt $(DOCSHAREDIR)/CHANGELOG +# cp "../Build/User Manual.pdf" $(DOCSHAREDIR) gzip $(DOCSHAREDIR)/CHANGELOG diff --git a/FreeFileSync/Source/RealTimeSync/Makefile b/FreeFileSync/Source/RealTimeSync/Makefile index 900d2d22..d04aeb6f 100755 --- a/FreeFileSync/Source/RealTimeSync/Makefile +++ b/FreeFileSync/Source/RealTimeSync/Makefile @@ -37,21 +37,21 @@ CPP_FILES+=../../../wx+/popup_dlg.cpp CPP_FILES+=../../../wx+/popup_dlg_generated.cpp CPP_FILES+=../../../xBRZ/src/xbrz.cpp -OBJECT_LIST=$(CPP_FILES:%.cpp=../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o) +OBJ_FILES=$(CPP_FILES:%=../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o) -all: ../../Build/$(APPNAME) +all: ../../Build/Bin/$(APPNAME) -../../Build/$(APPNAME): $(OBJECT_LIST) +../../Build/Bin/$(APPNAME): $(OBJ_FILES) g++ -o $@ $^ $(LINKFLAGS) -../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o : %.cpp +../../Obj/RTS_GCC_Make_Release/ffs/src/rts/%.o : % mkdir -p $(dir $@) g++ $(CXXFLAGS) -c $< -o $@ clean: rm -rf ../../Obj/RTS_GCC_Make_Release - rm -f ../../Build/$(APPNAME) + rm -f ../../Build/Bin/$(APPNAME) install: mkdir -p $(BINDIR) - cp ../../Build/$(APPNAME) $(BINDIR) + cp ../../Build/Bin/$(APPNAME) $(BINDIR) diff --git a/FreeFileSync/Source/RealTimeSync/application.cpp b/FreeFileSync/Source/RealTimeSync/application.cpp index 6047468f..af556ace 100755 --- a/FreeFileSync/Source/RealTimeSync/application.cpp +++ b/FreeFileSync/Source/RealTimeSync/application.cpp @@ -32,7 +32,6 @@ IMPLEMENT_APP(Application); namespace { - const wxEventType EVENT_ENTER_EVENT_LOOP = wxNewEventType(); } @@ -126,8 +125,8 @@ int Application::OnRun() { fff::logFatalError(e.what()); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works! - const auto title = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); - wxSafeShowMessage(title, e.what()); + const auto titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); + std::cerr << utfTo<std::string>(titleFmt + SPACED_DASH) << e.what() << "\n"; return fff::FFS_RC_EXCEPTION; } //catch (...) -> let it crash and create mini dump!!! diff --git a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp index 298d55b6..200cdbcf 100755 --- a/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp +++ b/FreeFileSync/Source/RealTimeSync/folder_selector2.cpp @@ -36,7 +36,8 @@ void setFolderPath(const Zstring& dirpath, wxTextCtrl* txtCtrl, wxWindow& toolti tooltipWnd.SetToolTip(utfTo<wxString>(folderPathFmt)); if (staticText) //change static box label only if there is a real difference to what is shown in wxTextCtrl anyway - staticText->SetLabel(equalLocalPath(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ? wxString(_("Drag && drop")) : utfTo<wxString>(folderPathFmt)); + staticText->SetLabel(equalNativePath(appendSeparator(trimCpy(dirpath)), appendSeparator(folderPathFmt)) ? + wxString(_("Drag && drop")) : utfTo<wxString>(folderPathFmt)); } } diff --git a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp index e4dfcb56..2689955c 100755 --- a/FreeFileSync/Source/RealTimeSync/main_dlg.cpp +++ b/FreeFileSync/Source/RealTimeSync/main_dlg.cpp @@ -98,10 +98,12 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) Zstring currentConfigFile = cfgFileName; if (currentConfigFile.empty()) - { - if (!itemNotExisting(lastRunConfigPath_)) //existing/access error? => user should be informed about access errors - currentConfigFile = lastRunConfigPath_; - } + try + { + if (itemStillExists(lastRunConfigPath_)) //throw FileError + currentConfigFile = lastRunConfigPath_; + } + catch (FileError&) { currentConfigFile = lastRunConfigPath_; } //access error? => user should be informed bool loadCfgSuccess = false; if (!currentConfigFile.empty()) @@ -212,7 +214,7 @@ void MainDialog::OnStart(wxCommandEvent& event) Hide(); XmlRealConfig currentCfg = getConfiguration(); - const Zstring activeCfgFilePath = !equalLocalPath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); + const Zstring activeCfgFilePath = !equalNativePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); switch (runFolderMonitor(currentCfg, ::extractJobName(activeCfgFilePath))) { @@ -231,7 +233,7 @@ void MainDialog::OnStart(wxCommandEvent& event) void MainDialog::OnConfigSave(wxCommandEvent& event) { - const Zstring defaultFilePath = !activeConfigFile_.empty() && !equalLocalPath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstr("Realtime.ffs_real"); + const Zstring defaultFilePath = !activeConfigFile_.empty() && !equalNativePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstr("Realtime.ffs_real"); auto defaultFolder = utfTo<wxString>(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); auto defaultFileName = utfTo<wxString>(afterLast (defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)); @@ -290,7 +292,7 @@ void MainDialog::setLastUsedConfig(const Zstring& filepath) { activeConfigFile_ = filepath; - const Zstring activeCfgFilePath = !equalLocalPath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); + const Zstring activeCfgFilePath = !equalNativePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); if (!activeCfgFilePath.empty()) SetTitle(utfTo<wxString>(activeCfgFilePath)); @@ -308,7 +310,7 @@ void MainDialog::OnConfigNew(wxCommandEvent& event) void MainDialog::OnConfigLoad(wxCommandEvent& event) { - const Zstring activeCfgFilePath = !equalLocalPath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); + const Zstring activeCfgFilePath = !equalNativePath(activeConfigFile_, lastRunConfigPath_) ? activeConfigFile_ : Zstring(); wxFileDialog filePicker(this, wxString(), diff --git a/FreeFileSync/Source/RealTimeSync/monitor.cpp b/FreeFileSync/Source/RealTimeSync/monitor.cpp index 8d12080b..647a1114 100755 --- a/FreeFileSync/Source/RealTimeSync/monitor.cpp +++ b/FreeFileSync/Source/RealTimeSync/monitor.cpp @@ -22,8 +22,8 @@ const std::chrono::seconds FOLDER_EXISTENCE_CHECK_INTERVAL(1); //wait until all directories become available (again) + logs in network share -std::set<Zstring, LessLocalPath> waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw FileError - const std::function<void(const Zstring& folderPath)>& requestUiRefresh, std::chrono::milliseconds cbInterval) +std::set<Zstring, LessNativePath> waitForMissingDirs(const std::vector<Zstring>& folderPathPhrases, //throw FileError + const std::function<void(const Zstring& folderPath)>& requestUiRefresh, std::chrono::milliseconds cbInterval) { //early failure! check for unsupported folder paths: for (const Zstring& protoName : { Zstr("FTP"), Zstr("SFTP"), Zstr("MTP") }) @@ -39,7 +39,7 @@ std::set<Zstring, LessLocalPath> waitForMissingDirs(const std::vector<Zstring>& Zstring folderPathPhrase; std::future<bool> folderAvailable; }; - std::map<Zstring, FolderInfo, LessLocalPath> folderInfos; //folderPath => FolderInfo + std::map<Zstring, FolderInfo, LessNativePath> folderInfos; //folderPath => FolderInfo for (const Zstring& phrase : folderPathPhrases) { @@ -50,8 +50,8 @@ std::set<Zstring, LessLocalPath> waitForMissingDirs(const std::vector<Zstring>& folderInfos[folderPath] = { phrase, runAsync([folderPath]{ return dirAvailable(folderPath); }) }; } - std::set<Zstring, LessLocalPath> availablePaths; - std::set<Zstring, LessLocalPath> missingPathPhrases; + std::set<Zstring, LessNativePath> availablePaths; + std::set<Zstring, LessNativePath> missingPathPhrases; for (auto& [folderPath, folderInfo] : folderInfos) { std::future<bool>& folderAvailable = folderInfo.folderAvailable; @@ -118,7 +118,7 @@ struct WaitResult }; -WaitResult waitForChanges(const std::set<Zstring, LessLocalPath>& folderPaths, //throw FileError +WaitResult waitForChanges(const std::set<Zstring, LessNativePath>& folderPaths, //throw FileError const std::function<void(bool readyForSync)>& requestUiRefresh, std::chrono::milliseconds cbInterval) { assert(std::all_of(folderPaths.begin(), folderPaths.end(), [](const Zstring& folderPath) { return dirAvailable(folderPath); })); @@ -163,7 +163,7 @@ WaitResult waitForChanges(const std::set<Zstring, LessLocalPath>& folderPaths, / { std::vector<DirWatcher::Entry> changedItems = watcher->getChanges([&] { requestUiRefresh(false /*readyForSync*/); /*throw X*/ }, cbInterval); //throw FileError - erase_if(changedItems, [](const DirWatcher::Entry& e) + eraseIf(changedItems, [](const DirWatcher::Entry& e) { return endsWith(e.itemPath, Zstr(".ffs_tmp")) || //sync.8ea2.ffs_tmp @@ -222,7 +222,7 @@ void rts::monitorDirectories(const std::vector<Zstring>& folderPathPhrases, std: for (;;) try { - std::set<Zstring, LessLocalPath> folderPaths = waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { requestUiRefresh(&folderPath); }, cbInterval); //throw FileError + std::set<Zstring, LessNativePath> folderPaths = waitForMissingDirs(folderPathPhrases, [&](const Zstring& folderPath) { requestUiRefresh(&folderPath); }, cbInterval); //throw FileError //schedule initial execution (*after* all directories have arrived) auto nextExecTime = std::chrono::steady_clock::now() + delay; diff --git a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp index 667b480d..106c508b 100755 --- a/FreeFileSync/Source/RealTimeSync/tray_menu.cpp +++ b/FreeFileSync/Source/RealTimeSync/tray_menu.cpp @@ -247,7 +247,7 @@ private: rts::AbortReason rts::runFolderMonitor(const XmlRealConfig& config, const wxString& jobname) { std::vector<Zstring> dirNamesNonFmt = config.directories; - erase_if(dirNamesNonFmt, [](const Zstring& str) { return trimCpy(str).empty(); }); //remove empty entries WITHOUT formatting paths yet! + eraseIf(dirNamesNonFmt, [](const Zstring& str) { return trimCpy(str).empty(); }); //remove empty entries WITHOUT formatting paths yet! if (dirNamesNonFmt.empty()) { diff --git a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp index 1e8ca476..4cd8636f 100755 --- a/FreeFileSync/Source/RealTimeSync/xml_proc.cpp +++ b/FreeFileSync/Source/RealTimeSync/xml_proc.cpp @@ -124,7 +124,7 @@ void rts::readRealOrBatchConfig(const Zstring& filePath, XmlRealConfig& config, XmlIn in(doc); //read folder pairs - std::set<Zstring, LessLocalPath> uniqueFolders; + std::set<Zstring, LessNativePath> uniqueFolders; for (XmlIn inPair = in["FolderPairs"]["Pair"]; inPair; inPair.next()) { @@ -142,7 +142,7 @@ void rts::readRealOrBatchConfig(const Zstring& filePath, XmlRealConfig& config, //--------------------------------------------------------------------------------------- - erase_if(uniqueFolders, [](const Zstring& str) { return trimCpy(str).empty(); }); + eraseIf(uniqueFolders, [](const Zstring& str) { return trimCpy(str).empty(); }); config.directories.assign(uniqueFolders.begin(), uniqueFolders.end()); config.commandline = Zstr('"') + fff::getFreeFileSyncLauncherPath() + Zstr("\" \"") + filePath + Zstr('"'); } @@ -162,9 +162,9 @@ wxLanguage rts::getProgramLanguage() //throw FileError } catch (FileError&) { - if (!itemNotExisting(filePath)) //existing or access error - throw; - return fff::getSystemLanguage(); + if (!itemStillExists(filePath)) //throw FileError + return fff::getSystemLanguage(); + throw; } if (getXmlTypeNoThrow(doc) != RtsXmlType::GLOBAL) //noexcept diff --git a/FreeFileSync/Source/base/algorithm.cpp b/FreeFileSync/Source/base/algorithm.cpp index ff1bc787..e35f0bdf 100755 --- a/FreeFileSync/Source/base/algorithm.cpp +++ b/FreeFileSync/Source/base/algorithm.cpp @@ -192,21 +192,19 @@ bool fff::allElementsEqual(const FolderComparison& folderCmp) namespace { template <SelectedSide side> inline -bool matchesDbEntry(const FilePair& file, const InSyncFolder::FileList::value_type* dbFile, const std::vector<unsigned int>& ignoreTimeShiftMinutes) +bool matchesDbEntry(const FilePair& file, const InSyncFile* dbFile, const std::vector<unsigned int>& ignoreTimeShiftMinutes) { if (file.isEmpty<side>()) return !dbFile; else if (!dbFile) return false; - const Zstring& fileNameDb = dbFile->first; - const InSyncDescrFile& descrDb = SelectParam<side>::ref(dbFile->second.left, dbFile->second.right); + const InSyncDescrFile& descrDb = SelectParam<side>::ref(dbFile->left, dbFile->right); - return getUnicodeNormalForm(file.getItemName<side>()) == getUnicodeNormalForm(fileNameDb) && //detect changes in case (but ignore Unicode normal forms) - //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes it's modification date by up to 2 seconds - //we're not interested in "fileTimeTolerance" here! - sameFileTime(file.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes) && - file.getFileSize<side>() == dbFile->second.fileSize; + //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes it's modification date by up to 2 seconds + return //we're not interested in "fileTimeTolerance" here! + sameFileTime(file.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes) && + file.getFileSize<side>() == dbFile->fileSize; //note: we do *not* consider FileId here, but are only interested in *visual* changes. Consider user moving data to some other medium, this is not a change! } @@ -239,19 +237,17 @@ bool stillInSync(const InSyncFile& dbFile, CompareVariant compareVar, int fileTi //check whether database entry and current item match: *irrespective* of current comparison settings template <SelectedSide side> inline -bool matchesDbEntry(const SymlinkPair& symlink, const InSyncFolder::SymlinkList::value_type* dbSymlink, const std::vector<unsigned int>& ignoreTimeShiftMinutes) +bool matchesDbEntry(const SymlinkPair& symlink, const InSyncSymlink* dbSymlink, const std::vector<unsigned int>& ignoreTimeShiftMinutes) { if (symlink.isEmpty<side>()) return !dbSymlink; else if (!dbSymlink) return false; - const Zstring& linkNameDb = dbSymlink->first; - const InSyncDescrLink& descrDb = SelectParam<side>::ref(dbSymlink->second.left, dbSymlink->second.right); + const InSyncDescrLink& descrDb = SelectParam<side>::ref(dbSymlink->left, dbSymlink->right); - return getUnicodeNormalForm(symlink.getItemName<side>()) == getUnicodeNormalForm(linkNameDb) && - //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes its modification date by up to 2 seconds - sameFileTime(symlink.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes); + //respect 2 second FAT/FAT32 precision! copying a file to a FAT32 drive changes its modification date by up to 2 seconds + return sameFileTime(symlink.getLastWriteTime<side>(), descrDb.modTime, 2, ignoreTimeShiftMinutes); } @@ -281,16 +277,10 @@ bool stillInSync(const InSyncSymlink& dbLink, CompareVariant compareVar, int fil //check whether database entry and current item match: *irrespective* of current comparison settings template <SelectedSide side> inline -bool matchesDbEntry(const FolderPair& folder, const InSyncFolder::FolderList::value_type* dbFolder) +bool matchesDbEntry(const FolderPair& folder, const InSyncFolder* dbFolder) { - if (!dbFolder || dbFolder->second.status == InSyncFolder::DIR_STATUS_STRAW_MAN) - return folder.isEmpty<side>(); - else if (folder.isEmpty<side>()) - return false; - - const Zstring& folderNameDb = dbFolder->first; - - return getUnicodeNormalForm(folder.getItemName<side>()) == getUnicodeNormalForm(folderNameDb); + const bool haveDbEntry = dbFolder && dbFolder->status != InSyncFolder::DIR_STATUS_STRAW_MAN; + return haveDbEntry == !folder.isEmpty<side>(); } @@ -315,22 +305,22 @@ private: fileTimeTolerance_(baseFolder.getFileTimeTolerance()), ignoreTimeShiftMinutes_(baseFolder.getIgnoredTimeShift()) { - recurse(baseFolder, &dbFolder); + recurse(baseFolder, &dbFolder, &dbFolder); if ((!exLeftOnlyById_ .empty() || !exLeftOnlyByPath_ .empty()) && (!exRightOnlyById_.empty() || !exRightOnlyByPath_.empty())) detectMovePairs(dbFolder); } - void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolder) + void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) { for (FilePair& file : hierObj.refSubFiles()) { - auto getDbFileEntry = [&]() -> const InSyncFile* //evaluate lazily! + auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& fileName) -> const InSyncFile* { if (dbFolder) { - auto it = dbFolder->files.find(file.getItemNameAny()); + auto it = dbFolder->files.find(fileName); if (it != dbFolder->files.end()) return &it->second; } @@ -341,8 +331,8 @@ private: if (cat == FILE_LEFT_SIDE_ONLY) { - if (const InSyncFile* dbFile = getDbFileEntry()) - exLeftOnlyByPath_.emplace(dbFile, &file); + if (const InSyncFile* dbEntry = getDbEntry(dbFolderL, file.getItemName<LEFT_SIDE>())) + exLeftOnlyByPath_.emplace(dbEntry, &file); else if (!file.getFileId<LEFT_SIDE>().empty()) { auto rv = exLeftOnlyById_.emplace(file.getFileId<LEFT_SIDE>(), &file); @@ -352,8 +342,8 @@ private: } else if (cat == FILE_RIGHT_SIDE_ONLY) { - if (const InSyncFile* dbFile = getDbFileEntry()) - exRightOnlyByPath_.emplace(dbFile, &file); + if (const InSyncFile* dbEntry = getDbEntry(dbFolderR, file.getItemName<RIGHT_SIDE>())) + exRightOnlyByPath_.emplace(dbEntry, &file); else if (!file.getFileId<RIGHT_SIDE>().empty()) { auto rv = exRightOnlyById_.emplace(file.getFileId<RIGHT_SIDE>(), &file); @@ -365,15 +355,22 @@ private: for (FolderPair& folder : hierObj.refSubFolders()) { - const InSyncFolder* dbSubFolder = nullptr; //try to find corresponding database entry - if (dbFolder) + auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& folderName) -> const InSyncFolder* { - auto it = dbFolder->folders.find(folder.getItemNameAny()); - if (it != dbFolder->folders.end()) - dbSubFolder = &it->second; - } + if (dbFolder) + { + auto it = dbFolder->folders.find(folderName); + if (it != dbFolder->folders.end()) + return &it->second; + } + return nullptr; + }; + const InSyncFolder* dbEntryL = getDbEntry(dbFolderL, folder.getItemName<LEFT_SIDE>()); + const InSyncFolder* dbEntryR = dbEntryL; + if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>())) + dbEntryR = getDbEntry(dbFolderR, folder.getItemName<RIGHT_SIDE>()); - recurse(folder, dbSubFolder); + recurse(folder, dbEntryL, dbEntryR); } } @@ -400,10 +397,10 @@ private: } template <SelectedSide side> - static FilePair* getAssocFilePair(const InSyncFile& dbFile, - const std::unordered_map<AFS::FileId, FilePair*, StringHash>& exOneSideById, - const std::unordered_map<const InSyncFile*, FilePair*>& exOneSideByPath) + FilePair* getAssocFilePair(const InSyncFile& dbFile) const { + const std::unordered_map<AFS::FileId, FilePair*, StringHash>& exOneSideById = SelectParam<side>::ref(exLeftOnlyById_, exRightOnlyById_); + const std::unordered_map<const InSyncFile*, FilePair* >& exOneSideByPath = SelectParam<side>::ref(exLeftOnlyByPath_, exRightOnlyByPath_); { auto it = exOneSideByPath.find(&dbFile); if (it != exOneSideByPath.end()) @@ -426,9 +423,9 @@ private: void findAndSetMovePair(const InSyncFile& dbFile) const { if (stillInSync(dbFile, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)) - if (FilePair* fileLeftOnly = getAssocFilePair<LEFT_SIDE>(dbFile, exLeftOnlyById_, exLeftOnlyByPath_)) + if (FilePair* fileLeftOnly = getAssocFilePair<LEFT_SIDE>(dbFile)) if (sameSizeAndDate<LEFT_SIDE>(*fileLeftOnly, dbFile)) - if (FilePair* fileRightOnly = getAssocFilePair<RIGHT_SIDE>(dbFile, exRightOnlyById_, exRightOnlyByPath_)) + if (FilePair* fileRightOnly = getAssocFilePair<RIGHT_SIDE>(dbFile)) if (sameSizeAndDate<RIGHT_SIDE>(*fileRightOnly, dbFile)) if (fileLeftOnly ->getMoveRef() == nullptr && //don't let a row participate in two move pairs! fileRightOnly->getMoveRef() == nullptr) // @@ -489,22 +486,22 @@ private: ignoreTimeShiftMinutes_(baseFolder.getIgnoredTimeShift()) { //-> considering filter not relevant: - //if narrowing filter: all ok; if widening filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine) - - recurse(baseFolder, &dbFolder); + // if stricter filter than last time: all ok; + // if less strict filter (if file ex on both sides -> conflict, fine; if file ex. on one side: copy to other side: fine) + recurse(baseFolder, &dbFolder, &dbFolder); } - void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolder) const + void recurse(ContainerObject& hierObj, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) const { for (FilePair& file : hierObj.refSubFiles()) - processFile(file, dbFolder); + processFile(file, dbFolderL, dbFolderR); for (SymlinkPair& link : hierObj.refSubLinks()) - processSymlink(link, dbFolder); + processSymlink(link, dbFolderL, dbFolderR); for (FolderPair& folder : hierObj.refSubFolders()) - processDir(folder, dbFolder); + processDir(folder, dbFolderL, dbFolderR); } - void processFile(FilePair& file, const InSyncFolder* dbFolder) const + void processFile(FilePair& file, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) const { const CompareFilesResult cat = file.getCategory(); if (cat == FILE_EQUAL) @@ -518,72 +515,88 @@ private: //#################################################################################### //try to find corresponding database entry - const InSyncFolder::FileList::value_type* dbEntry = nullptr; - if (dbFolder) + auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& fileName) -> const InSyncFile* { - auto it = dbFolder->files.find(file.getItemNameAny()); - if (it != dbFolder->files.end()) - dbEntry = &*it; - } + if (dbFolder) + { + auto it = dbFolder->files.find(fileName); + if (it != dbFolder->files.end()) + return &it->second; + } + return nullptr; + }; + const InSyncFile* dbEntryL = getDbEntry(dbFolderL, file.getItemName<LEFT_SIDE>()); + const InSyncFile* dbEntryR = dbEntryL; + if (dbFolderL != dbFolderR || getUnicodeNormalForm(file.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(file.getItemName<RIGHT_SIDE>())) + dbEntryR = getDbEntry(dbFolderR, file.getItemName<RIGHT_SIDE>()); //evaluation - const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes_); - const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(file, dbEntry, ignoreTimeShiftMinutes_); + const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(file, dbEntryL, ignoreTimeShiftMinutes_); + const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(file, dbEntryR, ignoreTimeShiftMinutes_); if (changeOnLeft != changeOnRight) { //if database entry not in sync according to current settings! -> set direction based on sync status only! - if (dbEntry && !stillInSync(dbEntry->second, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)) - file.setSyncDirConflict(txtDbNotInSync); + if ((dbEntryL && !stillInSync(*dbEntryL, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)) || + (dbEntryR && !stillInSync(*dbEntryR, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))) + file.setSyncDirConflict(txtDbNotInSync_); else file.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); } else { if (changeOnLeft) - file.setSyncDirConflict(txtBothSidesChanged); + file.setSyncDirConflict(txtBothSidesChanged_); else - file.setSyncDirConflict(txtNoSideChanged); + file.setSyncDirConflict(txtNoSideChanged_); } } - void processSymlink(SymlinkPair& symlink, const InSyncFolder* dbFolder) const + void processSymlink(SymlinkPair& symlink, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) const { const CompareSymlinkResult cat = symlink.getLinkCategory(); if (cat == SYMLINK_EQUAL) return; //try to find corresponding database entry - const InSyncFolder::SymlinkList::value_type* dbEntry = nullptr; - if (dbFolder) + auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& linkName) -> const InSyncSymlink* { - auto it = dbFolder->symlinks.find(symlink.getItemNameAny()); - if (it != dbFolder->symlinks.end()) - dbEntry = &*it; - } + if (dbFolder) + { + auto it = dbFolder->symlinks.find(linkName); + if (it != dbFolder->symlinks.end()) + return &it->second; + } + return nullptr; + }; + const InSyncSymlink* dbEntryL = getDbEntry(dbFolderL, symlink.getItemName<LEFT_SIDE>()); + const InSyncSymlink* dbEntryR = dbEntryL; + if (dbFolderL != dbFolderR || getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>())) + dbEntryR = getDbEntry(dbFolderR, symlink.getItemName<RIGHT_SIDE>()); //evaluation - const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes_); - const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(symlink, dbEntry, ignoreTimeShiftMinutes_); + const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(symlink, dbEntryL, ignoreTimeShiftMinutes_); + const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(symlink, dbEntryR, ignoreTimeShiftMinutes_); if (changeOnLeft != changeOnRight) { //if database entry not in sync according to current settings! -> set direction based on sync status only! - if (dbEntry && !stillInSync(dbEntry->second, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)) - symlink.setSyncDirConflict(txtDbNotInSync); + if ((dbEntryL && !stillInSync(*dbEntryL, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_)) || + (dbEntryR && !stillInSync(*dbEntryR, cmpVar_, fileTimeTolerance_, ignoreTimeShiftMinutes_))) + symlink.setSyncDirConflict(txtDbNotInSync_); else symlink.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); } else { if (changeOnLeft) - symlink.setSyncDirConflict(txtBothSidesChanged); + symlink.setSyncDirConflict(txtBothSidesChanged_); else - symlink.setSyncDirConflict(txtNoSideChanged); + symlink.setSyncDirConflict(txtNoSideChanged_); } } - void processDir(FolderPair& folder, const InSyncFolder* dbFolder) const + void processDir(FolderPair& folder, const InSyncFolder* dbFolderL, const InSyncFolder* dbFolderR) const { const CompareDirResult cat = folder.getDirCategory(); @@ -595,43 +608,51 @@ private: //####################################################################################### //try to find corresponding database entry - const InSyncFolder::FolderList::value_type* dbEntry = nullptr; - if (dbFolder) + auto getDbEntry = [](const InSyncFolder* dbFolder, const Zstring& folderName) -> const InSyncFolder* { - auto it = dbFolder->folders.find(folder.getItemNameAny()); - if (it != dbFolder->folders.end()) - dbEntry = &*it; - } + if (dbFolder) + { + auto it = dbFolder->folders.find(folderName); + if (it != dbFolder->folders.end()) + return &it->second; + } + return nullptr; + }; + const InSyncFolder* dbEntryL = getDbEntry(dbFolderL, folder.getItemName<LEFT_SIDE>()); + const InSyncFolder* dbEntryR = dbEntryL; + if (dbFolderL != dbFolderR || getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>())) + dbEntryR = getDbEntry(dbFolderR, folder.getItemName<RIGHT_SIDE>()); if (cat != DIR_EQUAL) { //evaluation - const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(folder, dbEntry); - const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(folder, dbEntry); + const bool changeOnLeft = !matchesDbEntry< LEFT_SIDE>(folder, dbEntryL); + const bool changeOnRight = !matchesDbEntry<RIGHT_SIDE>(folder, dbEntryR); if (changeOnLeft != changeOnRight) { //if database entry not in sync according to current settings! -> set direction based on sync status only! - if (dbEntry && !stillInSync(dbEntry->second)) - folder.setSyncDirConflict(txtDbNotInSync); + if ((dbEntryL && !stillInSync(*dbEntryL)) || + (dbEntryR && !stillInSync(*dbEntryR))) + folder.setSyncDirConflict(txtDbNotInSync_); else folder.setSyncDir(changeOnLeft ? SyncDirection::RIGHT : SyncDirection::LEFT); } else { if (changeOnLeft) - folder.setSyncDirConflict(txtBothSidesChanged); + folder.setSyncDirConflict(txtBothSidesChanged_); else - folder.setSyncDirConflict(txtNoSideChanged); + folder.setSyncDirConflict(txtNoSideChanged_); } } - recurse(folder, dbEntry ? &dbEntry->second : nullptr); + recurse(folder, dbEntryL, dbEntryR); } - const std::wstring txtBothSidesChanged = _("Both sides have changed since last synchronization."); - const std::wstring txtNoSideChanged = _("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization."); - const std::wstring txtDbNotInSync = _("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings."); + const std::wstring txtBothSidesChanged_ = _("Both sides have changed since last synchronization."); + const std::wstring txtNoSideChanged_ = _("Cannot determine sync-direction:") + L" \n" + _("No change since last synchronization."); + const std::wstring txtDbNotInSync_ = _("Cannot determine sync-direction:") + L" \n" + _("The database entry is not in sync considering current settings."); const CompareVariant cmpVar_; const int fileTimeTolerance_; @@ -858,10 +879,10 @@ template <FilterStrategy strategy> class ApplyHardFilter { public: - static void execute(ContainerObject& hierObj, const HardFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); } + static void execute(ContainerObject& hierObj, const PathFilter& filterProcIn) { ApplyHardFilter(hierObj, filterProcIn); } private: - ApplyHardFilter(ContainerObject& hierObj, const HardFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); } + ApplyHardFilter(ContainerObject& hierObj, const PathFilter& filterProcIn) : filterProc(filterProcIn) { recurse(hierObj); } void recurse(ContainerObject& hierObj) const { @@ -902,7 +923,7 @@ private: recurse(folder); } - const HardFilter& filterProc; + const PathFilter& filterProc; }; @@ -1033,7 +1054,7 @@ void fff::applyFiltering(FolderComparison& folderCmp, const MainConfiguration& m const NormalizedFilter normFilter = normalizeFilters(mainCfg.globalFilter, it->localFilter); //"set" hard filter - ApplyHardFilter<STRATEGY_SET>::execute(baseFolder, *normFilter.nameFilter); + ApplyHardFilter<STRATEGY_SET>::execute(baseFolder, normFilter.nameFilter.ref()); //"and" soft filter addSoftFiltering(baseFolder, normFilter.timeSizeFilter); @@ -1109,15 +1130,15 @@ void fff::applyTimeSpanFilter(FolderComparison& folderCmp, time_t timeFrom, time } -std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, - const AbstractPath& basePathR, const HardFilter& filterR) +std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePathL, const PathFilter& filterL, + const AbstractPath& basePathR, const PathFilter& filterR) { if (!AFS::isNullPath(basePathL) && !AFS::isNullPath(basePathR)) { - if (AFS::getRootPath(basePathL) == AFS::getRootPath(basePathR)) + if (basePathL.afsDevice == basePathR.afsDevice) { - const std::vector<Zstring> relPathL = split(AFS::getRootRelativePath(basePathL), FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); - const std::vector<Zstring> relPathR = split(AFS::getRootRelativePath(basePathR), FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); + const std::vector<Zstring> relPathL = split(basePathL.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); + const std::vector<Zstring> relPathR = split(basePathR.afsPath.value, FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); const bool leftParent = relPathL.size() <= relPathR.size(); @@ -1129,12 +1150,12 @@ std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePat Zstring relDirPath; std::for_each(relPathC.begin() + relPathP.size(), relPathC.end(), [&](const Zstring& itemName) { - relDirPath = AFS::appendPaths(relDirPath, itemName, FILE_NAME_SEPARATOR); + relDirPath = nativeAppendPaths(relDirPath, itemName); }); const AbstractPath& basePathP = leftParent ? basePathL : basePathR; const AbstractPath& basePathC = leftParent ? basePathR : basePathL; - const HardFilter& filterP = leftParent ? filterL : filterR; + const PathFilter& filterP = leftParent ? filterL : filterR; //if there's a dependency, check if the sub directory is (fully) excluded via filter //=> easy to check but still insufficient in general: // - one folder may have a *.txt include-filter, the other a *.lng include filter => no dependencies, but "childItemMightMatch = true" below! @@ -1150,10 +1171,10 @@ std::optional<PathDependency> fff::getPathDependency(const AbstractPath& basePat //############################################################################################################ -std::pair<std::wstring, int> fff::getSelectedItemsAsString(const std::vector<const FileSystemObject*>& selectionLeft, - const std::vector<const FileSystemObject*>& selectionRight) +std::pair<std::wstring, int> fff::getSelectedItemsAsString(std::span<const FileSystemObject* const> selectionLeft, + std::span<const FileSystemObject* const> selectionRight) { - //don't use wxString! its rather dumb linear allocation strategy brings perf down to a crawl! + //don't use wxString! its dumb linear allocation strategy brings perf down to a crawl! std::wstring fileList; // int totalDelCount = 0; @@ -1212,33 +1233,22 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT } catch (FileError&) { - const AFS::PathStatus ps = AFS::getPathStatus(targetPath); //throw FileError - - if (ps.relPath.empty()) //already existing + try { + AFS::getItemType(targetPath); //throw FileError + //already existing! => if (deletionError) std::rethrow_exception(deletionError); + throw; } - else if (ps.relPath.size() > 1) //parent folder missing - { - //notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(*AFS::getParentFolderPath(targetPath))); -> useful? + catch (FileError&) {} //not yet existing or access error - AbstractPath intermediatePath = ps.existingPath; - std::for_each(ps.relPath.begin(), ps.relPath.end() - 1, [&](const Zstring& itemName) - { - AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError - statReporter.reportDelta(1, 0); - callback.requestUiRefresh(); //throw X - }); - //potential future issue when adding multithreading support: intermediate folders might already exist - //potential future issue 2: folder created by parallel thread just after failure => ps->relPath.size() == 1, but need retry! - //see abstract.cpp; AFS::createFolderIfMissingRecursion() + if (const std::optional<AbstractPath> targetParentPath = AFS::getParentPath(targetPath)) + AFS::createFolderIfMissingRecursion(*targetParentPath); //throw FileError - //retry: - copyItemPlain(nullptr /*deleteTargetItem*/); //throw FileError - return; - } - throw; + //retry: + copyItemPlain(nullptr /*deleteTargetItem*/); //throw FileError + return; } }; @@ -1253,34 +1263,10 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT { ItemStatReporter<> statReporter(1, 0, callback); notifyItemCopy(txtCreatingFolder, AFS::getDisplayPath(targetPath)); - try - { - //target existing: undefined behavior! (fail/overwrite) - AFS::createFolderPlain(targetPath); //throw FileError - statReporter.reportDelta(1, 0); - } - catch (FileError&) - { - const AFS::PathStatus ps = AFS::getPathStatus(targetPath); //throw FileError - if (ps.existingType == AFS::ItemType::FILE) - throw; - - if (ps.relPath.size() == 1) //don't repeat the very same createFolderPlain() call from above! - throw; - //folder might already exist: see creation of intermediate directories below - - AbstractPath intermediatePath = ps.existingPath; - for (const Zstring& itemName : ps.relPath) - { - AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError - statReporter.reportDelta(1, 0); - callback.requestUiRefresh(); //throw X - } - //potential future issue when adding multithreading support: intermediate folders might already exist - //potential future issue 2: parent folder created by parallel thread just after failure => ps->relPath.size() == 1, but need retry! - //see abstract.cpp; AFS::createFolderIfMissingRecursion() - } + AFS::createFolderIfMissingRecursion(targetPath); //throw FileError + statReporter.reportDelta(1, 0); + //folder might already exist: see creation of intermediate directories below }, [&](const FilePair& file) @@ -1325,18 +1311,18 @@ void copyToAlternateFolderFrom(const std::vector<const FileSystemObject*>& rowsT } -void fff::copyToAlternateFolder(const std::vector<const FileSystemObject*>& rowsToCopyOnLeft, - const std::vector<const FileSystemObject*>& rowsToCopyOnRight, +void fff::copyToAlternateFolder(std::span<const FileSystemObject* const> rowsToCopyOnLeft, + std::span<const FileSystemObject* const> rowsToCopyOnRight, const Zstring& targetFolderPathPhrase, bool keepRelPaths, bool overwriteIfExists, WarningDialogs& warnings, ProcessCallback& callback) { - std::vector<const FileSystemObject*> itemSelectionLeft = rowsToCopyOnLeft; - std::vector<const FileSystemObject*> itemSelectionRight = rowsToCopyOnRight; - erase_if(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); - erase_if(itemSelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); + std::vector<const FileSystemObject*> itemSelectionLeft (rowsToCopyOnLeft .begin(), rowsToCopyOnLeft .end()); + std::vector<const FileSystemObject*> itemSelectionRight(rowsToCopyOnRight.begin(), rowsToCopyOnRight.end()); + eraseIf(itemSelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed for correct stats! + eraseIf(itemSelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); // const int itemTotal = static_cast<int>(itemSelectionLeft.size() + itemSelectionRight.size()); int64_t bytesTotal = 0; @@ -1514,8 +1500,8 @@ void fff::deleteFromGridAndHD(const std::vector<FileSystemObject*>& rowsToDelete std::vector<FileSystemObject*> deleteLeft = rowsToDeleteOnLeft; std::vector<FileSystemObject*> deleteRight = rowsToDeleteOnRight; - erase_if(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed? - erase_if(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); //yes, for correct stats: + eraseIf(deleteLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); //needed? + eraseIf(deleteRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); //yes, for correct stats: const int itemCount = static_cast<int>(deleteLeft.size() + deleteRight.size()); callback.initNewPhase(itemCount, 0, ProcessCallback::PHASE_SYNCHRONIZING); //throw X @@ -1601,13 +1587,16 @@ bool fff::operator<(const FileDescriptor& lhs, const FileDescriptor& rhs) if (lhs.attr.fileSize != rhs.attr.fileSize) return lhs.attr.fileSize < rhs.attr.fileSize; - if (lhs.attr.fileId != rhs.attr.fileId) - return lhs.attr.fileId < rhs.attr.fileId; - if (lhs.attr.isFollowedSymlink != rhs.attr.isFollowedSymlink) return lhs.attr.isFollowedSymlink < rhs.attr.isFollowedSymlink; - return lhs.path < rhs.path; + if (lhs.attr.fileId != rhs.attr.fileId) + return lhs.attr.fileId < rhs.attr.fileId; + + if (!lhs.attr.fileId.empty()) + return false; //when (non-empty) file IDs match we don't have to check the path => pre-mature optimization? + else + return lhs.path < rhs.path; } @@ -1678,7 +1667,7 @@ void TempFileBuffer::createTempFiles(const std::set<FileDescriptor>& workLoad, P const Zstring fileName = AFS::getItemName(descr.path); - auto it = find_last(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "." + auto it = findLast(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "." const Zstring tempFileName = Zstring(fileName.begin(), it) + Zstr('-') + descrHash + Zstring(it, fileName.end()); const Zstring tempFilePath = appendSeparator(tempFolderPath_) + tempFileName; diff --git a/FreeFileSync/Source/base/algorithm.h b/FreeFileSync/Source/base/algorithm.h index a8facb9d..f7b2cc15 100755 --- a/FreeFileSync/Source/base/algorithm.h +++ b/FreeFileSync/Source/base/algorithm.h @@ -48,16 +48,16 @@ struct PathDependency AbstractPath basePathChild; Zstring relPath; //filled if child path is subfolder of parent path; empty if child path == parent path }; -std::optional<PathDependency> getPathDependency(const AbstractPath& basePathL, const HardFilter& filterL, - const AbstractPath& basePathR, const HardFilter& filterR); +std::optional<PathDependency> getPathDependency(const AbstractPath& basePathL, const PathFilter& filterL, + const AbstractPath& basePathR, const PathFilter& filterR); std::pair<std::wstring, int> getSelectedItemsAsString( //returns string with item names and total count of selected(!) items, NOT total files/dirs! - const std::vector<const FileSystemObject*>& selectionLeft, //all pointers need to be bound! - const std::vector<const FileSystemObject*>& selectionRight); // + std::span<const FileSystemObject* const> selectionLeft, //all pointers need to be bound! + std::span<const FileSystemObject* const> selectionRight); // //manual copy to alternate folder: -void copyToAlternateFolder(const std::vector<const FileSystemObject*>& rowsToCopyOnLeft, //all pointers need to be bound! - const std::vector<const FileSystemObject*>& rowsToCopyOnRight, // +void copyToAlternateFolder(std::span<const FileSystemObject* const> rowsToCopyOnLeft, //all pointers need to be bound! + std::span<const FileSystemObject* const> rowsToCopyOnRight, // const Zstring& targetFolderPathPhrase, bool keepRelPaths, bool overwriteIfExists, diff --git a/FreeFileSync/Source/base/application.cpp b/FreeFileSync/Source/base/application.cpp index 4707fd98..fc07e808 100755 --- a/FreeFileSync/Source/base/application.cpp +++ b/FreeFileSync/Source/base/application.cpp @@ -13,6 +13,7 @@ #include <wx+/app_main.h> #include <wx+/popup_dlg.h> #include <wx+/image_resources.h> +#include <wx/msgdlg.h> #include "comparison.h" #include "algorithm.h" #include "synchronization.h" @@ -55,8 +56,8 @@ bool Application::OnInit() //do not call wxApp::OnInit() to avoid using wxWidgets command line parser ::gtk_init(nullptr, nullptr); - //::gtk_rc_parse((getResourceDirPf() + "styles.gtk_rc").c_str()); //remove inner border from bitmap buttons - //=> looks bad on Suse Linux! + ::gtk_rc_parse((getResourceDirPf() + "styles.gtk_rc").c_str()); //remove excessive inner border from bitmap buttons + //Windows User Experience Interaction Guidelines: tool tips should have 5s timeout, info tips no timeout => compromise: wxToolTip::Enable(true); //yawn, a wxWidgets screw-up: wxToolTip::SetAutoPop is no-op if global tooltip window is not yet constructed: wxToolTip::Enable creates it @@ -115,8 +116,8 @@ int Application::OnRun() { logFatalError(e.what()); //it's not always possible to display a message box, e.g. corrupted stack, however low-level file output works! - const auto title = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); - wxSafeShowMessage(title, e.what()); + const auto titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + _("An exception occurred"); + std::cerr << utfTo<std::string>(titleFmt + SPACED_DASH) << e.what() << "\n"; return FFS_RC_EXCEPTION; } //catch (...) -> let it crash and create mini dump!!! @@ -151,10 +152,12 @@ void Application::launch(const std::vector<Zstring>& commandArgs) { logFatalError(utfTo<std::string>(msg)); - //error handling strategy unknown and no sync log output available at this point! => show message box + //error handling strategy unknown and no sync log output available at this point! auto titleFmt = copyStringTo<std::wstring>(wxTheApp->GetAppDisplayName()) + SPACED_DASH + title; - wxSafeShowMessage(titleFmt, msg); - + std::cerr << utfTo<std::string>(titleFmt + SPACED_DASH + msg) << "\n"; + //alternative0: std::wcerr: cannot display non-ASCII at all, so why does it exist??? + //alternative1: wxSafeShowMessage => NO console output on Debian x86, WTF! + //alternative2: wxMessageBox() => works, but we probably shouldn't block during command line usage raiseReturnCode(returnCode_, FFS_RC_ABORTED); }; @@ -202,35 +205,23 @@ void Application::launch(const std::vector<Zstring>& commandArgs) else if (equalAsciiNoCase(*it, optionLeftDir)) { if (++it == commandArgs.end() || isCommandLineOption(*it)) - { - notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionLeftDir)), _("Syntax error")); - return; - } + return notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionLeftDir)), _("Syntax error")); dirPathPhrasesLeft.push_back(*it); } else if (equalAsciiNoCase(*it, optionRightDir)) { if (++it == commandArgs.end() || isCommandLineOption(*it)) - { - notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionRightDir)), _("Syntax error")); - return; - } + return notifyFatalError(replaceCpy(_("A directory path is expected after %x."), L"%x", utfTo<std::wstring>(optionRightDir)), _("Syntax error")); dirPathPhrasesRight.push_back(*it); } else if (equalAsciiNoCase(*it, optionDirPair)) { if (++it == commandArgs.end() || isCommandLineOption(*it)) - { - notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error")); - return; - } + return notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error")); dirPathPhrasePairs.emplace_back(*it, Zstring()); if (++it == commandArgs.end() || isCommandLineOption(*it)) - { - notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error")); - return; - } + return notifyFatalError(replaceCpy(_("A left and a right directory path are expected after %x."), L"%x", utfTo<std::wstring>(optionDirPair)), _("Syntax error")); dirPathPhrasePairs.back().second = *it; } else if (equalAsciiNoCase(*it, optionSendTo)) @@ -283,10 +274,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) else if (fileAvailable(filePath + Zstr(".xml"))) filePath += Zstr(".xml"); else - { - notifyFatalError(replaceCpy(_("Cannot find file %x."), L"%x", fmtPath(filePath)), _("Error")); - return; - } + return notifyFatalError(replaceCpy(_("Cannot find file %x."), L"%x", fmtPath(filePath)), _("Error")); } try @@ -303,22 +291,17 @@ void Application::launch(const std::vector<Zstring>& commandArgs) globalConfigFile = filePath; break; case XML_TYPE_OTHER: - notifyFatalError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath)), _("Error")); - return; + return notifyFatalError(replaceCpy(_("File %x does not contain a valid configuration."), L"%x", fmtPath(filePath)), _("Error")); } } catch (const FileError& e) { - notifyFatalError(e.toString(), _("Error")); - return; + return notifyFatalError(e.toString(), _("Error")); } } if (dirPathPhrasesLeft.size() != dirPathPhrasesRight.size()) - { - notifyFatalError(_("Unequal number of left and right directories specified."), _("Syntax error")); - return; - } + return notifyFatalError(_("Unequal number of left and right directories specified."), _("Syntax error")); for (size_t i = 0; i < dirPathPhrasesLeft.size(); ++i) dirPathPhrasePairs.emplace_back(dirPathPhrasesLeft[i], dirPathPhrasesRight[i]); @@ -395,8 +378,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) } catch (const FileError& e) { - notifyFatalError(e.toString(), _("Error")); - return; + return notifyFatalError(e.toString(), _("Error")); } if (!replaceDirectories(batchCfg.mainCfg)) return; @@ -417,8 +399,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) } catch (const FileError& e) { - notifyFatalError(e.toString(), _("Error")); - return; + return notifyFatalError(e.toString(), _("Error")); } if (!replaceDirectories(guiCfg.mainCfg)) return; @@ -432,10 +413,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) else { if (!dirPathPhrasePairs.empty()) - { - notifyFatalError(_("Directories cannot be set for more than one configuration file."), _("Syntax error")); - return; - } + return notifyFatalError(_("Directories cannot be set for more than one configuration file."), _("Syntax error")); std::vector<Zstring> filePaths; for (const auto& [filePath, xmlType] : configFiles) @@ -453,8 +431,7 @@ void Application::launch(const std::vector<Zstring>& commandArgs) } catch (const FileError& e) { - notifyFatalError(e.toString(), _("Error")); - return; + return notifyFatalError(e.toString(), _("Error")); } runGuiMode(globalConfigFilePath, guiCfg, filePaths, !openForEdit /*startComparison*/); } @@ -518,13 +495,19 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat { std::wstring warningMsg; readConfig(globalConfigFilePath, globalCfg, warningMsg); //throw FileError - assert(warningMsg.empty()); //ignore parsing errors: should be migration problems only *cross-fingers* } - catch (const FileError& e) + catch (FileError&) { - if (!itemNotExisting(globalConfigFilePath)) //existing or access error + try + { + if (itemStillExists(globalConfigFilePath)) //throw FileError + throw; + } + catch (const FileError& e) + { return notifyError(e.toString(), FFS_RC_ABORTED); //abort sync! + } } try @@ -544,7 +527,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat // checkForUpdatePeriodically(globalCfg.lastUpdateCheck); //WinInet not working when FFS is running as a service!!! https://support.microsoft.com/en-us/kb/238425 - const std::map<AbstractPath, size_t>& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps; + const std::map<AfsDevice, size_t>& deviceParallelOps = batchCfg.mainCfg.deviceParallelOps; std::set<AbstractPath> logFilePathsToKeep; for (const ConfigFileItem& item : globalCfg.gui.mainDlg.cfgFileHistory) @@ -605,7 +588,7 @@ void runBatchMode(const Zstring& globalConfigFilePath, const XmlBatchConfig& bat //update last sync stats for the selected cfg file for (ConfigFileItem& cfi : globalCfg.gui.mainDlg.cfgFileHistory) - if (equalLocalPath(cfi.cfgFilePath, cfgFilePath)) + if (equalNativePath(cfi.cfgFilePath, cfgFilePath)) { if (r.finalStatus != SyncResult::ABORTED) cfi.lastSyncTime = std::chrono::system_clock::to_time_t(syncStartTime); diff --git a/FreeFileSync/Source/base/comparison.cpp b/FreeFileSync/Source/base/comparison.cpp index ce601ac8..26c33dca 100755 --- a/FreeFileSync/Source/base/comparison.cpp +++ b/FreeFileSync/Source/base/comparison.cpp @@ -34,9 +34,9 @@ std::vector<FolderPairCfg> fff::extractCompareCfg(const MainConfiguration& mainC const SyncConfig syncCfg = lpc.localSyncCfg ? *lpc.localSyncCfg : mainCfg.syncCfg; NormalizedFilter filter = normalizeFilters(mainCfg.globalFilter, lpc.localFilter); - //exclude the database file(s) sync.ffs_db, sync.x64.ffs_db, etc. and lock files + //exclude sync.ffs_db and lock files //=> can't put inside fff::parallelDeviceTraversal() which is also used by versioning - filter.nameFilter = filter.nameFilter->copyFilterAddingExclusion(Zstring(Zstr("*")) + SYNC_DB_FILE_ENDING + Zstr("\n*") + LOCK_FILE_ENDING); + filter.nameFilter = filter.nameFilter.ref().copyFilterAddingExclusion(Zstring(Zstr("*")) + SYNC_DB_FILE_ENDING + Zstr("\n*") + LOCK_FILE_ENDING); output.push_back( { @@ -68,7 +68,7 @@ struct ResolvedBaseFolders }; -ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCfgList, const std::map<AbstractPath, size_t>& deviceParallelOps, +ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCfgList, const std::map<AfsDevice, size_t>& deviceParallelOps, bool allowUserInteraction, bool& warnFolderNotExisting, ProcessCallback& callback /*throw X*/) @@ -78,25 +78,31 @@ ResolvedBaseFolders initializeBaseFolders(const std::vector<FolderPairCfg>& fpCf tryReportingError([&] { - std::set<AbstractPath> uniqueBaseFolders; - //support "retry" for environment variable and variable driver letter resolution! - output.resolvedPairs.clear(); + std::set<AbstractPath> baseFolders; + std::vector<std::pair<AbstractPath, AbstractPath>> folderPairs; + for (const FolderPairCfg& fpCfg : fpCfgList) { - AbstractPath folderPathLeft = createAbstractPath(fpCfg.folderPathPhraseLeft_); - AbstractPath folderPathRight = createAbstractPath(fpCfg.folderPathPhraseRight_); + folderPairs.emplace_back(createAbstractPath(fpCfg.folderPathPhraseLeft_), + createAbstractPath(fpCfg.folderPathPhraseRight_)); - uniqueBaseFolders.insert(folderPathLeft); - uniqueBaseFolders.insert(folderPathRight); - - output.resolvedPairs.push_back({ folderPathLeft, folderPathRight }); + baseFolders.insert(folderPairs.back().first); + baseFolders.insert(folderPairs.back().second); } - const FolderStatus status = getFolderStatusNonBlocking(uniqueBaseFolders, deviceParallelOps, //re-check *all* directories on each try! + const FolderStatus status = getFolderStatusNonBlocking(baseFolders, deviceParallelOps, //re-check *all* directories on each try! allowUserInteraction, callback); //throw X - output.existingBaseFolders = status.existing; - notExisting = status.notExisting; + output.resolvedPairs.clear(); + for (const auto& [folderPathL, folderPathR] : folderPairs) + output.resolvedPairs.push_back({ getNormalizedPath(status, folderPathL), getNormalizedPath(status, folderPathR)}); + + output.existingBaseFolders.clear(); + for (const AbstractPath& folderPath : status.existing) + output.existingBaseFolders.insert(getNormalizedPath(status, folderPath)); + + notExisting = status.notExisting; + if (!status.failedChecks.empty()) { std::wstring msg = _("Cannot find the following folders:") + L"\n"; @@ -134,7 +140,7 @@ class ComparisonBuffer { public: ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead, - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, int fileTimeTolerance, ProcessCallback& callback); @@ -155,12 +161,12 @@ private: std::map<DirectoryKey, DirectoryValue> directoryBuffer_; //contains only *existing* directories const int fileTimeTolerance_; ProcessCallback& cb_; - const std::map<AbstractPath, size_t> deviceParallelOps_; + const std::map<AfsDevice, size_t> deviceParallelOps_; }; ComparisonBuffer::ComparisonBuffer(const std::set<DirectoryKey>& foldersToRead, - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, int fileTimeTolerance, ProcessCallback& callback) : fileTimeTolerance_(fileTimeTolerance), cb_(callback), deviceParallelOps_(deviceParallelOps) @@ -248,6 +254,12 @@ Zstringw getDescrDiffMetaDate(const FileOrLinkPair& file) arrowRight + L" " + _("Date:") + L" " + formatUtcToLocalTime(file.template getLastWriteTime<RIGHT_SIDE>())); } + +Zstringw getConflictAmbiguousItemName(const Zstring& itemName) +{ + return copyStringTo<Zstringw>(replaceCpy(_("The name %x is used by more than one item in the folder."), L"%x", fmtPath(itemName))); +} + //----------------------------------------------------------------------------- void categorizeSymlinkByTime(SymlinkPair& symlink) @@ -261,7 +273,7 @@ void categorizeSymlinkByTime(SymlinkPair& symlink) //1. SYMLINK_EQUAL may only be set if short names match in case: InSyncFolder's mapping tables use short name as a key! see db_file.cpp //2. harmonize with "bool stillInSync()" in algorithm.cpp - if (getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE >()) == + if (getUnicodeNormalForm(symlink.getItemName< LEFT_SIDE>()) == getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>())) symlink.setCategory<FILE_EQUAL>(); else @@ -311,7 +323,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::compareByTimeSize(const Resolv //3. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h if (file->getFileSize<LEFT_SIDE>() == file->getFileSize<RIGHT_SIDE>()) { - if (getUnicodeNormalForm(file->getItemName<LEFT_SIDE >()) == + if (getUnicodeNormalForm(file->getItemName< LEFT_SIDE>()) == getUnicodeNormalForm(file->getItemName<RIGHT_SIDE>())) file->setCategory<FILE_EQUAL>(); else @@ -369,7 +381,7 @@ void categorizeSymlinkByContent(SymlinkPair& symlink, ProcessCallback& callback) //2. harmonize with "bool stillInSync()" in algorithm.cpp, FilePair::setSyncedTo() in file_hierarchy.h //symlinks have same "content" - if (getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE >()) != + if (getUnicodeNormalForm(symlink.getItemName< LEFT_SIDE>()) != getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>())) symlink.setCategoryDiffMetadata(getDescrDiffMetaShortnameCase(symlink)); //else if (!sameFileTime(symlink.getLastWriteTime<LEFT_SIDE>(), @@ -450,7 +462,7 @@ void categorizeFileByContent(FilePair& file, const std::wstring& txtComparingCon interruptionPoint(); //throw ThreadInterruption }; - haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath<LEFT_SIDE >(), + haveSameContent = parallel::filesHaveSameContent(file.getAbstractPath< LEFT_SIDE>(), file.getAbstractPath<RIGHT_SIDE>(), notifyUnbufferedIO, singleThread); //throw FileError statReporter.reportDelta(1, 0); }, acb); //throw ThreadInterruption @@ -491,7 +503,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co size_t effectiveMax = 0; //a folder pair is allowed to use the maximum parallelOps that left/right devices support // => consider max over all folder pairs, that a device is involved with! }; - std::map<AbstractPath, ParallelOps> parallelOpsStatus; + std::map<AfsDevice, ParallelOps> parallelOpsStatus; struct BinaryWorkload { @@ -503,15 +515,12 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co auto addToBinaryWorkload = [&](const AbstractPath& basePathL, const AbstractPath& basePathR, RingBuffer<FilePair*>&& filesToCompareBytewise) { - const AbstractPath rootPathL = AFS::getRootPath(basePathL); - const AbstractPath rootPathR = AFS::getRootPath(basePathR); - //calculate effective max parallelOps that devices must support - const size_t parallelOpsFp = std::max(getDeviceParallelOps(deviceParallelOps_, rootPathL), - getDeviceParallelOps(deviceParallelOps_, rootPathR)); + const size_t parallelOpsFp = std::max(getDeviceParallelOps(deviceParallelOps_, basePathL.afsDevice), + getDeviceParallelOps(deviceParallelOps_, basePathR.afsDevice)); - ParallelOps& posL = parallelOpsStatus[rootPathL]; - ParallelOps& posR = parallelOpsStatus[rootPathR]; + ParallelOps& posL = parallelOpsStatus[basePathL.afsDevice]; + ParallelOps& posR = parallelOpsStatus[basePathR.afsDevice]; posL.effectiveMax = std::max(posL.effectiveMax, parallelOpsFp); posR.effectiveMax = std::max(posR.effectiveMax, parallelOpsFp); @@ -548,7 +557,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co filesToCompareBytewise.push_back(file); } if (!filesToCompareBytewise.empty()) - addToBinaryWorkload(output.back()->getAbstractPath<LEFT_SIDE >(), + addToBinaryWorkload(output.back()->getAbstractPath< LEFT_SIDE>(), output.back()->getAbstractPath<RIGHT_SIDE>(), std::move(filesToCompareBytewise)); //finish symlink categorization @@ -591,9 +600,7 @@ std::list<std::shared_ptr<BaseFolderPair>> ComparisonBuffer::compareByContent(co ParallelOps& posL = bwl.parallelOpsL; ParallelOps& posR = bwl.parallelOpsR; - const size_t newTaskCount = numeric::min<size_t>(posL.effectiveMax - posL.current, - posR.effectiveMax - posR.current, - bwl.filesToCompareBytewise.size()); + const size_t newTaskCount = std::min<size_t>({ posL.effectiveMax - posL.current, posR.effectiveMax - posR.current, bwl.filesToCompareBytewise.size() }); if (&posL != &posR) posL.current += newTaskCount; // /**/ posR.current += newTaskCount; //consider aliasing! @@ -721,7 +728,7 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn { struct FileRef { - Zstring normalName; //buffer expensive makeUpperCopy() calls!! + Zstring upperCaseName; //buffer expensive makeUpperCopy() calls!! const typename MapType::value_type* ref; bool leftSide; }; @@ -731,20 +738,9 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn for (const auto& item : mapLeft ) fileList.push_back({ makeUpperCopy(item.first), &item, true }); for (const auto& item : mapRight) fileList.push_back({ makeUpperCopy(item.first), &item, false }); - std::sort(fileList.begin(), fileList.end(), [&](const FileRef& lhs, const FileRef& rhs) - { - int rv = compareString(lhs.normalName, rhs.normalName); - if (rv != 0) - return rv < 0; //primary sort key: ignore unicode normal form and case - - //perf: sorting by secondary/tertiary key here costs about 7% additional runtime - rv = compareString(getUnicodeNormalForm(lhs.ref->first), getUnicodeNormalForm(rhs.ref->first)); - if (rv != 0) - return rv < 0; //secondary sort key: ignore unicode normal - - return lhs.ref->first < rhs.ref->first; //tertiary sort key: raw string value - }); + //primary sort: ignore unicode normal form and case //bonus: natural default sequence on file guid UI + std::sort(fileList.begin(), fileList.end(), [](const FileRef& lhs, const FileRef& rhs) { return lhs.upperCaseName < rhs.upperCaseName; }); auto tryMatchRange = [&](auto it, auto itLast) { @@ -758,11 +754,11 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn else bo(*it[1].ref, *it[0].ref); } - else if (equalCountL == 0) //no match - std::for_each(it, itLast, [&](const FileRef& fr) { ro(*fr.ref); }); - else if (equalCountR == 0) //no match - std::for_each(it, itLast, [&](const FileRef& fr) { lo(*fr.ref); }); - else //ambiguous + else if (equalCountL == 1 && equalCountR == 0) + lo(*it->ref, nullptr); + else if (equalCountL == 0 && equalCountR == 1) + ro(*it->ref, nullptr); + else //ambiguous (yes, even if one side only, e.g. different Unicode normalization forms) return false; return true; }; @@ -770,29 +766,30 @@ void matchFolders(const MapType& mapLeft, const MapType& mapRight, ProcessLeftOn for (auto it = fileList.begin(); it != fileList.end();) { //find equal range: ignore case, ignore Unicode normalization - auto itEndEq = std::find_if(it + 1, fileList.end(), [&](const FileRef& fr) { return fr.normalName != it->normalName; }); + auto itEndEq = std::find_if(it + 1, fileList.end(), [&](const FileRef& fr) { return fr.upperCaseName != it->upperCaseName; }); if (!tryMatchRange(it, itEndEq)) + { + //secondary sort: respect case, ignore unicode normal forms + std::sort(it, itEndEq, [](const FileRef& lhs, const FileRef& rhs) { return getUnicodeNormalForm(lhs.ref->first) < getUnicodeNormalForm(rhs.ref->first); }); + for (auto itCase = it; itCase != itEndEq;) { //find equal range: respect case, ignore Unicode normalization auto itEndCase = std::find_if(itCase + 1, itEndEq, [&](const FileRef& fr) { return getUnicodeNormalForm(fr.ref->first) != getUnicodeNormalForm(itCase->ref->first); }); if (!tryMatchRange(itCase, itEndCase)) - for (auto itRaw = itCase; itRaw != itEndCase;) + { + const Zstringw& conflictMsg = getConflictAmbiguousItemName(itCase->ref->first); + std::for_each(itCase, itEndCase, [&](const FileRef& fr) { - //find equal range: respect case, respect Unicode normalization - auto itEndRaw = std::find_if(itRaw + 1, itEndCase, [&](const FileRef& fr) { return fr.ref->first != itRaw->ref->first; }); - if (!tryMatchRange(itRaw, itEndRaw)) - std::for_each(itRaw, itEndRaw, [&](const FileRef& fr) - { - if (fr.leftSide) - lo(*fr.ref); - else - ro(*fr.ref); - }); - itRaw = itEndRaw; - } + if (fr.leftSide) + lo(*fr.ref, &conflictMsg); + else + ro(*fr.ref, &conflictMsg); + }); + } itCase = itEndCase; } + } it = itEndEq; } } @@ -802,11 +799,17 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer { using FileData = FolderContainer::FileList::value_type; - matchFolders(lhs.files, rhs.files, - [&](const FileData& fileLeft ) { FilePair& newItem = output.addSubFile< LEFT_SIDE>(fileLeft .first, fileLeft .second); checkFailedRead(newItem, errorMsg); }, //left only - [&](const FileData& fileRight) { FilePair& newItem = output.addSubFile<RIGHT_SIDE>(fileRight.first, fileRight.second); checkFailedRead(newItem, errorMsg); }, //right only - - [&](const FileData& fileLeft, const FileData& fileRight) //both sides + matchFolders(lhs.files, rhs.files, [&](const FileData& fileLeft, const Zstringw* conflictMsg) + { + FilePair& newItem = output.addSubFile< LEFT_SIDE>(fileLeft .first, fileLeft .second); + checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg); + }, + [&](const FileData& fileRight, const Zstringw* conflictMsg) + { + FilePair& newItem = output.addSubFile<RIGHT_SIDE>(fileRight.first, fileRight.second); + checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg); + }, + [&](const FileData& fileLeft, const FileData& fileRight) { FilePair& newItem = output.addSubFile(fileLeft.first, fileLeft.second, @@ -821,10 +824,16 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer //----------------------------------------------------------------------------------------------- using SymlinkData = FolderContainer::SymlinkList::value_type; - matchFolders(lhs.symlinks, rhs.symlinks, - [&](const SymlinkData& symlinkLeft ) { SymlinkPair& newItem = output.addSubLink< LEFT_SIDE>(symlinkLeft .first, symlinkLeft .second); checkFailedRead(newItem, errorMsg); }, //left only - [&](const SymlinkData& symlinkRight) { SymlinkPair& newItem = output.addSubLink<RIGHT_SIDE>(symlinkRight.first, symlinkRight.second); checkFailedRead(newItem, errorMsg); }, //right only - + matchFolders(lhs.symlinks, rhs.symlinks, [&](const SymlinkData& symlinkLeft, const Zstringw* conflictMsg) + { + SymlinkPair& newItem = output.addSubLink< LEFT_SIDE>(symlinkLeft .first, symlinkLeft .second); + checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg); + }, + [&](const SymlinkData& symlinkRight, const Zstringw* conflictMsg) + { + SymlinkPair& newItem = output.addSubLink<RIGHT_SIDE>(symlinkRight.first, symlinkRight.second); + checkFailedRead(newItem, conflictMsg ? conflictMsg : errorMsg); + }, [&](const SymlinkData& symlinkLeft, const SymlinkData& symlinkRight) //both sides { SymlinkPair& newItem = output.addSubLink(symlinkLeft.first, @@ -839,21 +848,19 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer //----------------------------------------------------------------------------------------------- using FolderData = FolderContainer::FolderList::value_type; - matchFolders(lhs.folders, rhs.folders, - [&](const FolderData& dirLeft) //left only + matchFolders(lhs.folders, rhs.folders, [&](const FolderData& dirLeft, const Zstringw* conflictMsg) { FolderPair& newFolder = output.addSubFolder<LEFT_SIDE>(dirLeft.first, dirLeft.second.first); - const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg); + const Zstringw* errorMsgNew = checkFailedRead(newFolder, conflictMsg ? conflictMsg : errorMsg); this->fillOneSide<LEFT_SIDE>(dirLeft.second.second, errorMsgNew, newFolder); //recurse }, - [&](const FolderData& dirRight) //right only + [&](const FolderData& dirRight, const Zstringw* conflictMsg) { FolderPair& newFolder = output.addSubFolder<RIGHT_SIDE>(dirRight.first, dirRight.second.first); - const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg); + const Zstringw* errorMsgNew = checkFailedRead(newFolder, conflictMsg ? conflictMsg : errorMsg); this->fillOneSide<RIGHT_SIDE>(dirRight.second.second, errorMsgNew, newFolder); //recurse }, - - [&](const FolderData& dirLeft, const FolderData& dirRight) //both sides + [&](const FolderData& dirLeft, const FolderData& dirRight) { FolderPair& newFolder = output.addSubFolder(dirLeft.first, dirLeft.second.first, DIR_EQUAL, dirRight.first, dirRight.second.first); const Zstringw* errorMsgNew = checkFailedRead(newFolder, errorMsg); @@ -870,7 +877,7 @@ void MergeSides::mergeTwoSides(const FolderContainer& lhs, const FolderContainer //----------------------------------------------------------------------------------------------- //uncheck excluded directories (see parallelDeviceTraversal()) + remove superfluous excluded subdirectories -void stripExcludedDirectories(ContainerObject& hierObj, const HardFilter& filterProc) +void stripExcludedDirectories(ContainerObject& hierObj, const PathFilter& filterProc) { for (FolderPair& folder : hierObj.refSubFolders()) stripExcludedDirectories(folder, filterProc); @@ -942,7 +949,7 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv bufValueLeft != nullptr, //dir existence must be checked only once: available iff buffer entry exists! fp.folderPathRight, bufValueRight != nullptr, - fpCfg.filter.nameFilter->copyFilterAddingExclusion(excludefilterFailedRead), + fpCfg.filter.nameFilter.ref().copyFilterAddingExclusion(excludefilterFailedRead), fpCfg.compareVar, fileTimeTolerance_, fpCfg.ignoreTimeShiftMinutes); @@ -957,8 +964,8 @@ std::shared_ptr<BaseFolderPair> ComparisonBuffer::performComparison(const Resolv //NOTE: we need to finish de-activating rows BEFORE binary comparison is run so that it can skip them! //attention: some excluded directories are still in the comparison result! (see include filter handling!) - if (!fpCfg.filter.nameFilter->isNull()) - stripExcludedDirectories(*output, *fpCfg.filter.nameFilter); //mark excluded directories (see parallelDeviceTraversal()) + remove superfluous excluded subdirectories + if (!fpCfg.filter.nameFilter.ref().isNull()) + stripExcludedDirectories(*output, fpCfg.filter.nameFilter.ref()); //mark excluded directories (see parallelDeviceTraversal()) + remove superfluous excluded subdirectories //apply soft filtering (hard filter already applied during traversal!) addSoftFiltering(*output, fpCfg.filter.timeSizeFilter); @@ -1007,7 +1014,7 @@ FolderComparison fff::compare(WarningDialogs& warnings, bool createDirLocks, std::unique_ptr<LockHolder>& dirLocks, const std::vector<FolderPairCfg>& fpCfgList, - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, ProcessCallback& callback) { //PERF_START; @@ -1078,8 +1085,8 @@ FolderComparison fff::compare(WarningDialogs& warnings, std::wstring msg; for (const auto& [folderPair, fpCfg] : workLoad) - if (std::optional<PathDependency> pd = getPathDependency(folderPair.folderPathLeft, *fpCfg.filter.nameFilter, - folderPair.folderPathRight, *fpCfg.filter.nameFilter)) + if (std::optional<PathDependency> pd = getPathDependency(folderPair.folderPathLeft, fpCfg.filter.nameFilter.ref(), + folderPair.folderPathRight, fpCfg.filter.nameFilter.ref())) { msg += L"\n\n" + AFS::getDisplayPath(folderPair.folderPathLeft) + L"\n" + @@ -1098,12 +1105,12 @@ FolderComparison fff::compare(WarningDialogs& warnings, //lock (existing) directories before comparison if (createDirLocks) { - std::set<Zstring, LessFilePath> dirPathsExisting; + std::set<Zstring> folderPathsToLock; for (const AbstractPath& folderPath : resInfo.existingBaseFolders) if (std::optional<Zstring> nativePath = AFS::getNativeItemPath(folderPath)) //restrict directory locking to native paths until further - dirPathsExisting.insert(*nativePath); + folderPathsToLock.insert(*nativePath); - dirLocks = std::make_unique<LockHolder>(dirPathsExisting, warnings.warnDirectoryLockFailed, callback); + dirLocks = std::make_unique<LockHolder>(folderPathsToLock, warnings.warnDirectoryLockFailed, callback); } try diff --git a/FreeFileSync/Source/base/comparison.h b/FreeFileSync/Source/base/comparison.h index b956019b..82e73637 100755 --- a/FreeFileSync/Source/base/comparison.h +++ b/FreeFileSync/Source/base/comparison.h @@ -58,7 +58,7 @@ FolderComparison compare(WarningDialogs& warnings, bool createDirLocks, std::unique_ptr<LockHolder>& dirLocks, //out const std::vector<FolderPairCfg>& fpCfgList, - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, ProcessCallback& callback); } diff --git a/FreeFileSync/Source/base/db_file.cpp b/FreeFileSync/Source/base/db_file.cpp index 2dc88402..3f06a197 100755 --- a/FreeFileSync/Source/base/db_file.cpp +++ b/FreeFileSync/Source/base/db_file.cpp @@ -141,7 +141,7 @@ DbStreams loadStreams(const AbstractPath& dbPath, const IOCallback& notifyUnbuff catch (FileError&) { bool dbNotYetExisting = false; - try { dbNotYetExisting = !AFS::getItemTypeIfExists(dbPath); /*throw FileError*/ } + try { dbNotYetExisting = !AFS::itemStillExists(dbPath); /*throw FileError*/ } catch (FileError&) {} //previous exception is more relevant if (dbNotYetExisting) //throw FileError @@ -546,7 +546,7 @@ public: } private: - LastSynchronousStateUpdater(CompareVariant activeCmpVar, const HardFilter& filter) : + LastSynchronousStateUpdater(CompareVariant activeCmpVar, const PathFilter& filter) : filter_(filter), activeCmpVar_(activeCmpVar) {} @@ -562,15 +562,15 @@ private: { //C++17's map::try_emplace() is faster than map::emplace() if key is already existing auto rv = map.try_emplace(key, std::forward<V>(value)); //and does NOT MOVE r-value arguments unlike map::emplace()! - if (rv.second) - return rv.first->second; + if (!rv.second) + rv.first->second = std::forward<V>(value); - return rv.first->second = std::forward<V>(value); + return rv.first->second; } void process(const ContainerObject::FileList& currentFiles, const Zstring& parentRelPath, InSyncFolder::FileList& dbFiles) { - std::unordered_set<const InSyncFile*> toPreserve; //referencing fixed-in-memory std::map elements + std::set<Zstring, LessUnicodeNormal> toPreserve; for (const FilePair& file : currentFiles) if (!file.isPairEmpty()) @@ -584,30 +584,29 @@ private: assert(file.getFileSize<LEFT_SIDE>() == file.getFileSize<RIGHT_SIDE>()); //create or update new "in-sync" state - InSyncFile& dbFile = mapAddOrUpdate(dbFiles, file.getItemNameAny(), - InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(), - file.getFileId < LEFT_SIDE>()), - InSyncDescrFile(file.getLastWriteTime<RIGHT_SIDE>(), - file.getFileId <RIGHT_SIDE>()), - activeCmpVar_, - file.getFileSize<LEFT_SIDE>())); - toPreserve.insert(&dbFile); + mapAddOrUpdate(dbFiles, file.getItemNameAny(), + InSyncFile(InSyncDescrFile(file.getLastWriteTime< LEFT_SIDE>(), + file.getFileId < LEFT_SIDE>()), + InSyncDescrFile(file.getLastWriteTime<RIGHT_SIDE>(), + file.getFileId <RIGHT_SIDE>()), + activeCmpVar_, + file.getFileSize<LEFT_SIDE>())); + toPreserve.insert(file.getItemNameAny()); } else //not in sync: preserve last synchronous state { - auto it = dbFiles.find(file.getItemNameAny()); - if (it != dbFiles.end()) - toPreserve.insert(&it->second); + toPreserve.insert(file.getItemName< LEFT_SIDE>()); //left/right may differ in case! + toPreserve.insert(file.getItemName<RIGHT_SIDE>()); // } } //delete removed items (= "in-sync") from database - erase_if(dbFiles, [&](const InSyncFolder::FileList::value_type& v) -> bool + eraseIf(dbFiles, [&](const InSyncFolder::FileList::value_type& v) -> bool { - if (toPreserve.find(&v.second) != toPreserve.end()) + if (toPreserve.find(v.first) != toPreserve.end()) return false; //all items not existing in "currentFiles" have either been deleted meanwhile or been excluded via filter: - const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR); + const Zstring& itemRelPath = nativeAppendPaths(parentRelPath, v.first); return filter_.passFileFilter(itemRelPath); //note: items subject to traveral errors are also excluded by this file filter here! see comparison.cpp, modified file filter for read errors }); @@ -615,7 +614,7 @@ private: void process(const ContainerObject::SymlinkList& currentSymlinks, const Zstring& parentRelPath, InSyncFolder::SymlinkList& dbSymlinks) { - std::unordered_set<const InSyncSymlink*> toPreserve; + std::set<Zstring, LessUnicodeNormal> toPreserve; for (const SymlinkPair& symlink : currentSymlinks) if (!symlink.isPairEmpty()) @@ -625,27 +624,26 @@ private: assert(getUnicodeNormalForm(symlink.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(symlink.getItemName<RIGHT_SIDE>())); //create or update new "in-sync" state - InSyncSymlink& dbSymlink = mapAddOrUpdate(dbSymlinks, symlink.getItemNameAny(), - InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime<LEFT_SIDE>()), - InSyncDescrLink(symlink.getLastWriteTime<RIGHT_SIDE>()), - activeCmpVar_)); - toPreserve.insert(&dbSymlink); + mapAddOrUpdate(dbSymlinks, symlink.getItemNameAny(), + InSyncSymlink(InSyncDescrLink(symlink.getLastWriteTime< LEFT_SIDE>()), + InSyncDescrLink(symlink.getLastWriteTime<RIGHT_SIDE>()), + activeCmpVar_)); + toPreserve.insert(symlink.getItemNameAny()); } else //not in sync: preserve last synchronous state { - auto it = dbSymlinks.find(symlink.getItemNameAny()); - if (it != dbSymlinks.end()) - toPreserve.insert(&it->second); + toPreserve.insert(symlink.getItemName< LEFT_SIDE>()); //left/right may differ in case! + toPreserve.insert(symlink.getItemName<RIGHT_SIDE>()); // } } //delete removed items (= "in-sync") from database - erase_if(dbSymlinks, [&](const InSyncFolder::SymlinkList::value_type& v) -> bool + eraseIf(dbSymlinks, [&](const InSyncFolder::SymlinkList::value_type& v) -> bool { - if (toPreserve.find(&v.second) != toPreserve.end()) + if (toPreserve.find(v.first) != toPreserve.end()) return false; //all items not existing in "currentSymlinks" have either been deleted meanwhile or been excluded via filter: - const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR); + const Zstring& itemRelPath = nativeAppendPaths(parentRelPath, v.first); return filter_.passFileFilter(itemRelPath); }); } @@ -656,59 +654,45 @@ private: for (const FolderPair& folder : currentFolders) if (!folder.isPairEmpty()) - switch (folder.getDirCategory()) + { + if (folder.getDirCategory() == DIR_EQUAL) { - case DIR_EQUAL: - { - assert(getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>())); - - //update directory entry only (shallow), but do *not touch* exising child elements!!! - const Zstring& key = folder.getItemNameAny(); - auto insertResult = dbFolders.emplace(key, InSyncFolder(InSyncFolder::DIR_STATUS_IN_SYNC)); //get or create - auto it = insertResult.first; - - InSyncFolder& dbFolder = it->second; - dbFolder.status = InSyncFolder::DIR_STATUS_IN_SYNC; //update immediate directory entry - toPreserve.insert(&dbFolder); - recurse(folder, dbFolder); - } - break; - - case DIR_CONFLICT: - case DIR_DIFFERENT_METADATA: - //if DIR_DIFFERENT_METADATA and no old database entry yet: we have to insert a placeholder database entry: - //we cannot simply skip the whole directory, since sub-items might be in sync! - //Example: directories on left and right differ in case while sub-files are equal - { - //reuse last "in-sync" if available or insert strawman entry (do not try to update and thereby remove child elements!!!) - InSyncFolder& dbFolder = dbFolders.emplace(folder.getItemNameAny(), InSyncFolder(InSyncFolder::DIR_STATUS_STRAW_MAN)).first->second; - toPreserve.insert(&dbFolder); - recurse(folder, dbFolder); //unconditional recursion without filter check! => no problem since "childItemMightMatch" is optional!!! - } - break; - - //not in sync: reuse last synchronous state: - case DIR_LEFT_SIDE_ONLY: - case DIR_RIGHT_SIDE_ONLY: + assert(getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) == getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>())); + + //update directory entry only (shallow), but do *not touch* exising child elements!!! + InSyncFolder& dbFolder = dbFolders.emplace(folder.getItemNameAny(), InSyncFolder(InSyncFolder::DIR_STATUS_IN_SYNC)).first->second; //get or create + dbFolder.status = InSyncFolder::DIR_STATUS_IN_SYNC; //update immediate directory entry + + toPreserve.insert(&dbFolder); + recurse(folder, dbFolder); + } + else //not in sync: preserve last synchronous state + { + auto preserveDbEntry = [&](const Zstring& folderName) { - auto it = dbFolders.find(folder.getItemNameAny()); + auto it = dbFolders.find(folderName); if (it != dbFolders.end()) { toPreserve.insert(&it->second); - recurse(folder, it->second); //although existing sub-items cannot be in sync, items deleted on both sides *are* in-sync!!! + recurse(folder, it->second); //required: existing child-items may not be in sync, but items deleted on both sides *are* in-sync!!! } - } - break; + }; + preserveDbEntry(folder.getItemName<LEFT_SIDE>()); + + //folder match with names differing in case? => treat like any other folder rename => no *new* database entries even if child items are in sync + if (getUnicodeNormalForm(folder.getItemName<LEFT_SIDE>()) != getUnicodeNormalForm(folder.getItemName<RIGHT_SIDE>())) + preserveDbEntry(folder.getItemName<RIGHT_SIDE>()); } + } //delete removed items (= "in-sync") from database - erase_if(dbFolders, [&](InSyncFolder::FolderList::value_type& v) -> bool + eraseIf(dbFolders, [&](InSyncFolder::FolderList::value_type& v) -> bool { if (toPreserve.find(&v.second) != toPreserve.end()) return false; - const Zstring& itemRelPath = AFS::appendPaths(parentRelPath, v.first, FILE_NAME_SEPARATOR); - //if directory is not included in "currentDirs", it is either not existing anymore, in which case it should be deleted from database + const Zstring& itemRelPath = nativeAppendPaths(parentRelPath, v.first); + //if folder is not included in "current folders", it is either not existing anymore, in which case it should be deleted from database //or it was excluded via filter and the database entry should be preserved bool childItemMightMatch = true; @@ -722,10 +706,10 @@ private: //delete all entries for removed folder (= "in-sync") from database void dbSetEmptyState(InSyncFolder& dbFolder, const Zstring& parentRelPathPf) { - erase_if(dbFolder.files, [&](const InSyncFolder::FileList ::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); }); - erase_if(dbFolder.symlinks, [&](const InSyncFolder::SymlinkList::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); }); + eraseIf(dbFolder.files, [&](const InSyncFolder::FileList ::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); }); + eraseIf(dbFolder.symlinks, [&](const InSyncFolder::SymlinkList::value_type& v) { return filter_.passFileFilter(parentRelPathPf + v.first); }); - erase_if(dbFolder.folders, [&](InSyncFolder::FolderList::value_type& v) + eraseIf(dbFolder.folders, [&](InSyncFolder::FolderList::value_type& v) { const Zstring& itemRelPath = parentRelPathPf + v.first; @@ -737,7 +721,7 @@ private: }); } - const HardFilter& filter_; //filter used while scanning directory: generates view on actual files! + const PathFilter& filter_; //filter used while scanning directory: generates view on actual files! const CompareVariant activeCmpVar_; }; diff --git a/FreeFileSync/Source/base/db_file.h b/FreeFileSync/Source/base/db_file.h index ebbb0c01..c0d1b390 100755 --- a/FreeFileSync/Source/base/db_file.h +++ b/FreeFileSync/Source/base/db_file.h @@ -58,16 +58,16 @@ struct InSyncFolder enum InSyncStatus { DIR_STATUS_IN_SYNC, - DIR_STATUS_STRAW_MAN //there is no last synchronous state, but used as container only + DIR_STATUS_STRAW_MAN //no last synchronous state, but used as container only }; InSyncFolder(InSyncStatus statusIn) : status(statusIn) {} InSyncStatus status = DIR_STATUS_STRAW_MAN; //------------------------------------------------------------------ - using FolderList = std::map<Zstring, InSyncFolder, LessFilePath>; // - using FileList = std::map<Zstring, InSyncFile, LessFilePath>; // key: file name - using SymlinkList = std::map<Zstring, InSyncSymlink, LessFilePath>; // + using FolderList = std::map<Zstring, InSyncFolder, LessUnicodeNormal>; // + using FileList = std::map<Zstring, InSyncFile, LessUnicodeNormal>; // key: file name (ignoring Unicode normal forms) + using SymlinkList = std::map<Zstring, InSyncSymlink, LessUnicodeNormal>; // //------------------------------------------------------------------ FolderList folders; diff --git a/FreeFileSync/Source/base/dir_exist_async.h b/FreeFileSync/Source/base/dir_exist_async.h index c445a665..749e79ce 100755 --- a/FreeFileSync/Source/base/dir_exist_async.h +++ b/FreeFileSync/Source/base/dir_exist_async.h @@ -29,46 +29,78 @@ struct FolderStatus std::set<AbstractPath> existing; std::set<AbstractPath> notExisting; std::map<AbstractPath, zen::FileError> failedChecks; + + std::map<AbstractPath, AbstractPath> normalizedPathsEx; //get rid of folder aliases (e.g. path differing in case) }; -FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPaths, const std::map<AbstractPath, size_t>& deviceParallelOps, +AbstractPath getNormalizedPath(const FolderStatus& status, const AbstractPath& folderPath) +{ + auto it = status.normalizedPathsEx.find(folderPath); + return it == status.normalizedPathsEx.end() ? folderPath : it->second; +} + + +FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPaths, const std::map<AfsDevice, size_t>& deviceParallelOps, bool allowUserInteraction, ProcessCallback& procCallback /*throw X*/) { using namespace zen; //aggregate folder paths that are on the same root device: see parallel_scan.h - std::map<AbstractPath, std::set<AbstractPath>> perDevicePaths; + std::map<AfsDevice, std::set<AbstractPath>> perDevicePaths; for (const AbstractPath& folderPath : folderPaths) if (!AFS::isNullPath(folderPath)) //skip empty folders - perDevicePaths[AFS::getRootPath(folderPath)].insert(folderPath); + perDevicePaths[folderPath.afsDevice].insert(folderPath); - std::vector<std::pair<AbstractPath, std::future<bool>>> futureInfo; + std::vector<std::pair<AbstractPath, std::future<std::optional<AFS::FileId>>>> futureDetails; - std::vector<ThreadGroup<std::packaged_task<bool()>>> perDeviceThreads; - for (const auto& [rootPath, deviceFolderPaths] : perDevicePaths) + std::vector<ThreadGroup<std::packaged_task<std::optional<AFS::FileId>()>>> perDeviceThreads; + for (const auto& [afsDevice, deviceFolderPaths] : perDevicePaths) { - const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, rootPath); + const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, afsDevice); - perDeviceThreads.emplace_back(parallelOps, "DirExist: " + utfTo<std::string>(AFS::getDisplayPath(rootPath))); + perDeviceThreads.emplace_back(parallelOps, "DirExist: " + utfTo<std::string>(AFS::getDisplayPath(AbstractPath(afsDevice, AfsPath())))); auto& threadGroup = perDeviceThreads.back(); threadGroup.detach(); //don't wait on threads hanging longer than "folderAccessTimeout" for (const AbstractPath& folderPath : deviceFolderPaths) { - std::packaged_task<bool()> pt([folderPath, allowUserInteraction] //AbstractPath is thread-safe like an int! :) + std::packaged_task<std::optional<AFS::FileId>()> pt([folderPath, allowUserInteraction]() -> std::optional<AFS::FileId> { //1. login to network share, open FTP connection, etc. AFS::connectNetworkFolder(folderPath, allowUserInteraction); //throw FileError - //2. check dir existence - return static_cast<bool>(AFS::getItemTypeIfExists(folderPath)); //throw FileError - //TODO: consider ItemType::FILE a failure instead? In any case: return "false" IFF nothing (of any type) exists + //2. check dir existence (...by doing something useful and getting the file ID) + std::exception_ptr fidError; + try + { + const AFS::FileId fileId = AFS::getFileId(folderPath); //throw FileError + if (!fileId.empty()) //=> folder exists + return fileId; + } + catch (FileError&) { fidError = std::current_exception(); } + //else: error or fileId not available, e.g. FTP, SFTP + + /* CAVEAT: the case-sensitive semantics of AFS::itemStillExists() do not fit here! + BUT: its implementation happens to be okay for our use: + Assume we have a case-insensitive path match: + => AFS::itemStillExists() first checks AFS::getItemType() + => either succeeds (fine) or fails because of 1. not existing or 2. access error + => the subsequent folder search reports "no folder": only a problem in case 2 + => FFS tries to create the folder during sync and fails with I. access error (fine) or II. already existing (obscures the previous "access error") */ + if (!AFS::itemStillExists(folderPath)) //throw FileError + return {}; + + if (fidError) + std::rethrow_exception(fidError); + else + return AFS::FileId(); + //consider ItemType::FILE a failure instead? Meanwhile: return "false" IFF nothing (of any type) exists }); auto fut = pt.get_future(); threadGroup.run(std::move(pt)); - futureInfo.emplace_back(folderPath, std::move(fut)); + futureDetails.emplace_back(folderPath, std::move(fut)); } } @@ -76,8 +108,9 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath const auto startTime = std::chrono::steady_clock::now(); FolderStatus output; + std::map<AFS::FileId, AbstractPath> exFoldersById; - for (auto& [folderPath, future] : futureInfo) + for (auto& [folderPath, future] : futureDetails) { const std::wstring& displayPathFmt = fmtPath(AFS::getDisplayPath(folderPath)); @@ -96,8 +129,17 @@ FolderStatus getFolderStatusNonBlocking(const std::set<AbstractPath>& folderPath try { //call future::get() only *once*! otherwise: undefined behavior! - if (future.get()) //throw FileError - output.existing.insert(folderPath); + if (std::optional<AFS::FileId> folderInfo = future.get()) //throw FileError + { + output.existing.emplace(folderPath); + + //find folder aliases (e.g. path differing in case) + const AFS::FileId fileId = *folderInfo; + if (!fileId.empty()) + exFoldersById.emplace(fileId, folderPath); + + output.normalizedPathsEx.emplace(folderPath, fileId.empty() ? folderPath : exFoldersById.find(fileId)->second); + } else output.notExisting.insert(folderPath); } diff --git a/FreeFileSync/Source/base/dir_lock.cpp b/FreeFileSync/Source/base/dir_lock.cpp index 86006337..cfcf6699 100755 --- a/FreeFileSync/Source/base/dir_lock.cpp +++ b/FreeFileSync/Source/base/dir_lock.cpp @@ -436,9 +436,9 @@ public: tidyUp(); //optimization: check if we already own a lock for this path - auto iterGuid = fileToGuid_.find(lockFilePath); - if (iterGuid != fileToGuid_.end()) - if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(iterGuid->second)) //returns null-lock if not found + auto itGuid = guidByPath_.find(lockFilePath); + if (itGuid != guidByPath_.end()) + if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(itGuid->second)) //returns null-lock if not found return activeLock; //SharedDirLock is still active -> enlarge circle of shared ownership try //check based on lock GUID, deadlock prevention: "lockFilePath" may be an alternative name for a lock already owned by this process @@ -446,7 +446,7 @@ public: const std::string lockId = retrieveLockId(lockFilePath); //throw FileError if (const std::shared_ptr<SharedDirLock>& activeLock = getActiveLock(lockId)) //returns null-lock if not found { - fileToGuid_[lockFilePath] = lockId; //found an alias for one of our active locks + guidByPath_[lockFilePath] = lockId; //found an alias for one of our active locks return activeLock; } } @@ -456,9 +456,8 @@ public: auto newLock = std::make_shared<SharedDirLock>(lockFilePath, notifyStatus, cbInterval); //throw FileError const std::string& newLockGuid = retrieveLockId(lockFilePath); //throw FileError - //update registry - fileToGuid_[lockFilePath] = newLockGuid; //throw() - guidToLock_[newLockGuid] = newLock; // + guidByPath_[lockFilePath] = newLockGuid; //update registry + locksByGuid_[newLockGuid] = newLock; // return newLock; } @@ -469,23 +468,21 @@ private: LockAdmin& operator=(const LockAdmin&) = delete; using UniqueId = std::string; - using FileToGuidMap = std::map<Zstring, UniqueId, LessFilePath>; //n:1 handle uppper/lower case correctly - using GuidToLockMap = std::map<UniqueId, std::weak_ptr<SharedDirLock>>; //1:1 std::shared_ptr<SharedDirLock> getActiveLock(const UniqueId& lockId) //returns null if none found { - auto it = guidToLock_.find(lockId); - return it != guidToLock_.end() ? it->second.lock() : nullptr; //try to get shared_ptr; throw() + auto it = locksByGuid_.find(lockId); + return it != locksByGuid_.end() ? it->second.lock() : nullptr; //try to get shared_ptr; throw() } void tidyUp() //remove obsolete entries { - erase_if(guidToLock_, [ ](const GuidToLockMap::value_type& v) { return !v.second.lock(); }); - erase_if(fileToGuid_, [&](const FileToGuidMap::value_type& v) { return guidToLock_.find(v.second) == guidToLock_.end(); }); + eraseIf(locksByGuid_, [](const auto& v) { return !v.second.lock(); }); + eraseIf(guidByPath_, [&](const auto& v) { return locksByGuid_.find(v.second) == locksByGuid_.end(); }); } - FileToGuidMap fileToGuid_; //lockname |-> GUID; locks can be referenced by a lockFilePath or alternatively a GUID - GuidToLockMap guidToLock_; //GUID |-> "shared lock ownership" + std::map<Zstring, UniqueId> guidByPath_; //lockFilePath |-> GUID; n:1; locks can be referenced by a lockFilePath or alternatively a GUID + std::map<UniqueId, std::weak_ptr<SharedDirLock>> locksByGuid_; //GUID |-> "shared lock ownership"; 1:1 }; diff --git a/FreeFileSync/Source/base/dir_lock.h b/FreeFileSync/Source/base/dir_lock.h index 910e551d..20795804 100755 --- a/FreeFileSync/Source/base/dir_lock.h +++ b/FreeFileSync/Source/base/dir_lock.h @@ -17,7 +17,7 @@ namespace fff { /* RAII structure to place a directory lock against other FFS processes: - - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts etc. + - recursive locking supported, even with alternate lockfile names, e.g. via symlinks, network mounts, case-differences etc. - ownership shared between all object instances refering to a specific lock location(= GUID) - can be copied safely and efficiently! (ref-counting) - detects and resolves abandoned locks (instantly if lock is associated with local pc, else after 30 seconds) diff --git a/FreeFileSync/Source/base/ffs_paths.cpp b/FreeFileSync/Source/base/ffs_paths.cpp index 3668f059..43ea8fff 100755 --- a/FreeFileSync/Source/base/ffs_paths.cpp +++ b/FreeFileSync/Source/base/ffs_paths.cpp @@ -16,18 +16,44 @@ using namespace zen; namespace { inline -Zstring getExecutablePathPf() //directory containing executable WITH path separator at end +Zstring getExeFolderPath() //directory containing executable WITH path separator at end { - return appendSeparator(beforeLast(utfTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); + return beforeLast(utfTo<Zstring>(wxStandardPaths::Get().GetExecutablePath()), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); } + + +inline +Zstring getExeFolderParentPath() +{ + return beforeLast(getExeFolderPath(), FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); +} +} + + + + +VolumeId fff::getVolumeSerialOs() //throw FileError +{ + return getFileId("/").volumeId; //throw FileError } +VolumeId fff::getVolumeSerialFfs() //throw FileError +{ + return getFileId(getExeFolderPath()).volumeId; //throw FileError +} bool fff::isPortableVersion() { - return !endsWith(getExecutablePathPf(), "/bin/"); //this check is a bit lame... + return false; //users want local installation type: https://freefilesync.org/forum/viewtopic.php?t=5750 + //try + //{ + // return getVolumeSerialFfs() != getVolumeSerialOs(); //throw FileError + //} + //catch (FileError&) {} + //assert(false); + //return false; } @@ -39,10 +65,10 @@ Zstring fff::getResourceDirPf() wxTheApp->SetAppName(L"FreeFileSync"); ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName)); - if (isPortableVersion()) - return getExecutablePathPf(); - else //use OS' standard paths - return appendSeparator(utfTo<Zstring>(wxStandardPathsBase::Get().GetResourcesDir())); + //if (isPortableVersion()) + return appendSeparator(getExeFolderParentPath()); + //else //use OS' standard paths + // return appendSeparator(utfTo<Zstring>(wxStandardPathsBase::Get().GetResourcesDir())); } @@ -53,24 +79,39 @@ Zstring fff::getConfigDirPathPf() wxTheApp->SetAppName(L"FreeFileSync"); ZEN_ON_SCOPE_EXIT(wxTheApp->SetAppName(appName)); + Zstring cfgFolderPath; if (isPortableVersion()) - return getExecutablePathPf(); - //use OS' standard paths - Zstring configDirPath = utfTo<Zstring>(wxStandardPaths::Get().GetUserDataDir()); - - try + cfgFolderPath = getExeFolderParentPath(); + else //OS standard path (XDG layout): ~/.config/FreeFileSync { - createDirectoryIfMissingRecursion(configDirPath); //throw FileError + //wxBug: wxStandardPaths::GetUserDataDir() does not honor FileLayout_XDG flag + wxStandardPaths::Get().SetFileLayout(wxStandardPaths::FileLayout_XDG); + cfgFolderPath = appendSeparator(utfTo<Zstring>(wxStandardPaths::Get().GetUserConfigDir())) + "FreeFileSync"; } - catch (FileError&) { assert(false); } - return appendSeparator(configDirPath); +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-variable" +#endif + static int initOnce = [&] //"magic static" is the lesser evil in this wxWidgets context... + { + try //create the config folder if not existing + create "Logs" subfolder while we're at it + { + createDirectoryIfMissingRecursion(appendSeparator(cfgFolderPath) + Zstr("Logs")); //throw FileError + } + catch (FileError&) { assert(false); } + return 0; + }(); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + return appendSeparator(cfgFolderPath); } //this function is called by RealTimeSync!!! Zstring fff::getFreeFileSyncLauncherPath() { - return getExecutablePathPf() + Zstr("FreeFileSync"); + return getExeFolderParentPath() + Zstr("/FreeFileSync"); } diff --git a/FreeFileSync/Source/base/ffs_paths.h b/FreeFileSync/Source/base/ffs_paths.h index 3cb4c07b..4be75986 100755 --- a/FreeFileSync/Source/base/ffs_paths.h +++ b/FreeFileSync/Source/base/ffs_paths.h @@ -8,6 +8,7 @@ #define FFS_PATHS_H_842759083425342534253 #include <zen/zstring.h> +#include <zen/file_id_def.h> namespace fff @@ -22,6 +23,10 @@ Zstring getConfigDirPathPf(); //config directory WITH trailing path separator bool isPortableVersion(); + +zen::VolumeId getVolumeSerialOs (); //throw FileError +zen::VolumeId getVolumeSerialFfs(); // + Zstring getFreeFileSyncLauncherPath(); //full path to application launcher C:\...\FreeFileSync.exe } diff --git a/FreeFileSync/Source/base/file_hierarchy.cpp b/FreeFileSync/Source/base/file_hierarchy.cpp index d6e65ebd..39af1e8e 100755 --- a/FreeFileSync/Source/base/file_hierarchy.cpp +++ b/FreeFileSync/Source/base/file_hierarchy.cpp @@ -20,8 +20,8 @@ std::wstring fff::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL AbstractPath tmpPathR = itemPathR; for (;;) { - std::optional<AbstractPath> parentPathL = AFS::getParentFolderPath(tmpPathL); - std::optional<AbstractPath> parentPathR = AFS::getParentFolderPath(tmpPathR); + std::optional<AbstractPath> parentPathL = AFS::getParentPath(tmpPathL); + std::optional<AbstractPath> parentPathR = AFS::getParentPath(tmpPathR); if (!parentPathL || !parentPathR) break; @@ -33,14 +33,14 @@ std::wstring fff::getShortDisplayNameForFolderPair(const AbstractPath& itemPathL tmpPathL = *parentPathL; tmpPathR = *parentPathR; - commonTrail = AFS::appendPaths(itemNameL, commonTrail, FILE_NAME_SEPARATOR); + commonTrail = nativeAppendPaths(itemNameL, commonTrail); } if (!commonTrail.empty()) return utfTo<std::wstring>(commonTrail); auto getLastComponent = [](const AbstractPath& itemPath) { - if (!AFS::getParentFolderPath(itemPath)) //= device root + if (!AFS::getParentPath(itemPath)) //= device root return AFS::getDisplayPath(itemPath); return utfTo<std::wstring>(AFS::getItemName(itemPath)); }; diff --git a/FreeFileSync/Source/base/file_hierarchy.h b/FreeFileSync/Source/base/file_hierarchy.h index 4d06f73b..0b466c44 100755 --- a/FreeFileSync/Source/base/file_hierarchy.h +++ b/FreeFileSync/Source/base/file_hierarchy.h @@ -18,7 +18,7 @@ #include <zen/stl_tools.h> #include <zen/file_id_def.h> #include "structures.h" -#include "hard_filter.h" +#include "path_filter.h" #include "../fs/abstract.h" @@ -184,10 +184,10 @@ private: virtual Zstring getRelativePathR() const = 0; // }; -template <> inline AbstractPath PathInformation::getAbstractPath<LEFT_SIDE >() const { return getAbstractPathL(); } +template <> inline AbstractPath PathInformation::getAbstractPath< LEFT_SIDE>() const { return getAbstractPathL(); } template <> inline AbstractPath PathInformation::getAbstractPath<RIGHT_SIDE>() const { return getAbstractPathR(); } -template <> inline Zstring PathInformation::getRelativePath<LEFT_SIDE >() const { return getRelativePathL(); } +template <> inline Zstring PathInformation::getRelativePath< LEFT_SIDE>() const { return getRelativePathL(); } template <> inline Zstring PathInformation::getRelativePath<RIGHT_SIDE>() const { return getRelativePathR(); } //------------------------------------------------------------------ @@ -286,7 +286,7 @@ public: bool folderAvailableLeft, const AbstractPath& folderPathRight, bool folderAvailableRight, - const HardFilter::FilterRef& filter, + const FilterRef& filter, CompareVariant cmpVar, int fileTimeTolerance, const std::vector<unsigned int>& ignoreTimeShiftMinutes) : @@ -303,7 +303,7 @@ public: template <SelectedSide side> void setAvailable(bool value); //update after creating the directory in FFS //get settings which were used while creating BaseFolderPair - const HardFilter& getFilter() const { return *filter_; } + const PathFilter& getFilter() const { return filter_.ref(); } CompareVariant getCompVariant() const { return cmpVar_; } int getFileTimeTolerance() const { return fileTimeTolerance_; } const std::vector<unsigned int>& getIgnoredTimeShift() const { return ignoreTimeShiftMinutes_; } @@ -314,7 +314,7 @@ private: AbstractPath getAbstractPathL() const override { return folderPathLeft_; } AbstractPath getAbstractPathR() const override { return folderPathRight_; } - const HardFilter::FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files! + const FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files! const CompareVariant cmpVar_; const int fileTimeTolerance_; const std::vector<unsigned int> ignoreTimeShiftMinutes_; @@ -480,7 +480,7 @@ private: FileSystemObject (const FileSystemObject&) = delete; FileSystemObject& operator=(const FileSystemObject&) = delete; - AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath<LEFT_SIDE >(), getRelativePath<LEFT_SIDE >()); } + AbstractPath getAbstractPathL() const override { return AFS::appendRelPath(base().getAbstractPath< LEFT_SIDE>(), getRelativePath< LEFT_SIDE>()); } AbstractPath getAbstractPathR() const override { return AFS::appendRelPath(base().getAbstractPath<RIGHT_SIDE>(), getRelativePath<RIGHT_SIDE>()); } virtual void removeObjectL() = 0; @@ -593,8 +593,8 @@ public: bool isSymlinkSrc); private: - Zstring getRelativePathL() const override { return AFS::appendPaths(parent().getRelativePath<LEFT_SIDE >(), getItemName<LEFT_SIDE >(), FILE_NAME_SEPARATOR); } - Zstring getRelativePathR() const override { return AFS::appendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR); } + Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< LEFT_SIDE>(), getItemName< LEFT_SIDE>()); } + Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>()); } SyncOperation applyMoveOptimization(SyncOperation op) const; @@ -637,8 +637,8 @@ public: int64_t lastWriteTimeSrc); private: - Zstring getRelativePathL() const override { return AFS::appendPaths(parent().getRelativePath<LEFT_SIDE >(), getItemName<LEFT_SIDE >(), FILE_NAME_SEPARATOR); } - Zstring getRelativePathR() const override { return AFS::appendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR); } + Zstring getRelativePathL() const override { return nativeAppendPaths(parent().getRelativePath< LEFT_SIDE>(), getItemName< LEFT_SIDE>()); } + Zstring getRelativePathR() const override { return nativeAppendPaths(parent().getRelativePath<RIGHT_SIDE>(), getItemName<RIGHT_SIDE>()); } void flip() override; void removeObjectL() override { attrL_ = LinkAttributes(); } @@ -841,7 +841,7 @@ void FileSystemObject::setSynced(const Zstring& itemName) cmpResult_ = FILE_EQUAL; setSyncDir(SyncDirection::NONE); - propagateChangedItemName<LEFT_SIDE >(itemNameOldL); + propagateChangedItemName< LEFT_SIDE>(itemNameOldL); propagateChangedItemName<RIGHT_SIDE>(itemNameOldR); } @@ -917,9 +917,9 @@ template <SelectedSide side> inline void ContainerObject::updateRelPathsRecursion(const FileSystemObject& fsAlias) { assert(SelectParam<side>::ref(relPathL_, relPathR_) != //perf: only call if actual item name changed! - AFS::appendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>(), FILE_NAME_SEPARATOR)); + nativeAppendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>())); - SelectParam<side>::ref(relPathL_, relPathR_) = AFS::appendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>(), FILE_NAME_SEPARATOR); + SelectParam<side>::ref(relPathL_, relPathR_) = nativeAppendPaths(fsAlias.parent().getRelativePath<side>(), fsAlias.getItemName<side>()); for (FolderPair& folder : subFolders_) folder.updateRelPathsRecursion<side>(folder); @@ -928,14 +928,14 @@ void ContainerObject::updateRelPathsRecursion(const FileSystemObject& fsAlias) inline ContainerObject::ContainerObject(const FileSystemObject& fsAlias) : - relPathL_(AFS::appendPaths(fsAlias.parent().relPathL_, fsAlias.getItemName<LEFT_SIDE>(), FILE_NAME_SEPARATOR)), + relPathL_(nativeAppendPaths(fsAlias.parent().relPathL_, fsAlias.getItemName<LEFT_SIDE>())), relPathR_( fsAlias.parent().relPathL_.c_str() == // fsAlias.parent().relPathR_.c_str() && //take advantage of FileSystemObject's Zstring reuse: - fsAlias.getItemName<LEFT_SIDE >().c_str() == //=> perf: 12% faster merge phase; -4% peak memory + fsAlias.getItemName< LEFT_SIDE>().c_str() == //=> perf: 12% faster merge phase; -4% peak memory fsAlias.getItemName<RIGHT_SIDE>().c_str() ? // relPathL_ : //ternary-WTF! (implicit copy-constructor call!!) => no big deal for a Zstring - AFS::appendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR)), + nativeAppendPaths(fsAlias.parent().relPathR_, fsAlias.getItemName<RIGHT_SIDE>())), base_(fsAlias.parent().base_) { assert(relPathL_.c_str() == relPathR_.c_str() || relPathL_ != relPathR_); diff --git a/FreeFileSync/Source/base/generate_logfile.cpp b/FreeFileSync/Source/base/generate_logfile.cpp index dbb8e42a..dcb621be 100755 --- a/FreeFileSync/Source/base/generate_logfile.cpp +++ b/FreeFileSync/Source/base/generate_logfile.cpp @@ -22,22 +22,22 @@ std::wstring generateLogHeader(const ProcessSummary& s, const ErrorLog& log, con //assemble summary box std::vector<std::wstring> summary; - //write header - std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE); + const std::wstring tabSpace(4, L' '); //4, the one true space count for tabs + + std::wstring headerLine = formatTime<std::wstring>(FORMAT_DATE); //+ L" [" + formatTime<std::wstring>(FORMAT_TIME, startTime + L"]"; if (!s.jobName.empty()) - headerLine += L" | " + s.jobName; - headerLine += L" | " + finalStatusMsg; + headerLine += L" " + s.jobName; summary.push_back(headerLine); summary.push_back(L""); + summary.push_back(tabSpace + finalStatusMsg); - const std::wstring tabSpace(4, L' '); //4, the one true space count for tabs const int errorCount = log.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR); const int warningCount = log.getItemCount(MSG_TYPE_WARNING); - if (errorCount > 0) summary.push_back(tabSpace + _("Error" ) + L": " + formatNumber(errorCount)); - if (warningCount > 0) summary.push_back(tabSpace + _("Warning") + L": " + formatNumber(warningCount)); + if (errorCount > 0) summary.push_back(tabSpace + _("Errors:") + L" " + formatNumber(errorCount)); + if (warningCount > 0) summary.push_back(tabSpace + _("Warnings:") + L" " + formatNumber(warningCount)); std::wstring itemsProc = tabSpace + _("Items processed:") + L" " + formatNumber(s.statsProcessed.items); //show always, even if 0! @@ -45,7 +45,7 @@ std::wstring generateLogHeader(const ProcessSummary& s, const ErrorLog& log, con summary.push_back(itemsProc); if ((s.statsTotal.items < 0 && s.statsTotal.bytes < 0) || //no total items/bytes: e.g. for pure folder comparison - s.statsProcessed == s.statsTotal) //...if everything was processed successfully + s.statsProcessed == s.statsTotal) //...if everything was processed successfully ; else summary.push_back(tabSpace + _("Items remaining:") + @@ -55,7 +55,7 @@ std::wstring generateLogHeader(const ProcessSummary& s, const ErrorLog& log, con const int64_t totalTimeSec = std::chrono::duration_cast<std::chrono::seconds>(s.totalTime).count(); summary.push_back(tabSpace + _("Total time:") + L" " + copyStringTo<std::wstring>(wxTimeSpan::Seconds(totalTimeSec).Format())); - //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-byte-UTF16 codes are usually wider than fixed width chars anyway! + //calculate max width, this considers UTF-16, not Unicode code points...but maybe good idea? those 2-byte-UTF16 chars are usually wider than fixed-width chars anyway! size_t sepLineLen = 0; for (const std::wstring& str : summary) sepLineLen = std::max(sepLineLen, str.size()); @@ -202,8 +202,8 @@ std::vector<LogFileInfo> getLogFiles(const AbstractPath& logFolderPath) //throw auto tsEnd = fi.itemName.end() - 4; if (tsBegin != tsEnd && tsEnd[-1] == STATUS_END_TOKEN) - tsEnd = search_last(tsBegin, tsEnd, - std::begin(STATUS_BEGIN_TOKEN), std::end(STATUS_BEGIN_TOKEN) - 1); + tsEnd = searchLast(tsBegin, tsEnd, + std::begin(STATUS_BEGIN_TOKEN), std::end(STATUS_BEGIN_TOKEN) - 1); if (tsEnd - tsBegin >= TIME_STAMP_LENGTH && tsEnd[-4] == Zstr('.') && @@ -261,6 +261,7 @@ void limitLogfileCount(const AbstractPath& logFolderPath, //throw FileError for (const LogFileInfo& lfi : logFiles) if (lfi.timeStamp < cutOffTime && logFilePathsToKeep.find(lfi.filePath) == logFilePathsToKeep.end()) //don't trim latest log files corresponding to last used config files! + //nitpicker's corner: what about path differences due to case? e.g. user-overriden log file path changed in case { if (notifyStatus) notifyStatus(_("Cleaning up log files:") + L" " + fmtPath(AFS::getDisplayPath(lfi.filePath))); try @@ -280,23 +281,6 @@ void limitLogfileCount(const AbstractPath& logFolderPath, //throw FileError Zstring fff::getDefaultLogFolderPath() { return getConfigDirPathPf() + Zstr("Logs") ; } -MessageType fff::getFinalMsgType(SyncResult finalStatus) -{ - switch (finalStatus) - { - case SyncResult::FINISHED_WITH_SUCCESS: - return MSG_TYPE_INFO; - case SyncResult::FINISHED_WITH_WARNINGS: - return MSG_TYPE_WARNING; - case SyncResult::FINISHED_WITH_ERROR: - case SyncResult::ABORTED: //= user cancel; *not* a MSG_TYPE_FATAL_ERROR! - return MSG_TYPE_ERROR; - } - assert(false); - return MSG_TYPE_FATAL_ERROR; -} - - AbstractPath fff::saveLogFile(const ProcessSummary& summary, //throw FileError const ErrorLog& log, const std::chrono::system_clock::time_point& syncStartTime, diff --git a/FreeFileSync/Source/base/generate_logfile.h b/FreeFileSync/Source/base/generate_logfile.h index dbb3cbd0..9892fa83 100755 --- a/FreeFileSync/Source/base/generate_logfile.h +++ b/FreeFileSync/Source/base/generate_logfile.h @@ -26,8 +26,6 @@ AbstractPath saveLogFile(const ProcessSummary& summary, //throw FileError int logfilesMaxAgeDays, const std::set<AbstractPath>& logFilePathsToKeep, const std::function<void(const std::wstring& msg)>& notifyStatus /*throw X*/); - -zen::MessageType getFinalMsgType(SyncResult finalStatus); } #endif //GENERATE_LOGFILE_H_931726432167489732164 diff --git a/FreeFileSync/Source/base/localization.cpp b/FreeFileSync/Source/base/localization.cpp index de1b7087..3b7faee3 100755 --- a/FreeFileSync/Source/base/localization.cpp +++ b/FreeFileSync/Source/base/localization.cpp @@ -147,7 +147,7 @@ std::vector<TranslationInfo> loadTranslations() assert(!lngHeader.localeName .empty()); assert(!lngHeader.flagFile .empty()); /* - Some ISO codes are used by multiple wxLanguage IDs which can lead to incorrect mapping!!! + Some ISO codes are used by multiple wxLanguage IDs which can lead to incorrect mapping by wxLocale::FindLanguageInfo()!!! => Identify by description, e.g. "Chinese (Traditional)". The following ids are affected: wxLANGUAGE_CHINESE_TRADITIONAL wxLANGUAGE_ENGLISH_UK @@ -330,6 +330,75 @@ wxLanguage mapLanguageDialect(wxLanguage language) } +//we need to interface with wxWidgets' translation handling for a few translations used in their internal source files +// => since there is no better API: dynamically generate a MO file and feed it to wxTranslation +class MemoryTranslationLoader : public wxTranslationsLoader +{ +public: + MemoryTranslationLoader(wxLanguage langId, std::map<std::string, std::wstring>&& transMapping) : + canonicalName_(wxLocale::GetLanguageCanonicalName(langId)) + { + //https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html + transMapping[""] = L"Content-Type: text/plain; charset=UTF-8\n"; + + const int headerSize = 28; + writeNumber<uint32_t>(moBuf_, 0x950412de); //magic number + writeNumber<uint32_t>(moBuf_, 0); //format version + writeNumber<uint32_t>(moBuf_, transMapping.size()); //string count + writeNumber<uint32_t>(moBuf_, headerSize); //string references offset: original + writeNumber<uint32_t>(moBuf_, headerSize + 8 * transMapping.size()); //string references offset: translation + writeNumber<uint32_t>(moBuf_, 0); //size of hashing table + writeNumber<uint32_t>(moBuf_, 0); //offset of hashing table + + const int stringsOffset = headerSize + 2 * 8 * transMapping.size(); + std::string stringsList; + + for (const auto& [original, translation] : transMapping) + { + writeNumber<uint32_t>(moBuf_, original.size()); //string length + writeNumber<uint32_t>(moBuf_, stringsOffset + stringsList.size()); //string offset + stringsList.append(original.c_str(), original.size() + 1); //include 0-termination + } + + for (const auto& item : transMapping) + { + const auto& translation = utfTo<std::string>(item.second); + writeNumber<uint32_t>(moBuf_, translation.size()); //string length + writeNumber<uint32_t>(moBuf_, stringsOffset + stringsList.size()); //string offset + stringsList.append(translation.c_str(), translation.size() + 1); //include 0-termination + } + + writeArray(moBuf_, stringsList.c_str(), stringsList.size()); + } + + wxMsgCatalog* LoadCatalog(const wxString& domain, const wxString& lang) override + { + //"lang" is NOT (exactly) what we return from GetAvailableTranslations(), but has a little "extra", e.g.: de_DE.WINDOWS-1252 or ar.WINDOWS-1252 + if (equalAsciiNoCase(extractIsoLangCode(lang), extractIsoLangCode(canonicalName_))) + return wxMsgCatalog::CreateFromData(wxScopedCharBuffer::CreateNonOwned(moBuf_.ref().c_str(), moBuf_.ref().size()), domain); + assert(false); + return nullptr; + } + + wxArrayString GetAvailableTranslations(const wxString& domain) const override + { + wxArrayString available; + available.push_back(canonicalName_); + return available; + } + +private: + static wxString extractIsoLangCode(wxString langCode) + { + langCode = beforeLast(langCode, L".", IF_MISSING_RETURN_ALL); + return beforeLast(langCode, L"_", IF_MISSING_RETURN_ALL); + } + + const wxString canonicalName_; + MemoryStreamOut<std::string> moBuf_; +}; + + //global wxWidgets localization: sets up C localization runtime as well! class wxWidgetsLocale { @@ -356,6 +425,7 @@ public: locale_->Init(wxLANGUAGE_DEFAULT); //use sys-lang to preserve sub-language specific rules (e.g. german swiss number punctation) else locale_->Init(lng); //have to use the supplied language to enable RTL layout different than user settings + locLng_ = lng; } @@ -404,7 +474,10 @@ void fff::setLanguage(wxLanguage lng) //throw FileError //load language file into buffer if (langFilePath.empty()) //if languageFile is empty, texts will be english by default + { setTranslator(nullptr); + lng = wxLANGUAGE_ENGLISH_US; + } else try { @@ -420,11 +493,25 @@ void fff::setLanguage(wxLanguage lng) //throw FileError } catch (plural::ParsingError&) { - throw FileError(replaceCpy<std::wstring>(L"%x: Invalid plural form definition", L"%x", fmtPath(langFilePath))); //user should never see this! + throw FileError(L"Invalid plural form definition: " + fmtPath(langFilePath)); //user should never see this! } //handle RTL swapping: we need wxWidgets to do this - wxWidgetsLocale::getInstance().init(langFilePath.empty() ? wxLANGUAGE_ENGLISH : lng); + wxWidgetsLocale::getInstance().init(lng); + + //add translation for wxWidgets-internal strings: + assert(wxTranslations::Get()); //already initialized by wxLocale + if (wxTranslations* wxtrans = wxTranslations::Get()) + { + std::map<std::string, std::wstring> transMapping = + { + }; + wxtrans->SetLanguage(lng); //!= wxLocale's language, which could be wxLANGUAGE_DEFAULT (see wxWidgetsLocale) + wxtrans->SetLoader(new MemoryTranslationLoader(lng, std::move(transMapping))); + const bool catalogAdded = wxtrans->AddCatalog(wxString(), lng); + (void)catalogAdded; + assert(catalogAdded); + } } diff --git a/FreeFileSync/Source/base/lock_holder.h b/FreeFileSync/Source/base/lock_holder.h index d4e3371c..7bc470ba 100755 --- a/FreeFileSync/Source/base/lock_holder.h +++ b/FreeFileSync/Source/base/lock_holder.h @@ -13,28 +13,28 @@ namespace fff //intermediate locks created by DirLock use this extension, too: const Zchar LOCK_FILE_ENDING[] = Zstr(".ffs_lock"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace! +//Attention: 1. call after having checked directory existence! +// 2. perf: remove folder aliases (e.g. case differences) *before* calling this function!!! + //hold locks for a number of directories without blocking during lock creation -//call after having checked directory existence! class LockHolder { public: - LockHolder(const std::set<Zstring, LessFilePath>& dirPathsExisting, //resolved paths - bool& warnDirectoryLockFailed, - ProcessCallback& pcb /*throw X*/) + LockHolder(const std::set<Zstring>& folderPaths, bool& warnDirectoryLockFailed, ProcessCallback& pcb /*throw X*/) { using namespace zen; - std::map<Zstring, FileError, LessFilePath> failedLocks; + std::map<Zstring, FileError> failedLocks; - for (const Zstring& dirpath : dirPathsExisting) + for (const Zstring& folderPath : folderPaths) try { //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages) - lockHolder_.emplace_back(appendSeparator(dirpath) + Zstr("sync") + LOCK_FILE_ENDING, + lockHolder_.emplace_back(appendSeparator(folderPath) + Zstr("sync") + LOCK_FILE_ENDING, [&](const std::wstring& msg) { pcb.reportStatus(msg); /*throw X*/ }, UI_UPDATE_INTERVAL / 2); //throw FileError } - catch (const FileError& e) { failedLocks.emplace(dirpath, e); } + catch (const FileError& e) { failedLocks.emplace(folderPath, e); } if (!failedLocks.empty()) { diff --git a/FreeFileSync/Source/base/norm_filter.h b/FreeFileSync/Source/base/norm_filter.h index c2606ffc..f96a0aef 100755 --- a/FreeFileSync/Source/base/norm_filter.h +++ b/FreeFileSync/Source/base/norm_filter.h @@ -7,7 +7,7 @@ #ifndef NORM_FILTER_H_974896787346251 #define NORM_FILTER_H_974896787346251 -#include "hard_filter.h" +#include "path_filter.h" #include "soft_filter.h" @@ -15,10 +15,10 @@ namespace fff { struct NormalizedFilter //grade-a filter: global/local filter settings combined, units resolved, ready for use { - NormalizedFilter(const HardFilter::FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {} + NormalizedFilter(const FilterRef& hf, const SoftFilter& sf) : nameFilter(hf), timeSizeFilter(sf) {} //"hard" filter: relevant during comparison, physically skips files - HardFilter::FilterRef nameFilter; + FilterRef nameFilter; //"soft" filter: relevant after comparison; equivalent to user selection SoftFilter timeSizeFilter; }; diff --git a/FreeFileSync/Source/base/parallel_scan.cpp b/FreeFileSync/Source/base/parallel_scan.cpp index 9ffb5f67..35951a37 100755 --- a/FreeFileSync/Source/base/parallel_scan.cpp +++ b/FreeFileSync/Source/base/parallel_scan.cpp @@ -316,7 +316,7 @@ private: struct TraverserConfig { const AbstractPath baseFolderPath; //thread-safe like an int! :) - const HardFilter::FilterRef filter; //always bound! + const FilterRef filter; const SymLinkHandling handleSymlinks; std::map<Zstring, std::wstring>& failedDirReads; @@ -396,7 +396,7 @@ void DirCallback::onFile(const AFS::FileInfo& fi) //throw ThreadInterruption //------------------------------------------------------------------------------------ //apply filter before processing (use relative name!) - if (!cfg_.filter->passFileFilter(fileRelPath)) + if (!cfg_.filter.ref().passFileFilter(fileRelPath)) return; //sync.ffs_db database and lock files are excluded via filter! @@ -431,7 +431,7 @@ std::shared_ptr<AFS::TraverserCallback> DirCallback::onFolder(const AFS::FolderI //------------------------------------------------------------------------------------ //apply filter before processing (use relative name!) bool childItemMightMatch = true; - const bool passFilter = cfg_.filter->passDirFilter(folderRelPath, &childItemMightMatch); + const bool passFilter = cfg_.filter.ref().passDirFilter(folderRelPath, &childItemMightMatch); if (!passFilter && !childItemMightMatch) return nullptr; //do NOT traverse subdirs //else: attention! ensure directory filtering is applied later to exclude actually filtered directories @@ -473,7 +473,7 @@ DirCallback::HandleLink DirCallback::onSymlink(const AFS::SymlinkInfo& si) //thr return LINK_SKIP; case SymLinkHandling::DIRECT: - if (cfg_.filter->passFileFilter(linkRelPath)) //always use file filter: Link type may not be "stable" on Linux! + if (cfg_.filter.ref().passFileFilter(linkRelPath)) //always use file filter: Link type may not be "stable" on Linux! { output_.addSubLink(si.itemName, LinkAttributes(si.modTime)); cfg_.acb.incItemsScanned(); //add 1 element to the progress indicator @@ -483,10 +483,10 @@ DirCallback::HandleLink DirCallback::onSymlink(const AFS::SymlinkInfo& si) //thr case SymLinkHandling::FOLLOW: //filter symlinks before trying to follow them: handle user-excluded broken symlinks! //since we don't know yet what type the symlink will resolve to, only do this when both filter variants agree: - if (!cfg_.filter->passFileFilter(linkRelPath)) + if (!cfg_.filter.ref().passFileFilter(linkRelPath)) { bool childItemMightMatch = true; - if (!cfg_.filter->passDirFilter(linkRelPath, &childItemMightMatch)) + if (!cfg_.filter.ref().passDirFilter(linkRelPath, &childItemMightMatch)) if (!childItemMightMatch) return LINK_SKIP; } @@ -520,7 +520,7 @@ DirCallback::HandleError DirCallback::reportError(const std::wstring& msg, size_ void fff::parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead, std::map<DirectoryKey, DirectoryValue>& output, - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, const TravErrorCb& onError, const TravStatusCb& onStatusUpdate, std::chrono::milliseconds cbInterval) { @@ -530,10 +530,10 @@ void fff::parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead, // => one worker thread *per device*: avoid excessive parallelism // => parallel folder traversal considers "parallel file operations" as specified by user // => (S)FTP: avoid hitting connection limits inadvertently - std::map<AbstractPath, std::set<DirectoryKey>> perDeviceFolders; + std::map<AfsDevice, std::set<DirectoryKey>> perDeviceFolders; for (const DirectoryKey& key : foldersToRead) - perDeviceFolders[AFS::getRootPath(key.folderPath)].insert(key); + perDeviceFolders[key.folderPath.afsDevice].insert(key); //communication channel used by threads AsyncCallback acb(perDeviceFolders.size() /*threadsToFinish*/, cbInterval); //manage life time: enclose InterruptibleThread's!!! @@ -543,17 +543,17 @@ void fff::parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead, ZEN_ON_SCOPE_FAIL( for (InterruptibleThread& wt : worker) wt.interrupt(); ); //interrupt all first, then join //init worker threads - for (const auto& [rootPath, dirKeys] : perDeviceFolders) + for (const auto& [afsDevice, dirKeys] : perDeviceFolders) { const int threadIdx = static_cast<int>(worker.size()); - const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, rootPath); + const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, afsDevice); std::map<DirectoryKey, DirectoryValue*> workload; for (const DirectoryKey& key : dirKeys) workload.emplace(key, &output[key]); //=> DirectoryValue* unshared for lock-free worker-thread access - worker.emplace_back([rootPath = rootPath /*clang bug :>*/, workload, threadIdx, &acb, parallelOps]() mutable + worker.emplace_back([afsDevice = afsDevice /*clang bug :>*/, workload, threadIdx, &acb, parallelOps]() mutable { setCurrentThreadName(("Comp Worker[" + numberTo<std::string>(threadIdx) + "]").c_str()); @@ -566,11 +566,10 @@ void fff::parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead, for (auto& [folderKey, folderVal] : workload) { - const std::vector<Zstring> relPath = split(AFS::getRootRelativePath(folderKey.folderPath), FILE_NAME_SEPARATOR, SplitType::SKIP_EMPTY); - assert(AFS::getRootPath(folderKey.folderPath) == rootPath); - travWorkload.emplace_back(relPath, std::make_shared<BaseDirCallback>(folderKey, *folderVal, acb, threadIdx, lastReportTime)); + assert(folderKey.folderPath.afsDevice == afsDevice); + travWorkload.emplace_back(folderKey.folderPath.afsPath, std::make_shared<BaseDirCallback>(folderKey, *folderVal, acb, threadIdx, lastReportTime)); } - AFS::traverseFolderRecursive(rootPath, travWorkload, parallelOps); //throw ThreadInterruption + AFS::traverseFolderRecursive(afsDevice, travWorkload, parallelOps); //throw ThreadInterruption }); } diff --git a/FreeFileSync/Source/base/parallel_scan.h b/FreeFileSync/Source/base/parallel_scan.h index dbb0c1d9..71dfd586 100755 --- a/FreeFileSync/Source/base/parallel_scan.h +++ b/FreeFileSync/Source/base/parallel_scan.h @@ -10,7 +10,7 @@ #include <map> #include <set> #include <chrono> -#include "hard_filter.h" +#include "path_filter.h" #include "structures.h" #include "file_hierarchy.h" @@ -20,7 +20,7 @@ namespace fff struct DirectoryKey { AbstractPath folderPath; - HardFilter::FilterRef filter; //always bound by design! + FilterRef filter; SymLinkHandling handleSymlinks = SymLinkHandling::EXCLUDE; }; @@ -31,11 +31,11 @@ bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) if (lhs.handleSymlinks != rhs.handleSymlinks) return lhs.handleSymlinks < rhs.handleSymlinks; - const int cmp = AbstractFileSystem::compareAbstractPath(lhs.folderPath, rhs.folderPath); + const int cmp = AbstractFileSystem::comparePath(lhs.folderPath, rhs.folderPath); if (cmp != 0) return cmp < 0; - return *lhs.filter < *rhs.filter; + return lhs.filter.ref() < rhs.filter.ref(); } @@ -51,15 +51,15 @@ struct DirectoryValue }; -//attention: ensure directory filtering is applied later to exclude filtered folders which have been kept as parent folders - +//Attention: 1. ensure directory filtering is applied later to exclude filtered folders which have been kept as parent folders +// 2. remove folder aliases (e.g. case differences) *before* calling this function!!! using TravErrorCb = std::function<AFS::TraverserCallback::HandleError(const std::wstring& msg, size_t retryNumber)>; using TravStatusCb = std::function< void (const std::wstring& statusLine, int itemsTotal)>; void parallelDeviceTraversal(const std::set<DirectoryKey>& foldersToRead, std::map<DirectoryKey, DirectoryValue>& output, - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, const TravErrorCb& onError, const TravStatusCb& onStatusUpdate, //NOT optional std::chrono::milliseconds cbInterval); } diff --git a/FreeFileSync/Source/base/parse_lng.h b/FreeFileSync/Source/base/parse_lng.h index c282c2de..e1e5e57d 100755 --- a/FreeFileSync/Source/base/parse_lng.h +++ b/FreeFileSync/Source/base/parse_lng.h @@ -249,7 +249,7 @@ public: Token getNextToken() { //skip whitespace - pos_ = std::find_if(pos_, stream_.end(), std::not_fn(zen::isWhiteSpace<char>)); + pos_ = std::find_if_not(pos_, stream_.end(), zen::isWhiteSpace<char>); if (pos_ == stream_.end()) return Token(Token::TK_END); diff --git a/FreeFileSync/Source/base/parse_plural.h b/FreeFileSync/Source/base/parse_plural.h index e735c421..d80a0dca 100755 --- a/FreeFileSync/Source/base/parse_plural.h +++ b/FreeFileSync/Source/base/parse_plural.h @@ -211,7 +211,7 @@ public: Token getNextToken() //throw ParsingError { //skip whitespace - pos_ = std::find_if(pos_, stream_.end(), std::not_fn(zen::isWhiteSpace<char>)); + pos_ = std::find_if_not(pos_, stream_.end(), zen::isWhiteSpace<char>); if (pos_ == stream_.end()) return Token::TK_END; @@ -223,7 +223,7 @@ public: return Token(tokenEnum); } - auto digitEnd = std::find_if(pos_, stream_.end(), std::not_fn(zen::isDigit<char>)); + auto digitEnd = std::find_if_not(pos_, stream_.end(), zen::isDigit<char>); if (pos_ == digitEnd) throw ParsingError(); //unknown token diff --git a/FreeFileSync/Source/base/path_filter.cpp b/FreeFileSync/Source/base/path_filter.cpp new file mode 100755 index 00000000..79cb4a0f --- /dev/null +++ b/FreeFileSync/Source/base/path_filter.cpp @@ -0,0 +1,366 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#include "path_filter.h" +#include <set> +#include <stdexcept> +#include <vector> +#include <typeinfo> +#include <iterator> + +using namespace zen; +using namespace fff; + + +bool fff::operator<(const PathFilter& lhs, const PathFilter& rhs) +{ + if (typeid(lhs) != typeid(rhs)) + return typeid(lhs).before(typeid(rhs)); //in worst case, order is guaranteed to be stable only during each program run + + //lhs, rhs have same type: + return lhs.cmpLessSameType(rhs); +} + + +namespace +{ +//constructing Zstrings of these in addFilterEntry becomes perf issue for large filter lists => use global POD! +const Zchar sepAsterisk[] = Zstr("/*"); +const Zchar asteriskSep[] = Zstr("*/"); +static_assert(FILE_NAME_SEPARATOR == '/'); + + +void addFilterEntry(const Zstring& filterPhrase, std::vector<Zstring>& masksFileFolder, std::vector<Zstring>& masksFolder) +{ + warn_static("3. ignore path separator => bug regarding copyFilterAddingExclusion() after failed directory reads when dir has path separator from other OS in name") + + //normalize filter input: 1. ignore Unicode normalization form 2. ignore case 3. ignore path separator + Zstring filterFmt = makeUpperCopy(filterPhrase); + if constexpr (FILE_NAME_SEPARATOR != Zstr('/' )) replace(filterFmt, Zstr('/'), FILE_NAME_SEPARATOR); + if constexpr (FILE_NAME_SEPARATOR != Zstr('\\')) replace(filterFmt, Zstr('\\'), FILE_NAME_SEPARATOR); + /* + phrase | action + +---------+-------- + | \blah | remove \ + | \*blah | remove \ + | \*\blah | remove \ + | \*\* | remove \ + +---------+-------- + | *blah | + | *\blah | -> add blah + | *\*blah | -> add *blah + +---------+-------- + | blah\ | remove \; folder only + | blah*\ | remove \; folder only + | blah\*\ | remove \; folder only + +---------+-------- + | blah* | + | blah\* | remove \*; folder only + | blah*\* | remove \*; folder only + +---------+-------- + */ + auto processTail = [&masksFileFolder, &masksFolder](const Zstring& phrase) + { + if (endsWith(phrase, FILE_NAME_SEPARATOR) || //only relevant for folder filtering + endsWith(phrase, sepAsterisk)) // abc\* + { + const Zstring dirPhrase = beforeLast(phrase, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); + if (!dirPhrase.empty()) + masksFolder.push_back(dirPhrase); + } + else if (!phrase.empty()) + masksFileFolder.push_back(phrase); + }; + + if (startsWith(filterFmt, FILE_NAME_SEPARATOR)) // \abc + processTail(afterFirst(filterFmt, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); + else + { + processTail(filterFmt); + if (startsWith(filterFmt, asteriskSep)) // *\abc + processTail(afterFirst(filterFmt, asteriskSep, IF_MISSING_RETURN_NONE)); + } +} + + +template <class Char> inline +const Char* cStringFind(const Char* str, Char ch) //= strchr(), wcschr() +{ + for (;;) + { + const Char s = *str; + if (s == ch) //ch is allowed to be 0 by contract! must return end of string in this case + return str; + + if (s == 0) + return nullptr; + ++str; + } +} + + +/* +struct FullMatch +{ + static bool matchesMaskEnd (const Zchar* path) { return *path == 0; } + static bool matchesMaskStar(const Zchar* path) { return true; } +}; +*/ + +struct ParentFolderMatch //strict match of parent folder path! +{ + static bool matchesMaskEnd (const Zchar* path) { return *path == FILE_NAME_SEPARATOR; } + static bool matchesMaskStar(const Zchar* path) { return cStringFind(path, FILE_NAME_SEPARATOR) != nullptr; } +}; + +struct AnyMatch +{ + static bool matchesMaskEnd (const Zchar* path) { return *path == 0 || *path == FILE_NAME_SEPARATOR; } + static bool matchesMaskStar(const Zchar* path) { return true; } +}; + + +template <class PathEndMatcher> +bool matchesMask(const Zchar* path, const Zchar* mask) +{ + for (;; ++mask, ++path) + { + Zchar m = *mask; + switch (m) + { + case 0: + return PathEndMatcher::matchesMaskEnd(path); + + case Zstr('?'): + if (*path == 0) + return false; + break; + + case Zstr('*'): + do //advance mask to next non-* char + { + m = *++mask; + } + while (m == Zstr('*')); + + if (m == 0) //mask ends with '*': + return PathEndMatcher::matchesMaskStar(path); + + //*? - pattern + if (m == Zstr('?')) + { + ++mask; + while (*path++ != 0) + if (matchesMask<PathEndMatcher>(path, mask)) + return true; + return false; + } + + //*[letter] - pattern + ++mask; + for (;;) + { + path = cStringFind(path, m); + if (!path) + return false; + + ++path; + if (matchesMask<PathEndMatcher>(path, mask)) + return true; + } + + default: + if (*path != m) + return false; + } + } +} + + +//returns true if string matches at least the beginning of mask +inline +bool matchesMaskBegin(const Zchar* str, const Zchar* mask) +{ + for (;; ++mask, ++str) + { + const Zchar m = *mask; + switch (m) + { + case 0: + return *str == 0; + + case Zstr('?'): + if (*str == 0) + return true; + break; + + case Zstr('*'): + return true; + + default: + if (*str != m) + return *str == 0; + } + } +} + + +template <class PathEndMatcher> inline +bool matchesMask(const Zstring& name, const std::vector<Zstring>& masks) +{ + return std::any_of(masks.begin(), masks.end(), [&](const Zstring& mask) { return matchesMask<PathEndMatcher>(name.c_str(), mask.c_str()); }); +} + + +inline +bool matchesMaskBegin(const Zstring& name, const std::vector<Zstring>& masks) +{ + return std::any_of(masks.begin(), masks.end(), [&](const Zstring& mask) { return matchesMaskBegin(name.c_str(), mask.c_str()); }); +} +} + + +std::vector<Zstring> fff::splitByDelimiter(const Zstring& filterPhrase) +{ + //delimiters may be FILTER_ITEM_SEPARATOR or '\n' + std::vector<Zstring> output; + + for (const Zstring& str : split(filterPhrase, FILTER_ITEM_SEPARATOR, SplitType::SKIP_EMPTY)) //split by less common delimiter first (create few, large strings) + for (Zstring entry : split(str, Zstr('\n'), SplitType::SKIP_EMPTY)) + { + trim(entry); + if (!entry.empty()) + output.push_back(std::move(entry)); + } + + return output; +} + +//################################################################################################# + +NameFilter::NameFilter(const Zstring& includePhrase, const Zstring& excludePhrase) +{ + //setup include/exclude filters for files and directories + for (const Zstring& entry : splitByDelimiter(includePhrase)) addFilterEntry(entry, includeMasksFileFolder, includeMasksFolder); + for (const Zstring& entry : splitByDelimiter(excludePhrase)) addFilterEntry(entry, excludeMasksFileFolder, excludeMasksFolder); + + removeDuplicates(includeMasksFileFolder); + removeDuplicates(includeMasksFolder); + removeDuplicates(excludeMasksFileFolder); + removeDuplicates(excludeMasksFolder); +} + + +void NameFilter::addExclusion(const Zstring& excludePhrase) +{ + for (const Zstring& entry : splitByDelimiter(excludePhrase)) addFilterEntry(entry, excludeMasksFileFolder, excludeMasksFolder); + + removeDuplicates(excludeMasksFileFolder); + removeDuplicates(excludeMasksFolder); +} + + +bool NameFilter::passFileFilter(const Zstring& relFilePath) const +{ + assert(!startsWith(relFilePath, FILE_NAME_SEPARATOR)); + + //normalize input: 1. ignore Unicode normalization form 2. ignore case + const Zstring& pathFmt = makeUpperCopy(relFilePath); + + if (matchesMask<AnyMatch >(pathFmt, excludeMasksFileFolder) || //either full match on file or partial match on any parent folder + matchesMask<ParentFolderMatch>(pathFmt, excludeMasksFolder)) //partial match on any parent folder only + return false; + + return matchesMask<AnyMatch >(pathFmt, includeMasksFileFolder) || + matchesMask<ParentFolderMatch>(pathFmt, includeMasksFolder); +} + + +bool NameFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const +{ + assert(!startsWith(relDirPath, FILE_NAME_SEPARATOR)); + assert(!childItemMightMatch || *childItemMightMatch); //check correct usage + + //normalize input: 1. ignore Unicode normalization form 2. ignore case + const Zstring& pathFmt = makeUpperCopy(relDirPath); + + if (matchesMask<AnyMatch>(pathFmt, excludeMasksFileFolder) || + matchesMask<AnyMatch>(pathFmt, excludeMasksFolder)) + { + if (childItemMightMatch) + *childItemMightMatch = false; //perf: no need to traverse deeper; subfolders/subfiles would be excluded by filter anyway! + /* + Attention: the design choice that "childItemMightMatch" is optional implies that the filter must provide correct results no matter if this + value is considered by the client! + In particular, if *childItemMightMatch == false, then any filter evaluations for child items must also return "false"! + This is not a problem for folder traversal which stops at the first *childItemMightMatch == false anyway, but other code continues recursing further, + e.g. the database update code in db_file.cpp recurses unconditionally without filter check! It's possible to construct edge cases with incorrect + behavior if "childItemMightMatch" were not optional: + 1. two folders including a subfolder with some files are in sync with up-to-date database files + 2. deny access to this subfolder on both sides and start sync ignoring errors + 3. => database entries of this subfolder are incorrectly deleted! (if sub-folder is excluded, but child items are not!) + */ + return false; + } + + if (!matchesMask<AnyMatch>(pathFmt, includeMasksFileFolder) && + !matchesMask<AnyMatch>(pathFmt, includeMasksFolder)) + { + if (childItemMightMatch) + { + const Zstring& childPathBegin = pathFmt + FILE_NAME_SEPARATOR; + + *childItemMightMatch = matchesMaskBegin(childPathBegin, includeMasksFileFolder) || //might match a file or folder in subdirectory + matchesMaskBegin(childPathBegin, includeMasksFolder); // + } + return false; + } + + return true; +} + + +bool NameFilter::isNull(const Zstring& includePhrase, const Zstring& excludePhrase) +{ + const Zstring include = trimCpy(includePhrase); + const Zstring exclude = trimCpy(excludePhrase); + + return include == Zstr("*") && exclude.empty(); + //return NameFilter(includePhrase, excludePhrase).isNull(); -> very expensive for huge lists +} + + +bool NameFilter::isNull() const +{ + return includeMasksFileFolder.size() == 1 && includeMasksFileFolder[0] == Zstr("*") && + includeMasksFolder .empty() && + excludeMasksFileFolder.empty() && + excludeMasksFolder .empty(); + //avoid static non-POD null-NameFilter instance; instead test manually and verify function on startup: +} + + + +bool NameFilter::cmpLessSameType(const PathFilter& other) const +{ + assert(typeid(*this) == typeid(other)); //always given in this context! + + const NameFilter& otherNameFilt = static_cast<const NameFilter&>(other); + + if (includeMasksFileFolder != otherNameFilt.includeMasksFileFolder) + return includeMasksFileFolder < otherNameFilt.includeMasksFileFolder; + + if (includeMasksFolder != otherNameFilt.includeMasksFolder) + return includeMasksFolder < otherNameFilt.includeMasksFolder; + + if (excludeMasksFileFolder != otherNameFilt.excludeMasksFileFolder) + return excludeMasksFileFolder < otherNameFilt.excludeMasksFileFolder; + + if (excludeMasksFolder != otherNameFilt.excludeMasksFolder) + return excludeMasksFolder < otherNameFilt.excludeMasksFolder; + + return false; //all equal +} diff --git a/FreeFileSync/Source/base/path_filter.h b/FreeFileSync/Source/base/path_filter.h new file mode 100755 index 00000000..0b80fbce --- /dev/null +++ b/FreeFileSync/Source/base/path_filter.h @@ -0,0 +1,239 @@ +// ***************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 * +// * Copyright (C) Zenju (zenju AT freefilesync DOT org) - All Rights Reserved * +// ***************************************************************************** + +#ifndef HARD_FILTER_H_825780275842758345 +#define HARD_FILTER_H_825780275842758345 + +#include <vector> +#include <memory> +#include <zen/zstring.h> + + +namespace fff +{ +//------------------------------------------------------------------ +/* +Semantics of PathFilter: +1. using it creates a NEW folder hierarchy! -> must be considered by <Two way> variant! +2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! + + class hierarchy: + + PathFilter (interface) + /|\ + _________|_____________ + | | | +NullFilter NameFilter CombinedFilter +*/ +class PathFilter; +using FilterRef = zen::SharedRef<const PathFilter>; //always bound by design! Thread-safety: internally synchronized! + +class PathFilter //interface for filtering +{ +public: + virtual ~PathFilter() {} + + virtual bool passFileFilter(const Zstring& relFilePath) const = 0; + virtual bool passDirFilter (const Zstring& relDirPath, bool* childItemMightMatch) const = 0; + //childItemMightMatch: file/dir in subdirectories could(!) match + //note: this hint is only set if passDirFilter returns false! + + virtual bool isNull() const = 0; //filter is equivalent to NullFilter + + virtual FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const = 0; + +private: + friend bool operator<(const PathFilter& lhs, const PathFilter& rhs); + + virtual bool cmpLessSameType(const PathFilter& other) const = 0; //typeid(*this) == typeid(other) in this context! +}; + +bool operator<(const PathFilter& lhs, const PathFilter& rhs); //GCC: friend-declaration is not a "proper" declaration +inline bool operator==(const PathFilter& lhs, const PathFilter& rhs) { return !(lhs < rhs) && !(rhs < lhs); } +inline bool operator!=(const PathFilter& lhs, const PathFilter& rhs) { return !(lhs == rhs); } + + +//small helper method: merge two hard filters (thereby remove Null-filters) +FilterRef combineFilters(const FilterRef& first, const FilterRef& second); + + +class NullFilter : public PathFilter //no filtering at all +{ +public: + bool passFileFilter(const Zstring& relFilePath) const override { return true; } + bool passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const override; + bool isNull() const override { return true; } + FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const override; + +private: + bool cmpLessSameType(const PathFilter& other) const override; +}; + + +class NameFilter : public PathFilter //filter by base-relative file path +{ +public: + NameFilter(const Zstring& includePhrase, const Zstring& excludePhrase); + + void addExclusion(const Zstring& excludePhrase); + + bool passFileFilter(const Zstring& relFilePath) const override; + bool passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const override; + + bool isNull() const override; + static bool isNull(const Zstring& includePhrase, const Zstring& excludePhrase); //*fast* check without expensive NameFilter construction! + FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const override; + +private: + bool cmpLessSameType(const PathFilter& other) const override; + + std::vector<Zstring> includeMasksFileFolder; // + std::vector<Zstring> includeMasksFolder; //upper-case + Unicode-normalized by construction + std::vector<Zstring> excludeMasksFileFolder; // + std::vector<Zstring> excludeMasksFolder; // +}; + + +class CombinedFilter : public PathFilter //combine two filters to match if and only if both match +{ +public: + CombinedFilter(const NameFilter& first, const NameFilter& second) : first_(first), second_(second) { assert(!first.isNull() && !second.isNull()); } //if either is null, then wy use CombinedFilter? + + bool passFileFilter(const Zstring& relFilePath) const override; + bool passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const override; + bool isNull() const override; + FilterRef copyFilterAddingExclusion(const Zstring& excludePhrase) const override; + +private: + bool cmpLessSameType(const PathFilter& other) const override; + + const NameFilter first_; + const NameFilter second_; +}; + +const Zchar FILTER_ITEM_SEPARATOR = Zstr('|'); + + + + + +//--------------- inline implementation --------------------------------------- +inline +bool NullFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const +{ + assert(!childItemMightMatch || *childItemMightMatch); //check correct usage + return true; +} + + +inline +bool NullFilter::cmpLessSameType(const PathFilter& other) const +{ + assert(typeid(*this) == typeid(other)); //always given in this context! + return false; +} + + +inline +FilterRef NullFilter::copyFilterAddingExclusion(const Zstring& excludePhrase) const +{ + auto filter = zen::makeSharedRef<NameFilter>(Zstr("*"), excludePhrase); + if (filter.ref().isNull()) + return zen::makeSharedRef<const NullFilter>(); + return filter; +} + + +inline +FilterRef NameFilter::copyFilterAddingExclusion(const Zstring& excludePhrase) const +{ + auto tmp = zen::makeSharedRef<NameFilter>(*this); + tmp.ref().addExclusion(excludePhrase); + return tmp; +} + + +inline +bool CombinedFilter::passFileFilter(const Zstring& relFilePath) const +{ + return first_ .passFileFilter(relFilePath) && //short-circuit behavior + second_.passFileFilter(relFilePath); +} + + +inline +bool CombinedFilter::passDirFilter(const Zstring& relDirPath, bool* childItemMightMatch) const +{ + if (first_.passDirFilter(relDirPath, childItemMightMatch)) + return second_.passDirFilter(relDirPath, childItemMightMatch); + else + { + if (childItemMightMatch && *childItemMightMatch) + second_.passDirFilter(relDirPath, childItemMightMatch); + return false; + } +} + + +inline +bool CombinedFilter::isNull() const +{ + return first_.isNull() && second_.isNull(); +} + + +inline +FilterRef CombinedFilter::copyFilterAddingExclusion(const Zstring& excludePhrase) const +{ + NameFilter tmp(first_); + tmp.addExclusion(excludePhrase); + + return zen::makeSharedRef<CombinedFilter>(tmp, second_); +} + + +inline +bool CombinedFilter::cmpLessSameType(const PathFilter& other) const +{ + assert(typeid(*this) == typeid(other)); //always given in this context! + + const CombinedFilter& otherCombFilt = static_cast<const CombinedFilter&>(other); + + if (first_ != otherCombFilt.first_) + return first_ < otherCombFilt.first_; + + return second_ < otherCombFilt.second_; +} + + +inline +FilterRef constructFilter(const Zstring& includePhrase, + const Zstring& excludePhrase, + const Zstring& includePhrase2, + const Zstring& excludePhrase2) +{ + if (NameFilter::isNull(includePhrase, Zstring())) + { + auto filterTmp = zen::makeSharedRef<NameFilter>(includePhrase2, excludePhrase + Zstr("\n") + excludePhrase2); + if (filterTmp.ref().isNull()) + return zen::makeSharedRef<NullFilter>(); + + return filterTmp; + } + else + { + if (NameFilter::isNull(includePhrase2, Zstring())) + return zen::makeSharedRef<NameFilter>(includePhrase, excludePhrase + Zstr("\n") + excludePhrase2); + else + return zen::makeSharedRef<CombinedFilter>(NameFilter(includePhrase, excludePhrase + Zstr("\n") + excludePhrase2), NameFilter(includePhrase2, Zstring())); + } +} + + +std::vector<Zstring> splitByDelimiter(const Zstring& filterPhrase); //keep external linkage for unit test +} + +#endif //HARD_FILTER_H_825780275842758345 diff --git a/FreeFileSync/Source/base/perf_check.cpp b/FreeFileSync/Source/base/perf_check.cpp index 7ea523e8..9493a8a2 100755 --- a/FreeFileSync/Source/base/perf_check.cpp +++ b/FreeFileSync/Source/base/perf_check.cpp @@ -67,7 +67,7 @@ std::optional<std::wstring> PerfCheck::getBytesPerSecond() const const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeSpeed_); if (!numeric::isNull(timeDelta)) - return formatFilesizeShort(numeric::round(bytesDelta / timeDelta)) + _("/sec"); + return replaceCpy(_("%x/sec"), L"%x", formatFilesizeShort(numeric::round(bytesDelta / timeDelta))); return {}; } @@ -78,7 +78,7 @@ std::optional<std::wstring> PerfCheck::getItemsPerSecond() const const auto [timeDelta, itemsDelta, bytesDelta] = getBlockDeltas(windowSizeSpeed_); if (!numeric::isNull(timeDelta)) - return replaceCpy(_("%x items/sec"), L"%x", formatTwoDigitPrecision(itemsDelta / timeDelta)); + return replaceCpy(_("%x/sec"), L"%x", replaceCpy(_("%x items"), L"%x", formatTwoDigitPrecision(itemsDelta / timeDelta))); return {}; } diff --git a/FreeFileSync/Source/base/process_xml.cpp b/FreeFileSync/Source/base/process_xml.cpp index e3d1b89f..b8081253 100755 --- a/FreeFileSync/Source/base/process_xml.cpp +++ b/FreeFileSync/Source/base/process_xml.cpp @@ -999,7 +999,7 @@ void readConfig(const XmlIn& in, DirectionConfig& dirCfg) } -void readConfig(const XmlIn& in, SyncConfig& syncCfg, std::map<AbstractPath, size_t>& deviceParallelOps, int formatVer) +void readConfig(const XmlIn& in, SyncConfig& syncCfg, std::map<AfsDevice, size_t>& deviceParallelOps, int formatVer) { readConfig(in, syncCfg.directionCfg); @@ -1086,7 +1086,7 @@ void readConfig(const XmlIn& in, FilterConfig& filter, int formatVer) } -void readConfig(const XmlIn& in, LocalPairConfig& lpc, std::map<AbstractPath, size_t>& deviceParallelOps, int formatVer) +void readConfig(const XmlIn& in, LocalPairConfig& lpc, std::map<AfsDevice, size_t>& deviceParallelOps, int formatVer) { //read folder pairs in["Left" ](lpc.folderPathPhraseLeft); @@ -1884,7 +1884,7 @@ void writeConfig(const DirectionConfig& dirCfg, XmlOut& out) } -void writeConfig(const SyncConfig& syncCfg, const std::map<AbstractPath, size_t>& deviceParallelOps, XmlOut& out) +void writeConfig(const SyncConfig& syncCfg, const std::map<AfsDevice, size_t>& deviceParallelOps, XmlOut& out) { writeConfig(syncCfg.directionCfg, out); @@ -1921,7 +1921,7 @@ void writeConfig(const FilterConfig& filter, XmlOut& out) } -void writeConfig(const LocalPairConfig& lpc, const std::map<AbstractPath, size_t>& deviceParallelOps, XmlOut& out) +void writeConfig(const LocalPairConfig& lpc, const std::map<AfsDevice, size_t>& deviceParallelOps, XmlOut& out) { XmlOut outPair = out.ref().addChild("Pair"); diff --git a/FreeFileSync/Source/base/resolve_path.cpp b/FreeFileSync/Source/base/resolve_path.cpp index 49699fa4..b43d3463 100755 --- a/FreeFileSync/Source/base/resolve_path.cpp +++ b/FreeFileSync/Source/base/resolve_path.cpp @@ -61,7 +61,7 @@ Zstring resolveRelativePath(const Zstring& relativePath) if (startsWith(relativePath, "~/")) return appendSeparator(*homeDir) + afterFirst(relativePath, '/', IF_MISSING_RETURN_NONE); - else if (relativePath == "~") + else //relativePath == "~" return *homeDir; } @@ -173,7 +173,7 @@ Zstring expandVolumeName(Zstring pathPhrase) // [volname]:\folder [volnam } -void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, LessFilePath>& output) +void getFolderAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, LessNativePath>& output) { //3. environment variables: C:\Users\<user> -> %UserProfile% @@ -203,20 +203,20 @@ void getDirectoryAliasesRecursive(const Zstring& pathPhrase, std::set<Zstring, L const Zstring pathExp = fff::expandMacros(pathPhrase); if (pathExp != pathPhrase) if (output.insert(pathExp).second) - getDirectoryAliasesRecursive(pathExp, output); //recurse! + getFolderAliasesRecursive(pathExp, output); //recurse! } } } -std::vector<Zstring> fff::getDirectoryAliases(const Zstring& folderPathPhrase) +std::vector<Zstring> fff::getFolderPathAliases(const Zstring& folderPathPhrase) { - const Zstring dirPath = trimCpy(folderPathPhrase, true, false); + const Zstring dirPath = trimCpy(folderPathPhrase); if (dirPath.empty()) return {}; - std::set<Zstring, LessFilePath> tmp; - getDirectoryAliasesRecursive(dirPath, tmp); + std::set<Zstring, LessNativePath> tmp; + getFolderAliasesRecursive(dirPath, tmp); tmp.erase(dirPath); tmp.erase(Zstring()); @@ -233,9 +233,7 @@ Zstring fff::getResolvedFilePath(const Zstring& pathPhrase) //noexcept path = expandMacros(path); //expand before trimming! //remove leading/trailing whitespace before allowing misinterpretation in applyLongPathPrefix() - trim(path, true, false); - //don't remove all whitespace from right, e.g. 0xa0 may be used as part of a folder name - trim(path, false, true, [](Zchar c) { return c == Zstr(' '); }); + trim(path); //attention: don't remove all whitespace from right, e.g. 0xa0 may be used as part of a folder name path = expandVolumeName(path); //may block for slow USB sticks and idle HDDs! diff --git a/FreeFileSync/Source/base/resolve_path.h b/FreeFileSync/Source/base/resolve_path.h index dc23d1de..8b8f131e 100755 --- a/FreeFileSync/Source/base/resolve_path.h +++ b/FreeFileSync/Source/base/resolve_path.h @@ -27,7 +27,7 @@ Zstring getResolvedFilePath(const Zstring& pathPhrase); //noexcept //macro substitution only Zstring expandMacros(const Zstring& text); -std::vector<Zstring> getDirectoryAliases(const Zstring& folderPathPhrase); //may block for slow USB sticks when resolving [<volume name>] +std::vector<Zstring> getFolderPathAliases(const Zstring& folderPathPhrase); //may block for slow USB sticks when resolving [<volume name>] } diff --git a/FreeFileSync/Source/base/status_handler_impl.h b/FreeFileSync/Source/base/status_handler_impl.h index af578b82..bf4d7789 100755 --- a/FreeFileSync/Source/base/status_handler_impl.h +++ b/FreeFileSync/Source/base/status_handler_impl.h @@ -390,15 +390,15 @@ struct ParallelContext namespace { void massParallelExecute(const std::vector<std::pair<AbstractPath, ParallelWorkItem>>& workload, - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, const std::string& threadGroupName, ProcessCallback& callback /*throw X*/) { using namespace zen; - std::map<AbstractPath, std::vector<const std::pair<AbstractPath, ParallelWorkItem>*>> perDeviceWorkload; + std::map<AfsDevice, std::vector<const std::pair<AbstractPath, ParallelWorkItem>*>> perDeviceWorkload; for (const auto& item : workload) - perDeviceWorkload[AFS::getRootPath(item.first)].push_back(&item); + perDeviceWorkload[item.first.afsDevice].push_back(&item); struct ThreadGroupContext { @@ -412,29 +412,29 @@ void massParallelExecute(const std::vector<std::pair<AbstractPath, ParallelWorkI AsyncCallback acb; //manage life time: enclose ThreadGroup's!!! std::atomic<int> activeDeviceCount(perDeviceWorkload.size()); // - Protected<std::map<AbstractPath, ThreadGroupContext>*> deviceThreadGroupsShared; // + Protected<std::map<AfsDevice, ThreadGroupContext>*> deviceThreadGroupsShared; // //--------------------------------------------------------------------------------------------------------- - std::map<AbstractPath, ThreadGroupContext> deviceThreadGroups; //worker threads live here... + std::map<AfsDevice, ThreadGroupContext> deviceThreadGroups; //worker threads live here... //--------------------------------------------------------------------------------------------------------- //Attention: carefully orchestrate access to deviceThreadGroups and its contained worker threads! e.g. synchronize potential access during ~DeviceThreadGroup! - for (const auto& [rootPath, wl] : perDeviceWorkload) + for (const auto& [afsDevice, wl] : perDeviceWorkload) { - const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, rootPath); + const size_t parallelOps = getDeviceParallelOps(deviceParallelOps, afsDevice); const size_t statusPrio = deviceThreadGroups.size(); - auto scheduleExtraTask = [&acb, &deviceThreadGroupsShared, rootPath = rootPath /*clang bug :>*/](const AfsPath& afsPath, const ParallelWorkItem& task) + auto scheduleExtraTask = [&acb, &deviceThreadGroupsShared, afsDevice = afsDevice /*clang bug :>*/](const AfsPath& afsPath, const ParallelWorkItem& task) { - const AbstractPath& itemPath = AFS::appendRelPath(rootPath, afsPath.value); + const AbstractPath itemPath(afsDevice, afsPath); deviceThreadGroupsShared.access([&](auto* deviceThreadGroupsPtr) { if (!deviceThreadGroupsPtr) throw ThreadInterruption(); - ThreadGroupContext& ctx = deviceThreadGroupsPtr->find(rootPath)->second; //exists after construction above! + ThreadGroupContext& ctx = deviceThreadGroupsPtr->find(afsDevice)->second; //exists after construction above! ctx.threadGroup.run([&acb, statusPrio = ctx.statusPrio, itemPath, task, &scheduleExtraTask = ctx.scheduleExtraTask] { @@ -446,10 +446,10 @@ void massParallelExecute(const std::vector<std::pair<AbstractPath, ParallelWorkI }); }); }; - deviceThreadGroups.emplace(rootPath, ThreadGroupContext(parallelOps, - threadGroupName + " " + utfTo<std::string>(AFS::getDisplayPath(rootPath)), - statusPrio, - scheduleExtraTask)); + deviceThreadGroups.emplace(afsDevice, ThreadGroupContext(parallelOps, + threadGroupName + " " + utfTo<std::string>(AFS::getDisplayPath(AbstractPath(afsDevice, AfsPath()))), + statusPrio, + scheduleExtraTask)); } deviceThreadGroupsShared.access([&](auto*& deviceThreadGroupsPtr) { deviceThreadGroupsPtr = &deviceThreadGroups; }); //[!] deviceThreadGroups is shared with worker threads from here on! diff --git a/FreeFileSync/Source/base/structures.cpp b/FreeFileSync/Source/base/structures.cpp index e39084ea..ea5f9ad3 100755 --- a/FreeFileSync/Source/base/structures.cpp +++ b/FreeFileSync/Source/base/structures.cpp @@ -10,7 +10,7 @@ #include <ctime> #include <zen/i18n.h> #include <zen/time.h> -#include "hard_filter.h" +#include "path_filter.h" #include "../fs/concrete.h" using namespace zen; @@ -226,37 +226,35 @@ std::wstring fff::getSyncVariantName(const MainConfiguration& mainCfg) } -size_t fff::getDeviceParallelOps(const std::map<AbstractPath, size_t>& deviceParallelOps, const AbstractPath& ap) +size_t fff::getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice) { - const AbstractPath& rootPath = AFS::getRootPath(ap); - auto it = deviceParallelOps.find(rootPath); + auto it = deviceParallelOps.find(afsDevice); return std::max<size_t>(it != deviceParallelOps.end() ? it->second : 1, 1); } -void fff::setDeviceParallelOps(std::map<AbstractPath, size_t>& deviceParallelOps, const AbstractPath& ap, size_t parallelOps) +void fff::setDeviceParallelOps(std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice, size_t parallelOps) { assert(parallelOps > 0); - const AbstractPath rootPath = AFS::getRootPath(ap); - if (!AFS::isNullPath(rootPath)) + if (!AFS::isNullDevice(afsDevice)) { if (parallelOps > 1) - deviceParallelOps[rootPath] = parallelOps; + deviceParallelOps[afsDevice] = parallelOps; else - deviceParallelOps.erase(rootPath); + deviceParallelOps.erase(afsDevice); } } -size_t fff::getDeviceParallelOps(const std::map<AbstractPath, size_t>& deviceParallelOps, const Zstring& folderPathPhrase) +size_t fff::getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase) { - return getDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase)); + return getDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase).afsDevice); } -void fff::setDeviceParallelOps(std::map<AbstractPath, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps) +void fff::setDeviceParallelOps(std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps) { - setDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase), parallelOps); + setDeviceParallelOps(deviceParallelOps, createAbstractPath(folderPathPhrase).afsDevice, parallelOps); } @@ -443,13 +441,11 @@ FilterConfig mergeFilterConfig(const FilterConfig& global, const FilterConfig& l FilterConfig out = local; //hard filter - if (NameFilter::isNull(out.includeFilter, Zstring())) //fancy way of checking for "*" include + if (NameFilter::isNull(local.includeFilter, Zstring())) //fancy way of checking for "*" include out.includeFilter = global.includeFilter; - //else: if both global and local include filter contain data, only local filter is preserved + //else : if both global and local include filters are set, only local filter is preserved - trim(out.excludeFilter, true, false); - out.excludeFilter = global.excludeFilter + Zstr("\n") + out.excludeFilter; - trim(out.excludeFilter, true, false); + out.excludeFilter = trimCpy(trimCpy(global.excludeFilter) + Zstr("\n\n") + trimCpy(local.excludeFilter)); //soft filter time_t loctimeFrom = 0; @@ -603,7 +599,7 @@ MainConfiguration fff::merge(const std::vector<MainConfiguration>& mainCfgs) lpc.localFilter = FilterConfig(); } - std::map<AbstractPath, size_t> mergedParallelOps; + std::map<AfsDevice, size_t> mergedParallelOps; for (const MainConfiguration& mainCfg : mainCfgs) for (const auto& [rootPath, parallelOps] : mainCfg.deviceParallelOps) mergedParallelOps[rootPath] = std::max(mergedParallelOps[rootPath], parallelOps); diff --git a/FreeFileSync/Source/base/structures.h b/FreeFileSync/Source/base/structures.h index 8668acd2..f5f6aeb5 100755 --- a/FreeFileSync/Source/base/structures.h +++ b/FreeFileSync/Source/base/structures.h @@ -305,7 +305,7 @@ struct FilterConfig unitSizeMax (unitSizeMaxIn) {} /* - Semantics of HardFilter: + Semantics of PathFilter: 1. using it creates a NEW folder hierarchy! -> must be considered by <Two way> variant! (fortunately it turns out, doing nothing already has perfect semantics :) 2. it applies equally to both sides => it always matches either both sides or none! => can be used while traversing a single folder! */ @@ -403,7 +403,7 @@ struct MainConfiguration LocalPairConfig firstPair; //there needs to be at least one pair! std::vector<LocalPairConfig> additionalPairs; - std::map<AbstractPath /*device root*/, size_t /*parallel operations*/> deviceParallelOps; //should only include devices with >= 2 parallel ops + std::map<AfsDevice, size_t /*parallel operations*/> deviceParallelOps; //should only include devices with >= 2 parallel ops bool ignoreErrors = false; //true: errors will still be logged size_t automaticRetryCount = 0; @@ -418,11 +418,10 @@ struct MainConfiguration std::wstring getCompVariantName(const MainConfiguration& mainCfg); std::wstring getSyncVariantName(const MainConfiguration& mainCfg); -size_t getDeviceParallelOps(const std::map<AbstractPath, size_t>& deviceParallelOps, const AbstractPath& ap); -void setDeviceParallelOps( std::map<AbstractPath, size_t>& deviceParallelOps, const AbstractPath& ap, size_t parallelOps); -size_t getDeviceParallelOps(const std::map<AbstractPath, size_t>& deviceParallelOps, const Zstring& folderPathPhrase); -void setDeviceParallelOps( std::map<AbstractPath, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps); - +size_t getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice); +void setDeviceParallelOps( std::map<AfsDevice, size_t>& deviceParallelOps, const AfsDevice& afsDevice, size_t parallelOps); +size_t getDeviceParallelOps(const std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase); +void setDeviceParallelOps( std::map<AfsDevice, size_t>& deviceParallelOps, const Zstring& folderPathPhrase, size_t parallelOps); inline bool operator==(const MainConfiguration& lhs, const MainConfiguration& rhs) diff --git a/FreeFileSync/Source/base/synchronization.cpp b/FreeFileSync/Source/base/synchronization.cpp index 25723ce2..fbb84f4d 100755 --- a/FreeFileSync/Source/base/synchronization.cpp +++ b/FreeFileSync/Source/base/synchronization.cpp @@ -485,8 +485,8 @@ AFS::ItemType getItemType(const AbstractPath& ap, std::mutex& singleThread) //th { return parallelScope([ap] { return AFS::getItemType(ap); /*throw FileError*/ }, singleThread); } inline -std::optional<AFS::ItemType> getItemTypeIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError -{ return parallelScope([ap] { return AFS::getItemTypeIfExists(ap); /*throw FileError*/ }, singleThread); } +std::optional<AFS::ItemType> itemStillExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError +{ return parallelScope([ap] { return AFS::itemStillExists(ap); /*throw FileError*/ }, singleThread); } inline bool removeFileIfExists(const AbstractPath& ap, std::mutex& singleThread) //throw FileError @@ -1180,7 +1180,7 @@ void FolderPairSyncer::setup2StepMove(FilePair& sourceFile, //throw FileError, T //generate (hopefully) unique file name to avoid clashing with some remnant ffs_tmp file const Zstring shortGuid = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(getCrc16(generateGUID()))); const Zstring fileName = sourceFile.getItemName<side>(); - auto it = find_last(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "." + auto it = findLast(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "." const Zstring sourceRelPathTmp = Zstring(fileName.begin(), it) + Zstr('.') + shortGuid + AFS::TEMP_FILE_ENDING; //------------------------------------------------------------------------------------------- @@ -1246,7 +1246,7 @@ auto FolderPairSyncer::createMoveTargetFolder(FileSystemObject& fsObj) -> CmtfSt reportInfo(txtCreatingFolder_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption //shallow-"copying" a folder might not fail if source is missing, so we need to check this first: - if (parallel::getItemTypeIfExists(parentFolder->getAbstractPath<sideSrc>(), singleThread_)) //throw FileError + if (parallel::itemStillExists(parentFolder->getAbstractPath<sideSrc>(), singleThread_)) //throw FileError { AsyncItemStatReporter statReporter(1, 0, acb_); try //target existing: undefined behavior! (fail/overwrite) @@ -1613,7 +1613,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp) catch (const FileError& e) { bool sourceWasDeleted = false; - try { sourceWasDeleted = !parallel::getItemTypeIfExists(file.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ } + try { sourceWasDeleted = !parallel::itemStillExists(file.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ } catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! @@ -1637,7 +1637,7 @@ void FolderPairSyncer::synchronizeFileInt(FilePair& file, SyncOperation syncOp) AsyncItemStatReporter statReporter(1, 0, acb_); delHandlerTrg.removeFileWithCallback({ file.getAbstractPath<sideTrg>(), file.getAttributes<sideTrg>() }, - file.getRelativePath<sideTrg>(), statReporter, singleThread_); //throw FileError, X + file.getRelativePath<sideTrg>(), statReporter, singleThread_); //throw FileError, X file.removeObject<sideTrg>(); //update FilePair } break; @@ -1831,12 +1831,12 @@ void FolderPairSyncer::synchronizeLinkInt(SymlinkPair& symlink, SyncOperation sy } catch (const FileError& e) { - bool sourceWasDeleted = false; - try { sourceWasDeleted = !parallel::getItemTypeIfExists(symlink.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ } + bool sourceExists = true; + try { sourceExists = !!parallel::itemStillExists(symlink.getAbstractPath<sideSrc>(), singleThread_); /*throw FileError*/ } catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant //do not check on type (symlink, file, folder) -> if there is a type change, FFS should not be quiet about it! - if (sourceWasDeleted) + if (!sourceExists) { //even if the source item does not exist anymore, significant I/O work was done => report statReporter.reportDelta(1, 0); @@ -1964,7 +1964,7 @@ void FolderPairSyncer::synchronizeFolderInt(FolderPair& folder, SyncOperation sy reportInfo(txtCreatingFolder_, AFS::getDisplayPath(targetPath)); //throw ThreadInterruption //shallow-"copying" a folder might not fail if source is missing, so we need to check this first: - if (parallel::getItemTypeIfExists(folder.getAbstractPath<sideSrc>(), singleThread_)) //throw FileError + if (parallel::itemStillExists(folder.getAbstractPath<sideSrc>(), singleThread_)) //throw FileError { AsyncItemStatReporter statReporter(1, 0, acb_); try @@ -2170,9 +2170,8 @@ bool createBaseFolder(BaseFolderPair& baseFolder, bool copyFilePermissions, Proc { if (baseFolder.isAvailable<sideSrc>()) //copy file permissions { - if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(baseFolderPath)) - if (AFS::getParentFolderPath(*parentPath)) //not device root - AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError + if (std::optional<AbstractPath> parentPath = AFS::getParentPath(baseFolderPath)) + AFS::createFolderIfMissingRecursion(*parentPath); //throw FileError AFS::copyNewFolder(baseFolder.getAbstractPath<sideSrc>(), baseFolderPath, copyFilePermissions); //throw FileError } @@ -2217,7 +2216,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime bool runWithBackgroundPriority, const std::vector<FolderPairSyncCfg>& syncConfig, FolderComparison& folderCmp, - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, WarningDialogs& warnings, ProcessCallback& callback) { @@ -2278,7 +2277,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime std::vector<SyncStatistics::ConflictInfo> unresolvedConflicts; - std::vector<std::tuple<AbstractPath, const HardFilter*, bool /*write access*/>> readWriteCheckBaseFolders; + std::vector<std::tuple<AbstractPath, const PathFilter*, bool /*write access*/>> readWriteCheckBaseFolders; std::vector<std::pair<AbstractPath, AbstractPath>> significantDiffPairs; @@ -2287,8 +2286,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime //status of base directories which are set to DeletionPolicy::RECYCLER (and contain actual items to be deleted) std::map<AbstractPath, bool> recyclerSupported; //expensive to determine on Win XP => buffer + check recycle bin existence only once per base folder! - std::set<AbstractPath> verCheckVersioningPaths; - std::vector<std::pair<AbstractPath, const HardFilter*>> verCheckBaseFolderPaths; //hard filter creates new logical hierarchies for otherwise equal AbstractPath... + std::set<AbstractPath> verCheckVersioningPaths; + std::vector<std::pair<AbstractPath, const PathFilter*>> verCheckBaseFolderPaths; //hard filter creates new logical hierarchies for otherwise equal AbstractPath... //start checking folder pairs for (auto itBase = begin(folderCmp); itBase != end(folderCmp); ++itBase) @@ -2302,8 +2301,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime append(unresolvedConflicts, folderPairStat.getConflicts()); //exclude a few pathological cases (including empty left, right folders) - if (AFS::equalAbstractPath(baseFolder.getAbstractPath< LEFT_SIDE>(), - baseFolder.getAbstractPath<RIGHT_SIDE>())) + if (baseFolder.getAbstractPath< LEFT_SIDE>() == + baseFolder.getAbstractPath<RIGHT_SIDE>()) { jobType[folderIndex] = FolderPairJobType::SKIP; continue; @@ -2384,12 +2383,12 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime //prepare: check if versioning path itself will be synchronized (and was not excluded via filter) verCheckVersioningPaths.insert(versioningFolderPath); - verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath<LEFT_SIDE >(), &baseFolder.getFilter()); + verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(), &baseFolder.getFilter()); verCheckBaseFolderPaths.emplace_back(baseFolder.getAbstractPath<RIGHT_SIDE>(), &baseFolder.getFilter()); } //prepare: check if folders are used by multiple pairs in read/write access - readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath<LEFT_SIDE >(), &baseFolder.getFilter(), writeLeft); + readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath< LEFT_SIDE>(), &baseFolder.getFilter(), writeLeft); readWriteCheckBaseFolders.emplace_back(baseFolder.getAbstractPath<RIGHT_SIDE>(), &baseFolder.getFilter(), writeRight); //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted @@ -2506,8 +2505,8 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime if (std::get<bool>(*it)) //write access for (auto it2 = readWriteCheckBaseFolders.begin(); it2 != readWriteCheckBaseFolders.end(); ++it2) if (!std::get<bool>(*it2) || it < it2) //avoid duplicate comparisons - if (std::optional<PathDependency> pd = getPathDependency(std::get<AbstractPath>(*it), *std::get<const HardFilter*>(*it), - std::get<AbstractPath>(*it2), *std::get<const HardFilter*>(*it2))) + if (std::optional<PathDependency> pd = getPathDependency(std::get<AbstractPath>(*it), *std::get<const PathFilter*>(*it), + std::get<AbstractPath>(*it2), *std::get<const PathFilter*>(*it2))) { dependentFolders.insert(pd->basePathParent); dependentFolders.insert(pd->basePathChild); @@ -2537,7 +2536,7 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime { std::wstring line = L"\n\n" + _("Versioning folder:") + L" \t" + AFS::getDisplayPath(versioningFolderPath) + L"\n" + _("Base folder:") + L" \t" + AFS::getDisplayPath(folderPath); - if (AFS::equalAbstractPath(pd->basePathParent, folderPath) && !pd->relPath.empty()) + if (pd->basePathParent == folderPath && !pd->relPath.empty()) line += L"\n" + _("Exclude:") + L" \t" + utfTo<std::wstring>(FILE_NAME_SEPARATOR + pd->relPath + FILE_NAME_SEPARATOR); uniqueMsgs[folderPath] = line; @@ -2660,10 +2659,10 @@ void fff::synchronize(const std::chrono::system_clock::time_point& syncStartTime catch (...) { assert(false); } //what is this? ); - size_t parallelOps = std::max(getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath<LEFT_SIDE >()), - getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath<RIGHT_SIDE>())); + size_t parallelOps = std::max(getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath< LEFT_SIDE>().afsDevice), + getDeviceParallelOps(deviceParallelOps, baseFolder.getAbstractPath<RIGHT_SIDE>().afsDevice)); if (folderPairCfg.handleDeletion == DeletionPolicy::VERSIONING) - parallelOps = std::max(parallelOps, getDeviceParallelOps(deviceParallelOps, versioningFolderPath)); + parallelOps = std::max(parallelOps, getDeviceParallelOps(deviceParallelOps, versioningFolderPath.afsDevice)); FolderPairSyncer::SyncCtx syncCtx = { diff --git a/FreeFileSync/Source/base/synchronization.h b/FreeFileSync/Source/base/synchronization.h index 7bd5db5e..08d6ca55 100755 --- a/FreeFileSync/Source/base/synchronization.h +++ b/FreeFileSync/Source/base/synchronization.h @@ -94,7 +94,7 @@ void synchronize(const std::chrono::system_clock::time_point& syncStartTime, bool runWithBackgroundPriority, const std::vector<FolderPairSyncCfg>& syncConfig, //CONTRACT: syncConfig and folderCmp correspond row-wise! FolderComparison& folderCmp, // - const std::map<AbstractPath, size_t>& deviceParallelOps, + const std::map<AfsDevice, size_t>& deviceParallelOps, WarningDialogs& warnings, ProcessCallback& callback); } diff --git a/FreeFileSync/Source/base/versioning.cpp b/FreeFileSync/Source/base/versioning.cpp index 64536076..aa0b0bdf 100755 --- a/FreeFileSync/Source/base/versioning.cpp +++ b/FreeFileSync/Source/base/versioning.cpp @@ -15,13 +15,13 @@ Zstring getDotExtension(const Zstring& filePath) //including "." if extension is //const Zstring& extension = getFileExtension(filePath); //return extension.empty() ? extension : Zstr('.') + extension; - auto it = find_last(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR); + auto it = findLast(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR); if (it == filePath.end()) it = filePath.begin(); else ++it; - return Zstring(find_last(it, filePath.end(), Zstr('.')), filePath.end()); + return Zstring(findLast(it, filePath.end(), Zstr('.')), filePath.end()); }; } @@ -30,7 +30,7 @@ Zstring getDotExtension(const Zstring& filePath) //including "." if extension is //or "Sample 2012-05-15 131513" std::pair<time_t, Zstring> fff::impl::parseVersionedFileName(const Zstring& fileName) { - const StringRef<const Zchar> ext(find_last(fileName.begin(), fileName.end(), Zstr('.')), fileName.end()); + const StringRef<const Zchar> ext(findLast(fileName.begin(), fileName.end(), Zstr('.')), fileName.end()); if (fileName.size() < 2 * ext.length() + 18) return {}; @@ -111,43 +111,20 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract auto fixTargetPathIssues = [&](const FileError& prevEx) //throw FileError { - std::optional<AFS::PathStatus> psTmp; - try { psTmp = AFS::getPathStatus(targetPath); /*throw FileError*/ } - catch (const FileError& e) { throw FileError(prevEx.toString(), e.toString()); } - const AFS::PathStatus& ps = *psTmp; - //previous exception contains context-level information, but current exception is the immediate problem => combine both - //=> e.g. prevEx might be about missing parent folder; FFS considers session faulty and tries to create a new one, - //which might fail with: LIBSSH2_ERROR_AUTHENTICATION_FAILED (due to limit on #sessions?) https://freefilesync.org/forum/viewtopic.php?t=4765#p16016 - - if (ps.relPath.empty()) //already existing + try { + AFS::getItemType(targetPath); //throw FileError + //already existing! => if (deletionError) std::rethrow_exception(deletionError); throw prevEx; //yes, slicing, but not relevant here } + catch (FileError&) {} //not yet existing or access error //parent folder missing => create + retry //parent folder existing => maybe created shortly after move attempt by parallel thread! => retry - AbstractPath intermediatePath = ps.existingPath; - - std::for_each(ps.relPath.begin(), ps.relPath.end() - 1, [&](const Zstring& itemName) - { - try - { - AFS::createFolderPlain(intermediatePath = AFS::appendRelPath(intermediatePath, itemName)); //throw FileError - } - catch (FileError&) - { - try //already existing => possible, if moveExistingItemToVersioning() is run in parallel - { - if (AFS::getItemType(intermediatePath) != AFS::ItemType::FILE) //throw FileError - return; //=continue - } - catch (FileError&) {} - - throw; - } - }); + if (const std::optional<AbstractPath> targetParentPath = AFS::getParentPath(targetPath)) + AFS::createFolderIfMissingRecursion(*targetParentPath); //throw FileError }; try //first try to move directly without copying @@ -191,7 +168,7 @@ void moveExistingItemToVersioning(const AbstractPath& sourcePath, const Abstract void FileVersioner::revisionFile(const FileDescriptor& fileDescr, const Zstring& relativePath, const IOCallback& notifyUnbufferedIO) const //throw FileError { - if (std::optional<AFS::ItemType> type = AFS::getItemTypeIfExists(fileDescr.path)) //throw FileError + if (std::optional<AFS::ItemType> type = AFS::itemStillExists(fileDescr.path)) //throw FileError { if (*type == AFS::ItemType::SYMLINK) revisionSymlinkImpl(fileDescr.path, relativePath, nullptr /*onBeforeMove*/); //throw FileError @@ -228,7 +205,7 @@ void FileVersioner::revisionFileImpl(const FileDescriptor& fileDescr, const Zstr void FileVersioner::revisionSymlink(const AbstractPath& linkPath, const Zstring& relativePath) const //throw FileError { - if (AFS::getItemTypeIfExists(linkPath)) //throw FileError + if (AFS::itemStillExists(linkPath)) //throw FileError revisionSymlinkImpl(linkPath, relativePath, nullptr /*onBeforeMove*/); //throw FileError //else -> missing source item is not an error => check BEFORE deleting target } @@ -253,7 +230,7 @@ void FileVersioner::revisionFolder(const AbstractPath& folderPath, const Zstring const IOCallback& notifyUnbufferedIO) const { //no error situation if directory is not existing! manual deletion relies on it! - if (std::optional<AFS::ItemType> type = AFS::getItemTypeIfExists(folderPath)) //throw FileError + if (std::optional<AFS::ItemType> type = AFS::itemStillExists(folderPath)) //throw FileError { if (*type == AFS::ItemType::SYMLINK) //on Linux there is just one type of symlink, and since we do revision file symlinks, we should revision dir symlinks as well! revisionSymlinkImpl(folderPath, relativePath, onBeforeFileMove); //throw FileError @@ -317,7 +294,7 @@ struct VersionInfo AbstractPath filePath; bool isSymlink = false; }; -using VersionInfoMap = std::map<Zstring, std::vector<VersionInfo>, LessFilePath>; //relPathOrig => <version infos> +using VersionInfoMap = std::map<Zstring, std::vector<VersionInfo>>; //relPathOrig => <version infos> //subfolder\Sample.txt 2012-05-15 131513.txt => subfolder\Sample.txt version:2012-05-15 131513 //2012-05-15 131513\subfolder\Sample.txt => " " @@ -330,7 +307,7 @@ void findFileVersions(VersionInfoMap& versions, { auto addVersion = [&](const Zstring& fileName, const Zstring& fileNameOrig, time_t versionTime, bool isSymlink) { - const Zstring& relPathOrig = AFS::appendPaths(relPathOrigParent, fileNameOrig, FILE_NAME_SEPARATOR); + const Zstring& relPathOrig = nativeAppendPaths(relPathOrigParent, fileNameOrig); const AbstractPath& filePath = AFS::appendRelPath(parentFolderPath, fileName); versions[relPathOrig].push_back(VersionInfo{ versionTime, filePath, isSymlink }); @@ -372,7 +349,7 @@ void findFileVersions(VersionInfoMap& versions, findFileVersions(versions, attrAndSub.second, AFS::appendRelPath(parentFolderPath, folderName), - AFS::appendPaths(relPathOrigParent, folderName, FILE_NAME_SEPARATOR), + nativeAppendPaths(relPathOrigParent, folderName), versionTimeParent); } } @@ -393,7 +370,7 @@ void getFolderItemCount(std::map<AbstractPath, size_t>& folderItemCount, const F bool fff::operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolder& rhs) { - const int cmp = AFS::compareAbstractPath(lhs.versioningFolderPath, rhs.versioningFolderPath); + const int cmp = AFS::comparePath(lhs.versioningFolderPath, rhs.versioningFolderPath); if (cmp != 0) return cmp < 0; @@ -410,27 +387,39 @@ bool fff::operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolde } -void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolders, - const std::map<AbstractPath, size_t>& deviceParallelOps, +void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& folderLimits, + const std::map<AfsDevice, size_t>& deviceParallelOps, ProcessCallback& callback /*throw X*/) { //--------- determine existing folder paths for traversal --------- std::set<DirectoryKey> foldersToRead; + std::set<VersioningLimitFolder> folderLimitsNorm; //get rid of folder aliases (e.g. path differing in case) { - std::set<AbstractPath> folderPaths; - for (const VersioningLimitFolder& vlf : limitFolders) + std::set<AbstractPath> pathsToCheck; + std::vector<VersioningLimitFolder> folderLimitsTmp; + + for (const VersioningLimitFolder& vlf : folderLimits) if (vlf.versionMaxAgeDays > 0 || vlf.versionCountMax > 0) //only analyze versioning folders when needed! - folderPaths.emplace(vlf.versioningFolderPath); + { + pathsToCheck.insert(vlf.versioningFolderPath); + folderLimitsTmp.push_back(vlf); + } //we don't want to show an error if version path does not yet exist! tryReportingError([&] { - const FolderStatus status = getFolderStatusNonBlocking(folderPaths, deviceParallelOps, //re-check *all* directories on each try! + const FolderStatus status = getFolderStatusNonBlocking(pathsToCheck, deviceParallelOps, //re-check *all* directories on each try! false /*allowUserInteraction*/, callback); //throw X - foldersToRead.clear(); for (const AbstractPath& folderPath : status.existing) - foldersToRead.emplace(DirectoryKey({ folderPath, std::make_shared<NullFilter>(), SymLinkHandling::DIRECT })); + foldersToRead.insert(DirectoryKey({ getNormalizedPath(status, folderPath), makeSharedRef<NullFilter>(), SymLinkHandling::DIRECT })); + + folderLimitsNorm.clear(); + for (VersioningLimitFolder vlf : folderLimitsTmp) + { + vlf.versioningFolderPath = getNormalizedPath(status, vlf.versioningFolderPath); + folderLimitsNorm.insert(vlf); + } if (!status.failedChecks.empty()) { @@ -465,7 +454,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde return AFS::TraverserCallback::ON_ERROR_CONTINUE; }; - const std::wstring textScanning = _("Searching for excess file versions:") + L" "; + const std::wstring textScanning = _("Searching for old file versions:") + L" "; auto onStatusUpdate = [&](const std::wstring& statusLine, int itemsTotal) { @@ -499,8 +488,6 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde //make sure the versioning folder is never found empty and is not deleted: ++folderItemCount[versioningFolderPath]; - warn_static("TODO: unicode normalization, case-sensitivity!") - //similarly, failed folder traversal should not make folders look empty: for (const auto& [relPath, errorMsg] : folderVal.failedFolderReads) ++folderItemCount[AFS::appendRelPath(versioningFolderPath, relPath)]; for (const auto& [relPath, errorMsg] : folderVal.failedItemReads ) ++folderItemCount[AFS::appendRelPath(versioningFolderPath, beforeLast(relPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE))]; @@ -518,7 +505,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde return localToTimeT(tc); //returns -1 on error => swallow => no versions trimmed by versionMaxAgeDays }(); - for (const VersioningLimitFolder& vlf : limitFolders) + for (const VersioningLimitFolder& vlf : folderLimitsNorm) { auto it = versionDetails.find(vlf.versioningFolderPath); if (it != versionDetails.end()) @@ -553,7 +540,7 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde //--------- remove excess file versions --------- Protected<std::map<AbstractPath, size_t>&> folderItemCountShared(folderItemCount); - const std::wstring textRemoving = _("Removing excess file versions:") + L" "; + const std::wstring textRemoving = _("Removing old file versions:") + L" "; const std::wstring textDeletingFolder = _("Deleting folder %x"); ParallelWorkItem deleteEmptyFolderTask; @@ -566,12 +553,12 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde }, ctx.acb); if (errMsg.empty()) - if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(ctx.itemPath)) + if (std::optional<AbstractPath> parentPath = AFS::getParentPath(ctx.itemPath)) { bool scheduleDelete = false; folderItemCountShared.access([&](auto& folderItemCount2) { scheduleDelete = --folderItemCount2[*parentPath] == 0; }); if (scheduleDelete) - ctx.scheduleExtraTask(AfsPath(AFS::getRootRelativePath(*parentPath)), deleteEmptyFolderTask); //throw ThreadInterruption + ctx.scheduleExtraTask(parentPath->afsPath, deleteEmptyFolderTask); //throw ThreadInterruption } }; @@ -594,13 +581,13 @@ void fff::applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolde }, ctx.acb); if (errMsg.empty()) - if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(ctx.itemPath)) + if (std::optional<AbstractPath> parentPath = AFS::getParentPath(ctx.itemPath)) { bool scheduleDelete = false; folderItemCountShared.access([&](auto& folderItemCount2) { scheduleDelete = --folderItemCount2[*parentPath] == 0; }); if (scheduleDelete) - ctx.scheduleExtraTask(AfsPath(AFS::getRootRelativePath(*parentPath)), deleteEmptyFolderTask); //throw ThreadInterruption - assert(AFS::getRootPath(*parentPath) == AFS::getRootPath(ctx.itemPath)); + ctx.scheduleExtraTask(parentPath->afsPath, deleteEmptyFolderTask); //throw ThreadInterruption + assert(parentPath->afsDevice == ctx.itemPath.afsDevice); } warn_static("get rid of scheduleExtraTask and recursively delete parent folders!? need scheduleExtraTask for something else?") diff --git a/FreeFileSync/Source/base/versioning.h b/FreeFileSync/Source/base/versioning.h index f1cefcc5..b2d31090 100755 --- a/FreeFileSync/Source/base/versioning.h +++ b/FreeFileSync/Source/base/versioning.h @@ -102,8 +102,8 @@ struct VersioningLimitFolder bool operator<(const VersioningLimitFolder& lhs, const VersioningLimitFolder& rhs); -void applyVersioningLimit(const std::set<VersioningLimitFolder>& limitFolders, - const std::map<AbstractPath, size_t>& deviceParallelOps, +void applyVersioningLimit(const std::set<VersioningLimitFolder>& folderLimits, + const std::map<AfsDevice, size_t>& deviceParallelOps, ProcessCallback& callback /*throw X*/); diff --git a/FreeFileSync/Source/fs/abstract.cpp b/FreeFileSync/Source/fs/abstract.cpp index 33b6ec0e..72b88fde 100755 --- a/FreeFileSync/Source/fs/abstract.cpp +++ b/FreeFileSync/Source/fs/abstract.cpp @@ -25,34 +25,40 @@ bool fff::isValidRelPath(const Zstring& relPath) } -int AFS::compareAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs) +int AFS::compareDevice(const AbstractFileSystem& lhs, const AbstractFileSystem& rhs) { //note: in worst case, order is guaranteed to be stable only during each program run - if (typeid(*lhs.afs).before(typeid(*rhs.afs))) + if (typeid(lhs).before(typeid(rhs))) return -1; - if (typeid(*rhs.afs).before(typeid(*lhs.afs))) + if (typeid(rhs).before(typeid(lhs))) return 1; - assert(typeid(*lhs.afs) == typeid(*rhs.afs)); + assert(typeid(lhs) == typeid(rhs)); //caveat: typeid returns static type for pointers, dynamic type for references!!! - const int rv = lhs.afs->compareDeviceRootSameAfsType(*rhs.afs); + return lhs.compareDeviceSameAfsType(rhs); +} + + +int AFS::comparePath(const AbstractPath& lhs, const AbstractPath& rhs) +{ + const int rv = compareDevice(lhs.afsDevice.ref(), rhs.afsDevice.ref()); if (rv != 0) return rv; - return compareFilePath(lhs.afsPath.value, rhs.afsPath.value); + return compareString(lhs.afsPath.value, rhs.afsPath.value); } -std::optional<AbstractPath> AFS::getParentFolderPath(const AbstractPath& ap) +std::optional<AbstractPath> AFS::getParentPath(const AbstractPath& ap) { - if (const std::optional<AfsPath> parentAfsPath = getParentAfsPath(ap.afsPath)) - return AbstractPath(ap.afs, *parentAfsPath); + if (const std::optional<AfsPath> parentAfsPath = getParentPath(ap.afsPath)) + return AbstractPath(ap.afsDevice, *parentAfsPath); return {}; } -std::optional<AfsPath> AFS::getParentAfsPath(const AfsPath& afsPath) +std::optional<AfsPath> AFS::getParentPath(const AfsPath& afsPath) { if (afsPath.value.empty()) return {}; @@ -61,25 +67,6 @@ std::optional<AfsPath> AFS::getParentAfsPath(const AfsPath& afsPath) } -void AFS::traverseFolderRecursive(const AbstractPath& basePath, const AFS::TraverserWorkload& workload, size_t parallelOps) -{ - TraverserWorkloadImpl wlImpl; - for (const auto& [relPathComponents, cb] : workload) - { - AfsPath afsPath = basePath.afsPath; - for (const Zstring& itemName : relPathComponents) - { - assert(!contains(itemName, FILE_NAME_SEPARATOR)); - if (!afsPath.value.empty()) - afsPath.value += FILE_NAME_SEPARATOR; - afsPath.value += itemName; - } - wlImpl.emplace_back(afsPath, cb); - } - basePath.afs->traverseFolderRecursive(wlImpl, parallelOps); //throw -} - - namespace { struct FlatTraverserCallback : public AFS::TraverserCallback @@ -193,9 +180,9 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con auto copyFilePlain = [&](const AbstractPath& apTargetTmp) { //caveat: typeid returns static type for pointers, dynamic type for references!!! - if (typeid(*apSource.afs) == typeid(*apTargetTmp.afs)) - return apSource.afs->copyFileForSameAfsType(apSource.afsPath, attrSource, - apTargetTmp, copyFilePermissions, notifyUnbufferedIO); //throw FileError, ErrorFileLocked + if (typeid(apSource.afsDevice.ref()) == typeid(apTargetTmp.afsDevice.ref())) + return apSource.afsDevice.ref().copyFileForSameAfsType(apSource.afsPath, attrSource, + apTargetTmp, copyFilePermissions, notifyUnbufferedIO); //throw FileError, ErrorFileLocked //target existing: undefined behavior! (fail/overwrite/auto-rename) //fall back to stream-based file copy: @@ -203,7 +190,7 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con throw FileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtPath(AFS::getDisplayPath(apTargetTmp))), _("Operation not supported for different base folder types.")); - return apSource.afs->copyFileAsStream(apSource.afsPath, attrSource, apTargetTmp, notifyUnbufferedIO); //throw FileError, ErrorFileLocked + return apSource.afsDevice.ref().copyFileAsStream(apSource.afsPath, attrSource, apTargetTmp, notifyUnbufferedIO); //throw FileError, ErrorFileLocked //target existing: undefined behavior! (fail/overwrite/auto-rename) }; @@ -212,19 +199,27 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con warn_static("doesnt make sense for Google Drive") - std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(apTarget); + std::optional<AbstractPath> parentPath = AFS::getParentPath(apTarget); if (!parentPath) throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtPath(AFS::getDisplayPath(apTarget))), L"Path is device root."); const Zstring fileName = AFS::getItemName(apTarget); //- generate (hopefully) unique file name to avoid clashing with some remnant ffs_tmp file //- do not loop and avoid pathological cases, e.g. https://freefilesync.org/forum/viewtopic.php?t=1592 - const Zstring shortGuid = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(getCrc16(generateGUID()))); - auto it = find_last(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "." - const Zstring fileNameTmp = Zstring(fileName.begin(), it) + Zstr('.') + shortGuid + TEMP_FILE_ENDING; + const Zstring& shortGuid = printNumber<Zstring>(Zstr("%04x"), static_cast<unsigned int>(getCrc16(generateGUID()))); + const Zstring& tmpExt = Zstr('.') + shortGuid + TEMP_FILE_ENDING; + auto it = findLast(fileName.begin(), fileName.end(), Zstr('.')); //gracefully handle case of missing "." + + //don't make the temp name longer than the original; avoid hitting file system name length limitations: "lpMaximumComponentLength is commonly 255 characters" + if (fileName.size() > 200) //BUT don't trim short names! we want early failure on filename-related issues + it = std::min(it, fileName.end() - tmpExt.size()); + warn_static("utf8 anyone? atribtrarily trimming string, really?") + + + const Zstring& fileNameTmp = Zstring(fileName.begin(), it) + tmpExt; const AbstractPath apTargetTmp = AFS::appendRelPath(*parentPath, fileNameTmp); - //AbstractPath apTargetTmp(apTarget.afs, AfsPath(apTarget.afsPath.value + TEMP_FILE_ENDING)); + //AbstractPath apTargetTmp(apTarget.afsDevice, AfsPath(apTarget.afsPath.value + TEMP_FILE_ENDING)); //------------------------------------------------------------------------------------------- const AFS::FileCopyResult result = copyFilePlain(apTargetTmp); //throw FileError, ErrorFileLocked @@ -268,8 +263,18 @@ AFS::FileCopyResult AFS::copyFileTransactional(const AbstractPath& apSource, con void AFS::createFolderIfMissingRecursion(const AbstractPath& ap) //throw FileError { - if (!getParentFolderPath(ap)) //device root - return static_cast<void>(/*ItemType =*/ getItemType(ap)); //throw FileError + const std::optional<AbstractPath> parentPath = getParentPath(ap); + if (!parentPath) //device root + return; + + try //generally we expect that path already exists (see: versioning, base folder, log file path) => check first + { + if (getItemType(ap) != ItemType::FILE) //throw FileError + return; + } + catch (FileError&) {} //not yet existing or access error? let's find out... + + createFolderIfMissingRecursion(*parentPath); //throw FileError try { @@ -278,82 +283,52 @@ void AFS::createFolderIfMissingRecursion(const AbstractPath& ap) //throw FileErr } catch (FileError&) { - const PathStatus ps = getPathStatus(ap); //throw FileError - if (ps.existingType == ItemType::FILE) - throw; + try + { + if (getItemType(ap) != ItemType::FILE) //throw FileError + return; //already existing => possible, if createFolderIfMissingRecursion() is run in parallel + } + catch (FileError&) {} //not yet existing or access error + //catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } -> details needed??? - //ps.relPath.size() == 1 => same createFolderPlain() call from above? Maybe parent folder was created by parallel thread shortly after failure! - AbstractPath intermediatePath = ps.existingPath; - for (const Zstring& itemName : ps.relPath) - try - { - createFolderPlain(intermediatePath = appendRelPath(intermediatePath, itemName)); //throw FileError - } - catch (FileError&) - { - try //already existing => possible, if createFolderIfMissingRecursion() is run in parallel - { - if (getItemType(intermediatePath) != ItemType::FILE) //throw FileError - continue; - } - catch (FileError&) {} - - throw; - } + throw; } } -//essentially a(n abstract) duplicate of zen::getPathStatus() -AFS::PathStatusImpl AFS::getPathStatusViaFolderTraversal(const AfsPath& afsPath) const //throw FileError +std::optional<AFS::ItemType> AFS::itemStillExistsViaFolderTraversal(const AfsPath& afsPath) const //throw FileError { - const std::optional<AfsPath> parentAfsPath = getParentAfsPath(afsPath); try { - return { getItemType(afsPath), afsPath, {} }; //throw FileError + return getItemType(afsPath); //throw FileError } - catch (FileError&) + catch (const FileError& e) //not existing or access error { + const std::optional<AfsPath> parentAfsPath = getParentPath(afsPath); if (!parentAfsPath) //device root throw; //else: let's dig deeper... don't bother checking Win32 codes; e.g. not existing item may have the codes: // ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_NAME, ERROR_INVALID_DRIVE, // ERROR_NOT_READY, ERROR_INVALID_PARAMETER, ERROR_BAD_PATHNAME, ERROR_BAD_NETPATH => not reliable - } - const Zstring itemName = getItemName(afsPath); - assert(!itemName.empty()); - - PathStatusImpl ps = getPathStatusViaFolderTraversal(*parentAfsPath); //throw FileError - if (ps.relPath.empty() && - ps.existingType != ItemType::FILE) //obscure, but possible (and not an error) - try - { - traverseFolderFlat(*parentAfsPath, //throw FileError - [&](const FileInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FILE; }, - [&](const FolderInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FOLDER; }, - [&](const SymlinkInfo& si) { if (equalFilePath(si.itemName, itemName)) throw ItemType::SYMLINK; }); - } - catch (const ItemType& type) { return { type, afsPath, {} }; } //yes, exceptions for control-flow are bad design... but, but... - //we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found) - - ps.relPath.push_back(itemName); - return ps; -} + const Zstring itemName = getItemName(afsPath); + assert(!itemName.empty()); -std::optional<AFS::ItemType> AFS::getItemTypeIfExists(const AbstractPath& ap) //throw FileError -{ - const PathStatus ps = getPathStatus(ap); //throw FileError - if (ps.relPath.empty()) - return ps.existingType; - return {}; -} - - -AFS::PathStatus AFS::getPathStatus(const AbstractPath& ap) //throw FileError -{ - const PathStatusImpl psi = ap.afs->getPathStatus(ap.afsPath); //throw FileError - return { psi.existingType, AbstractPath(ap.afs, psi.existingAfsPath), psi.relPath }; + const std::optional<ItemType> parentType = itemStillExistsViaFolderTraversal(*parentAfsPath); //throw FileError + if (parentType && *parentType != ItemType::FILE /*obscure, but possible (and not an error)*/) + try + { + traverseFolderFlat(*parentAfsPath, //throw FileError + [&](const FileInfo& fi) { if (fi.itemName == itemName) throw ItemType::FILE; }, + [&](const FolderInfo& fi) { if (fi.itemName == itemName) throw ItemType::FOLDER; }, + [&](const SymlinkInfo& si) { if (si.itemName == itemName) throw ItemType::SYMLINK; }); + } + catch (const ItemType&) //finding the item after getItemType() previously failed is exceptional + { + throw e; //yes, slicing + } + return {}; + } } @@ -404,7 +379,7 @@ void AFS::removeFolderIfExistsRecursion(const AbstractPath& ap, //throw FileErro warn_static("what about parallelOps?") //no error situation if directory is not existing! manual deletion relies on it! - if (std::optional<ItemType> type = AFS::getItemTypeIfExists(ap)) //throw FileError + if (std::optional<ItemType> type = AFS::itemStillExists(ap)) //throw FileError { if (*type == AFS::ItemType::SYMLINK) { @@ -432,7 +407,7 @@ bool AFS::removeFileIfExists(const AbstractPath& ap) //throw FileError { try { - if (!AFS::getItemTypeIfExists(ap)) //throw FileError + if (!AFS::itemStillExists(ap)) //throw FileError return false; } catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant @@ -453,7 +428,7 @@ bool AFS::removeSymlinkIfExists(const AbstractPath& ap) //throw FileError { try { - if (!AFS::getItemTypeIfExists(ap)) //throw FileError + if (!AFS::itemStillExists(ap)) //throw FileError return false; } catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant @@ -473,7 +448,7 @@ void AFS::removeEmptyFolderIfExists(const AbstractPath& ap) //throw FileError { try { - if (!AFS::getItemTypeIfExists(ap)) //throw FileError + if (!AFS::itemStillExists(ap)) //throw FileError return; } catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } //unclear which exception is more relevant diff --git a/FreeFileSync/Source/fs/abstract.h b/FreeFileSync/Source/fs/abstract.h index d5de09c7..f7da2887 100755 --- a/FreeFileSync/Source/fs/abstract.h +++ b/FreeFileSync/Source/fs/abstract.h @@ -16,11 +16,13 @@ namespace fff { -struct AbstractFileSystem; - bool isValidRelPath(const Zstring& relPath); +struct AbstractFileSystem; + //============================================================================================================== +using AfsDevice = zen::SharedRef<const AbstractFileSystem>; + struct AfsPath //= path relative to the file system root folder (no leading/traling separator) { AfsPath() {} @@ -28,74 +30,70 @@ struct AfsPath //= path relative to the file system root folder (no leading/tral Zstring value; }; -class AbstractPath //THREAD-SAFETY: like an int! +struct AbstractPath //THREAD-SAFETY: like an int! { -public: - AbstractPath(const std::shared_ptr<const AbstractFileSystem>& afsIn, const AfsPath& afsPathIn) : afs(afsIn), afsPath(afsPathIn) {} + AbstractPath(const AfsDevice& afsIn, const AfsPath& afsPathIn) : afsDevice(afsIn), afsPath(afsPathIn) {} - //template <class T1, class T2> -> don't use forwarding constructor! => it circumvents AfsPath's explicit constructor - //AbstractPath(T1&& afsIn, T2&& afsPathIn) : afs(std::forward<T1>(afsIn)), afsPath(std::forward<T2>(afsPathIn)) { assert(isValidRelPath(afsPathIn)); } + //template <class T1, class T2> -> don't use forwarding constructor: it circumvents AfsPath's explicit constructor! + //AbstractPath(T1&& afsIn, T2&& afsPathIn) : afsDevice(std::forward<T1>(afsIn)), afsPath(std::forward<T2>(afsPathIn)) {} -private: - friend struct AbstractFileSystem; - - std::shared_ptr<const AbstractFileSystem> afs; //always bound; "const AbstractFileSystem" => all accesses expected to be thread-safe!!! - AfsPath afsPath; + AfsDevice afsDevice; //"const AbstractFileSystem" => all accesses expected to be thread-safe!!! + AfsPath afsPath; //relative to device root }; - //============================================================================================================== struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model thread-safe access! { - static int compareAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs); + //=============== convenience ================= + static Zstring getItemName(const AbstractPath& ap) { assert(getParentPath(ap)); return getItemName(ap.afsPath); } + static Zstring getItemName(const AfsPath& afsPath) { using namespace zen; return afterLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } - static bool equalAbstractPath(const AbstractPath& lhs, const AbstractPath& rhs) { return compareAbstractPath(lhs, rhs) == 0; } + static bool isNullPath(const AbstractPath& ap) { return isNullDevice(ap.afsDevice) /*&& ap.afsPath.value.empty()*/; } - static bool isNullPath(const AbstractPath& ap) { return ap.afs->isNullFileSystem() /*&& ap.afsPath.value.empty()*/; } + static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath); - static std::wstring getDisplayPath(const AbstractPath& ap) { return ap.afs->getDisplayPath(ap.afsPath); } + static std::optional<AbstractPath> getParentPath(const AbstractPath& ap); + static std::optional<AfsPath> getParentPath(const AfsPath& afsPath); + //============================================= - static Zstring getInitPathPhrase(const AbstractPath& ap) { return ap.afs->getInitPathPhrase(ap.afsPath); } + static int compareDevice(const AbstractFileSystem& lhs, const AbstractFileSystem& rhs); - static std::optional<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afs->getNativeItemPath(ap.afsPath); } + static int comparePath(const AbstractPath& lhs, const AbstractPath& rhs); - static AbstractPath appendRelPath(const AbstractPath& ap, const Zstring& relPath); + static bool isNullDevice(const AfsDevice& afsDevice) { return afsDevice.ref().isNullFileSystem(); } - static Zstring getItemName(const AbstractPath& ap) { assert(getParentFolderPath(ap)); return getItemName(ap.afsPath); } + static std::wstring getDisplayPath(const AbstractPath& ap) { return ap.afsDevice.ref().getDisplayPath(ap.afsPath); } - static std::optional<AbstractPath> getParentFolderPath(const AbstractPath& ap); + static Zstring getInitPathPhrase(const AbstractPath& ap) { return ap.afsDevice.ref().getInitPathPhrase(ap.afsPath); } - static AbstractPath getRootPath (const AbstractPath& ap) { return AbstractPath(ap.afs, AfsPath()); } - static Zstring getRootRelativePath(const AbstractPath& ap) { return ap.afsPath.value; } + static std::optional<Zstring> getNativeItemPath(const AbstractPath& ap) { return ap.afsDevice.ref().getNativeItemPath(ap.afsPath); } //---------------------------------------------------------------------------------------------------------------- - static void connectNetworkFolder(const AbstractPath& ap, bool allowUserInteraction) { return ap.afs->connectNetworkFolder(ap.afsPath, allowUserInteraction); } //throw FileError + static void connectNetworkFolder(const AbstractPath& ap, bool allowUserInteraction) { return ap.afsDevice.ref().connectNetworkFolder(ap.afsPath, allowUserInteraction); } //throw FileError - static int geAccessTimeout(const AbstractPath& ap) { return ap.afs->getAccessTimeout(); } //returns "0" if no timeout in force + static int geAccessTimeout(const AbstractPath& ap) { return ap.afsDevice.ref().getAccessTimeout(); } //returns "0" if no timeout in force //---------------------------------------------------------------------------------------------------------------- + using FileId = zen::Zbase<char>; + enum class ItemType { FILE, FOLDER, SYMLINK, }; - struct PathStatus - { - ItemType existingType; - AbstractPath existingPath; //itemPath =: existingPath + relPath - std::vector<Zstring> relPath; // - }; //(hopefully) fast: does not distinguish between error/not existing - static ItemType getItemType(const AbstractPath& ap) { return ap.afs->getItemType(ap.afsPath); } //throw FileError - //execute potentially SLOW folder traversal but distinguish error/not existing - static std::optional<ItemType> getItemTypeIfExists(const AbstractPath& ap); //throw FileError - static PathStatus getPathStatus(const AbstractPath& ap); //throw FileError + static ItemType getItemType(const AbstractPath& ap) { return ap.afsDevice.ref().getItemType(ap.afsPath); } //throw FileError + + //assumes: - base path still exists + // - all child item path parts must correspond to folder traversal + // => we can conclude whether an item is *not* existing anymore by doing a *case-sensitive* name search => potentially SLOW! + static std::optional<ItemType> itemStillExists(const AbstractPath& ap) { return ap.afsDevice.ref().itemStillExists(ap.afsPath); } //throw FileError //---------------------------------------------------------------------------------------------------------------- //target existing: undefined behavior! (fail/overwrite) //does NOT create parent directories recursively if not existing - static void createFolderPlain(const AbstractPath& ap) { ap.afs->createFolderPlain(ap.afsPath); } //throw FileError + static void createFolderPlain(const AbstractPath& ap) { ap.afsDevice.ref().createFolderPlain(ap.afsPath); } //throw FileError //no error if already existing //creates parent directories recursively if not existing @@ -108,21 +106,22 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t const std::function<void (const std::wstring& displayPath)>& onBeforeFileDeletion, //optional const std::function<void (const std::wstring& displayPath)>& onBeforeFolderDeletion); //one call for each object! - static void removeFilePlain (const AbstractPath& ap) { ap.afs->removeFilePlain (ap.afsPath); } //throw FileError - static void removeSymlinkPlain(const AbstractPath& ap) { ap.afs->removeSymlinkPlain(ap.afsPath); } //throw FileError - static void removeFolderPlain (const AbstractPath& ap) { ap.afs->removeFolderPlain (ap.afsPath); } //throw FileError + static void removeFilePlain (const AbstractPath& ap) { ap.afsDevice.ref().removeFilePlain (ap.afsPath); } //throw FileError + static void removeSymlinkPlain(const AbstractPath& ap) { ap.afsDevice.ref().removeSymlinkPlain(ap.afsPath); } //throw FileError + static void removeFolderPlain (const AbstractPath& ap) { ap.afsDevice.ref().removeFolderPlain (ap.afsPath); } //throw FileError //---------------------------------------------------------------------------------------------------------------- - static void setModTime(const AbstractPath& ap, time_t modTime) { ap.afs->setModTime(ap.afsPath, modTime); } //throw FileError, follows symlinks + static void setModTime(const AbstractPath& ap, time_t modTime) { ap.afsDevice.ref().setModTime(ap.afsPath, modTime); } //throw FileError, follows symlinks + + static FileId /*optional*/ getFileId(const AbstractPath& ap) { return ap.afsDevice.ref().getFileId(ap.afsPath); } //throw FileError - static AbstractPath getSymlinkResolvedPath(const AbstractPath& ap) { return ap.afs->getSymlinkResolvedPath(ap.afsPath); } //throw FileError - static std::string getSymlinkBinaryContent(const AbstractPath& ap) { return ap.afs->getSymlinkBinaryContent(ap.afsPath); } //throw FileError + static AbstractPath getSymlinkResolvedPath(const AbstractPath& ap) { return ap.afsDevice.ref().getSymlinkResolvedPath (ap.afsPath); } //throw FileError + static std::string getSymlinkBinaryContent(const AbstractPath& ap) { return ap.afsDevice.ref().getSymlinkBinaryContent(ap.afsPath); } //throw FileError //---------------------------------------------------------------------------------------------------------------- //noexcept; optional return value: - static zen::ImageHolder getFileIcon (const AbstractPath& ap, int pixelSize) { return ap.afs->getFileIcon (ap.afsPath, pixelSize); } - static zen::ImageHolder getThumbnailImage(const AbstractPath& ap, int pixelSize) { return ap.afs->getThumbnailImage(ap.afsPath, pixelSize); } + static zen::ImageHolder getFileIcon (const AbstractPath& ap, int pixelSize) { return ap.afsDevice.ref().getFileIcon (ap.afsPath, pixelSize); } + static zen::ImageHolder getThumbnailImage(const AbstractPath& ap, int pixelSize) { return ap.afsDevice.ref().getThumbnailImage(ap.afsPath, pixelSize); } //---------------------------------------------------------------------------------------------------------------- - using FileId = zen::Zbase<char>; struct StreamAttributes { @@ -167,13 +166,13 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //return value always bound: static std::unique_ptr<InputStream> getInputStream(const AbstractPath& ap, const zen::IOCallback& notifyUnbufferedIO) //throw FileError, ErrorFileLocked, X - { return ap.afs->getInputStream(ap.afsPath, notifyUnbufferedIO); } + { return ap.afsDevice.ref().getInputStream(ap.afsPath, notifyUnbufferedIO); } //target existing: undefined behavior! (fail/overwrite/auto-rename) static std::unique_ptr<OutputStream> getOutputStream(const AbstractPath& ap, //throw FileError const uint64_t* streamSize, //optional const zen::IOCallback& notifyUnbufferedIO) // - { return std::make_unique<OutputStream>(ap.afs->getOutputStream(ap.afsPath, streamSize, notifyUnbufferedIO), ap, streamSize); } + { return std::make_unique<OutputStream>(ap.afsDevice.ref().getOutputStream(ap.afsPath, streamSize, notifyUnbufferedIO), ap, streamSize); } //---------------------------------------------------------------------------------------------------------------- struct SymlinkInfo @@ -222,16 +221,19 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zstring& itemName) = 0; //failed to get data for single file/dir/symlink only! }; - using TraverserWorkload = std::vector<std::pair<std::vector<Zstring> /*relPath*/, std::shared_ptr<TraverserCallback> /*throw X*/>>; + using TraverserWorkload = std::vector<std::pair<AfsPath, std::shared_ptr<TraverserCallback> /*throw X*/>>; //- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...) - static void traverseFolderRecursive(const AbstractPath& basePath, const TraverserWorkload& workload, size_t parallelOps); + static void traverseFolderRecursive(const AfsDevice& afsDevice, const TraverserWorkload& workload /*throw X*/, size_t parallelOps) + { + afsDevice.ref().traverseFolderRecursive(workload, parallelOps); //throw + } static void traverseFolderFlat(const AbstractPath& ap, //throw FileError const std::function<void (const FileInfo& fi)>& onFile, // const std::function<void (const FolderInfo& fi)>& onFolder, //optional const std::function<void (const SymlinkInfo& si)>& onSymlink) // - { ap.afs->traverseFolderFlat(ap.afsPath, onFile, onFolder, onSymlink); } + { ap.afsDevice.ref().traverseFolderFlat(ap.afsPath, onFile, onFolder, onSymlink); } //---------------------------------------------------------------------------------------------------------------- static bool supportPermissionCopy(const AbstractPath& apSource, const AbstractPath& apTarget); //throw FileError @@ -273,9 +275,9 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t //---------------------------------------------------------------------------------------------------------------- - static uint64_t getFreeDiskSpace(const AbstractPath& ap) { return ap.afs->getFreeDiskSpace(ap.afsPath); } //throw FileError, returns 0 if not available + static uint64_t getFreeDiskSpace(const AbstractPath& ap) { return ap.afsDevice.ref().getFreeDiskSpace(ap.afsPath); } //throw FileError, returns 0 if not available - static bool supportsRecycleBin(const AbstractPath& ap, const std::function<void ()>& onUpdateGui) { return ap.afs->supportsRecycleBin(ap.afsPath, onUpdateGui); } //throw FileError + static bool supportsRecycleBin(const AbstractPath& ap, const std::function<void ()>& onUpdateGui) { return ap.afsDevice.ref().supportsRecycleBin(ap.afsPath, onUpdateGui); } //throw FileError struct RecycleSession { @@ -288,43 +290,28 @@ struct AbstractFileSystem //THREAD-SAFETY: "const" member functions must model t }; //precondition: supportsRecycleBin() must return true! - static std::unique_ptr<RecycleSession> createRecyclerSession(const AbstractPath& ap) { return ap.afs->createRecyclerSession(ap.afsPath); } //throw FileError, return value must be bound! + static std::unique_ptr<RecycleSession> createRecyclerSession(const AbstractPath& ap) { return ap.afsDevice.ref().createRecyclerSession(ap.afsPath); } //throw FileError, return value must be bound! - static void recycleItemIfExists(const AbstractPath& ap) { ap.afs->recycleItemIfExists(ap.afsPath); } //throw FileError + static void recycleItemIfExists(const AbstractPath& ap) { ap.afsDevice.ref().recycleItemIfExists(ap.afsPath); } //throw FileError //================================================================================================================ //no need to protect access: - static Zstring appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep); - virtual ~AbstractFileSystem() {} -protected: //grant derived classes access to AbstractPath: - static const AbstractFileSystem& getAfs (const AbstractPath& ap) { return *ap.afs; } - static AfsPath getAfsPath(const AbstractPath& ap) { return ap.afsPath; } - - static Zstring getItemName(const AfsPath& afsPath) { using namespace zen; return afterLast(afsPath.value, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); } - static std::optional<AfsPath> getParentAfsPath(const AfsPath& afsPath); - struct PathStatusImpl - { - ItemType existingType; - AfsPath existingAfsPath; //afsPath =: existingAfsPath + relPath - std::vector<Zstring> relPath; // - }; - PathStatusImpl getPathStatusViaFolderTraversal(const AfsPath& afsPath) const; //throw FileError +protected: + std::optional<ItemType> itemStillExistsViaFolderTraversal(const AfsPath& afsPath) const; //throw FileError void traverseFolderFlat(const AfsPath& afsPath, //throw FileError - const std::function<void (const FileInfo& fi)>& onFile, // - const std::function<void (const FolderInfo& fi)>& onFolder, //optional + const std::function<void (const FileInfo& fi)>& onFile, // + const std::function<void (const FolderInfo& fi)>& onFolder, //optional const std::function<void (const SymlinkInfo& si)>& onSymlink) const; // //target existing: undefined behavior! (fail/overwrite/auto-rename) FileCopyResult copyFileAsStream(const AfsPath& afsPathSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked const AbstractPath& apTarget, const zen::IOCallback& notifyUnbufferedIO) const; //may be nullptr; throw X! - using TraverserWorkloadImpl = std::vector<std::pair<AfsPath, std::shared_ptr<TraverserCallback> /*throw X*/>>; - private: virtual std::optional<Zstring> getNativeItemPath(const AfsPath& afsPath) const { return {}; }; @@ -334,11 +321,11 @@ private: virtual bool isNullFileSystem() const = 0; - virtual int compareDeviceRootSameAfsType(const AbstractFileSystem& afsRhs) const = 0; + virtual int compareDeviceSameAfsType(const AbstractFileSystem& afsRhs) const = 0; //---------------------------------------------------------------------------------------------------------------- virtual ItemType getItemType(const AfsPath& afsPath) const = 0; //throw FileError - virtual PathStatusImpl getPathStatus(const AfsPath& afsPath) const = 0; //throw FileError + virtual std::optional<ItemType> itemStillExists(const AfsPath& afsPath) const = 0; //throw FileError //---------------------------------------------------------------------------------------------------------------- //target existing: undefined behavior! (fail/overwrite) @@ -351,6 +338,8 @@ private: //---------------------------------------------------------------------------------------------------------------- virtual void setModTime(const AfsPath& afsPath, time_t modTime) const = 0; //throw FileError, follows symlinks + virtual FileId /*optional*/ getFileId(const AfsPath& afsPath) const = 0; //throw FileError + virtual AbstractPath getSymlinkResolvedPath(const AfsPath& afsPath) const = 0; //throw FileError virtual std::string getSymlinkBinaryContent(const AfsPath& afsPath) const = 0; //throw FileError //---------------------------------------------------------------------------------------------------------------- @@ -361,7 +350,7 @@ private: const uint64_t* streamSize, //optional const zen::IOCallback& notifyUnbufferedIO) const = 0; // //---------------------------------------------------------------------------------------------------------------- - virtual void traverseFolderRecursive(const TraverserWorkloadImpl& workload /*throw X*/, size_t parallelOps) const = 0; + virtual void traverseFolderRecursive(const TraverserWorkload& workload /*throw X*/, size_t parallelOps) const = 0; //---------------------------------------------------------------------------------------------------------------- virtual bool supportsPermissions(const AfsPath& afsPath) const = 0; //throw FileError @@ -398,8 +387,12 @@ private: }; -inline bool operator< (const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::compareAbstractPath(lhs, rhs) < 0; } -inline bool operator==(const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::compareAbstractPath(lhs, rhs) == 0; } +inline bool operator< (const AfsDevice& lhs, const AfsDevice& rhs) { return AbstractFileSystem::compareDevice(lhs.ref(), rhs.ref()) < 0; } +inline bool operator==(const AfsDevice& lhs, const AfsDevice& rhs) { return AbstractFileSystem::compareDevice(lhs.ref(), rhs.ref()) == 0; } +inline bool operator!=(const AfsDevice& lhs, const AfsDevice& rhs) { return !(lhs == rhs); } + +inline bool operator< (const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::comparePath(lhs, rhs) < 0; } +inline bool operator==(const AbstractPath& lhs, const AbstractPath& rhs) { return AbstractFileSystem::comparePath(lhs, rhs) == 0; } inline bool operator!=(const AbstractPath& lhs, const AbstractPath& rhs) { return !(lhs == rhs); } @@ -413,40 +406,8 @@ inline bool operator!=(const AbstractPath& lhs, const AbstractPath& rhs) { retur inline AbstractPath AbstractFileSystem::appendRelPath(const AbstractPath& ap, const Zstring& relPath) { - using namespace zen; - assert(isValidRelPath(relPath)); - return AbstractPath(ap.afs, AfsPath(appendPaths(ap.afsPath.value, relPath, FILE_NAME_SEPARATOR))); -} - - -inline -Zstring AbstractFileSystem::appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep) -{ - using namespace zen; - - assert(!startsWith(relPath, pathSep) && !endsWith(relPath, pathSep)); - if (relPath.empty()) - return basePath; - if (basePath.empty()) - return relPath; - - if (startsWith(relPath, pathSep)) - { - if (relPath.size() == 1) - return basePath; - - if (endsWith(basePath, pathSep)) - return basePath + (relPath.c_str() + 1); - } - else if (!endsWith(basePath, pathSep)) - { - Zstring output = basePath; - output.reserve(basePath.size() + 1 + relPath.size()); //append all three strings using a single memory allocation - return std::move(output) + pathSep + relPath; // - } - - return basePath + relPath; + return AbstractPath(ap.afsDevice, AfsPath(nativeAppendPaths(ap.afsPath.value, relPath))); } //-------------------------------------------------------------------------- @@ -504,11 +465,11 @@ AbstractFileSystem::FileId AbstractFileSystem::OutputStream::finalize() //throw inline bool AbstractFileSystem::supportPermissionCopy(const AbstractPath& apSource, const AbstractPath& apTarget) //throw FileError { - if (typeid(*apSource.afs) != typeid(*apTarget.afs)) + if (typeid(apSource.afsDevice.ref()) != typeid(apTarget.afsDevice.ref())) return false; - return apSource.afs->supportsPermissions(apSource.afsPath) && //throw FileError - apTarget.afs->supportsPermissions(apTarget.afsPath); + return apSource.afsDevice.ref().supportsPermissions(apSource.afsPath) && //throw FileError + apTarget.afsDevice.ref().supportsPermissions(apTarget.afsPath); } @@ -517,8 +478,8 @@ void AbstractFileSystem::moveAndRenameItem(const AbstractPath& apSource, const A { using namespace zen; - if (typeid(*apSource.afs) == typeid(*apTarget.afs)) - return apSource.afs->moveAndRenameItemForSameAfsType(apSource.afsPath, apTarget); //throw FileError, ErrorDifferentVolume + if (typeid(apSource.afsDevice.ref()) == typeid(apTarget.afsDevice.ref())) + return apSource.afsDevice.ref().moveAndRenameItemForSameAfsType(apSource.afsPath, apTarget); //throw FileError, ErrorDifferentVolume throw ErrorDifferentVolume(replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(getDisplayPath(apSource))), @@ -532,8 +493,8 @@ void AbstractFileSystem::copyNewFolder(const AbstractPath& apSource, const Abstr { using namespace zen; - if (typeid(*apSource.afs) == typeid(*apTarget.afs)) - return apSource.afs->copyNewFolderForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError + if (typeid(apSource.afsDevice.ref()) == typeid(apTarget.afsDevice.ref())) + return apSource.afsDevice.ref().copyNewFolderForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError //fall back: if (copyFilePermissions) @@ -550,8 +511,8 @@ void AbstractFileSystem::copySymlink(const AbstractPath& apSource, const Abstrac { using namespace zen; - if (typeid(*apSource.afs) == typeid(*apTarget.afs)) - return apSource.afs->copySymlinkForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError + if (typeid(apSource.afsDevice.ref()) == typeid(apTarget.afsDevice.ref())) + return apSource.afsDevice.ref().copySymlinkForSameAfsType(apSource.afsPath, apTarget, copyFilePermissions); //throw FileError throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtPath(getDisplayPath(apSource))), diff --git a/FreeFileSync/Source/fs/concrete_impl.h b/FreeFileSync/Source/fs/concrete_impl.h index 08e075a8..c6ccd2d6 100755 --- a/FreeFileSync/Source/fs/concrete_impl.h +++ b/FreeFileSync/Source/fs/concrete_impl.h @@ -69,13 +69,13 @@ public: //context of controlling thread, non-blocking: template <class Function> - void run(Task<Context, Function>&& wi) + void run(Task<Context, Function>&& wi, bool insertFront = false) { threadGroup_->run([this, wi = std::move(wi)] { try { this->returnResult<Function>({ wi, nullptr, wi.getResult() }); } //throw FileError catch (...) { this->returnResult<Function>({ wi, std::current_exception(), {} }); } - }); + }, insertFront); std::lock_guard<std::mutex> dummy(lockResult_); ++resultsPending_; @@ -197,7 +197,9 @@ void GenericDirTraverser<Functions...>::evalResult(TaskResult<TravContext, Funct cb->reportItemError(e.toString(), result.wi.ctx.errorRetryCount, result.wi.ctx.errorItemName)) //throw X { case AbstractFileSystem::TraverserCallback::ON_ERROR_RETRY: - scheduler_.template run<Function>({ std::move(result.wi.getResult), TravContext{ result.wi.ctx.errorItemName, result.wi.ctx.errorRetryCount + 1, cb }}); + //user expects that the task is retried immediately => we can't do much about other errors already waiting in the queue, but at least *prepend* to the work load! + scheduler_.template run<Function>({ std::move(result.wi.getResult), TravContext{ result.wi.ctx.errorItemName, result.wi.ctx.errorRetryCount + 1, cb }}, + true /*insertFront*/); return; case AbstractFileSystem::TraverserCallback::ON_ERROR_CONTINUE: diff --git a/FreeFileSync/Source/fs/native.cpp b/FreeFileSync/Source/fs/native.cpp index c924777e..a3e0a8f2 100755 --- a/FreeFileSync/Source/fs/native.cpp +++ b/FreeFileSync/Source/fs/native.cpp @@ -235,14 +235,19 @@ void traverseFolderRecursiveNative(const std::vector<std::pair<Zstring, std::sha } } -namespace fff //specialization in original namespace needed by GCC 6 -{ + template <> template <> void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::evalResultValue<GetDirDetails>(const GetDirDetails::Result& r, std::shared_ptr<AFS::TraverserCallback>& cb /*throw X*/) { - for (const FsItemRaw& rawItem : r) - scheduler_.run<GetItemDetails>({ GetItemDetails(rawItem), TravContext{ rawItem.itemName, 0 /*errorRetryCount*/, cb }}); + //attention: if we simply appended to the work queue this would repeatedly allow for situations where a large number of directories are traversed one after another + // without intermittent calls to evalResultValue<GetItemDetails>() => user incorrectly thinks the app is hanging! https://freefilesync.org/forum/viewtopic.php?t=5729 + //solution: *prepend* GetItemDetails() tasks (in correct order) to the work queue ASAP: + std::for_each(r.rbegin(), r.rend(), [&](const FsItemRaw& rawItem) + { + scheduler_.run<GetItemDetails>({ GetItemDetails(rawItem), TravContext{ rawItem.itemName, 0 /*errorRetryCount*/, cb }}, + true /*insertFront*/); + }); } @@ -292,7 +297,7 @@ void GenericDirTraverser<GetDirDetails, GetItemDetails, GetLinkTargetDetails>::e else //a file or named pipe, etc. cb->onFile({ r.raw.itemName, r.target.fileSize, r.target.modTime, convertToAbstractFileId(r.target.fileId), &linkInfo }); //throw X } -} + namespace { @@ -303,13 +308,13 @@ namespace class RecycleSessionNative : public AbstractFileSystem::RecycleSession { public: - RecycleSessionNative(const Zstring baseFolderPath) : baseFolderPathPf_(appendSeparator(baseFolderPath)) {} + RecycleSessionNative(const Zstring baseFolderPath) : baseFolderPath_(baseFolderPath) {} bool recycleItem(const AbstractPath& itemPath, const Zstring& logicalRelPath) override; //throw FileError void tryCleanup(const std::function<void (const std::wstring& displayPath)>& notifyDeletionStatus) override; //throw FileError private: - const Zstring baseFolderPathPf_; //ends with path separator + const Zstring baseFolderPath_; //ends with path separator }; //=========================================================================================================================== @@ -387,7 +392,7 @@ public: NativeFileSystem(const Zstring& rootPath) : rootPath_(rootPath) {} private: - Zstring getNativePath(const AfsPath& afsPath) const { return appendPaths(rootPath_, afsPath.value, FILE_NAME_SEPARATOR); } + Zstring getNativePath(const AfsPath& afsPath) const { return nativeAppendPaths(rootPath_, afsPath.value); } std::optional<Zstring> getNativeItemPath(const AfsPath& afsPath) const override { return getNativePath(afsPath); } @@ -397,11 +402,11 @@ private: bool isNullFileSystem() const override { return rootPath_.empty(); } - int compareDeviceRootSameAfsType(const AbstractFileSystem& afsRhs) const override + int compareDeviceSameAfsType(const AbstractFileSystem& afsRhs) const override { const Zstring& rootPathRhs = static_cast<const NativeFileSystem&>(afsRhs).rootPath_; - return compareLocalPath(rootPath_, rootPathRhs); + return compareNativePath(rootPath_, rootPathRhs); } //---------------------------------------------------------------------------------------------------------------- @@ -421,9 +426,9 @@ private: return AFS::ItemType::FILE; } - PathStatusImpl getPathStatus(const AfsPath& afsPath) const override //throw FileError + std::optional<ItemType> itemStillExists(const AfsPath& afsPath) const override //throw FileError { - return getPathStatusViaFolderTraversal(afsPath); //throw FileError + return itemStillExistsViaFolderTraversal(afsPath); //throw FileError } //---------------------------------------------------------------------------------------------------------------- @@ -459,6 +464,12 @@ private: zen::setFileTime(getNativePath(afsPath), modTime, ProcSymlink::FOLLOW); //throw FileError } + FileId /*optional*/ getFileId(const AfsPath& afsPath) const override //throw FileError + { + initComForThread(); //throw FileError + return convertToAbstractFileId(zen::getFileId(getNativePath(afsPath))); //throw FileError + } + AbstractPath getSymlinkResolvedPath(const AfsPath& afsPath) const override //throw FileError { initComForThread(); //throw FileError @@ -470,7 +481,7 @@ private: throw FileError(replaceCpy(_("Cannot determine final path for %x."), L"%x", fmtPath(nativePath)), replaceCpy<std::wstring>(L"Invalid path %x.", L"%x", fmtPath(resolvedPath))); - return AbstractPath(std::make_shared<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath)); + return AbstractPath(makeSharedRef<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath)); } std::string getSymlinkBinaryContent(const AfsPath& afsPath) const override //throw FileError @@ -500,7 +511,7 @@ private: } //---------------------------------------------------------------------------------------------------------------- - void traverseFolderRecursive(const TraverserWorkloadImpl& workload /*throw X*/, size_t parallelOps) const override + void traverseFolderRecursive(const TraverserWorkload& workload /*throw X*/, size_t parallelOps) const override { //initComForThread() -> done on traverser worker threads @@ -517,7 +528,7 @@ private: FileCopyResult copyFileForSameAfsType(const AfsPath& afsPathSource, const StreamAttributes& attrSource, //throw FileError, ErrorFileLocked const AbstractPath& apTarget, bool copyFilePermissions, const IOCallback& notifyUnbufferedIO) const override //may be nullptr; throw X! { - const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(getAfs(apTarget)).getNativePath(getAfsPath(apTarget)); + const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath); initComForThread(); //throw FileError @@ -539,7 +550,7 @@ private: initComForThread(); //throw FileError const Zstring& sourcePath = getNativePath(afsPathSource); - const Zstring& targetPath = static_cast<const NativeFileSystem&>(getAfs(apTarget)).getNativePath(getAfsPath(apTarget)); + const Zstring& targetPath = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath); zen::createDirectory(targetPath); //throw FileError, ErrorTargetExisting @@ -548,7 +559,7 @@ private: //do NOT copy attributes for volume root paths which return as: FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY //https://freefilesync.org/forum/viewtopic.php?t=5550 - if (getParentAfsPath(afsPathSource)) //=> not a root path + if (getParentPath(afsPathSource)) //=> not a root path tryCopyDirectoryAttributes(sourcePath, targetPath); //throw FileError if (copyFilePermissions) @@ -557,7 +568,7 @@ private: void copySymlinkForSameAfsType(const AfsPath& afsPathSource, const AbstractPath& apTarget, bool copyFilePermissions) const override //throw FileError { - const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(getAfs(apTarget)).getNativePath(getAfsPath(apTarget)); + const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath); initComForThread(); //throw FileError zen::copySymlink(getNativePath(afsPathSource), nativePathTarget, copyFilePermissions); //throw FileError @@ -568,14 +579,14 @@ private: { //perf test: detecting different volumes by path is ~30 times faster than having MoveFileEx fail with ERROR_NOT_SAME_DEVICE (6µs vs 190µs) //=> maybe we can even save some actual I/O in some cases? - if (compareDeviceRootSameAfsType(getAfs(apTarget)) != 0) + if (compareDeviceSameAfsType(apTarget.afsDevice.ref()) != 0) throw ErrorDifferentVolume(replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtPath(getDisplayPath(afsPathSource))), L"%y", L"\n" + fmtPath(AFS::getDisplayPath(apTarget))), formatSystemError(L"compareDeviceRoot", EXDEV) ); initComForThread(); //throw FileError - const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(getAfs(apTarget)).getNativePath(getAfsPath(apTarget)); + const Zstring nativePathTarget = static_cast<const NativeFileSystem&>(apTarget.afsDevice.ref()).getNativePath(apTarget.afsPath); zen::renameFile(getNativePath(afsPathSource), nativePathTarget); //throw FileError, ErrorTargetExisting, ErrorDifferentVolume } @@ -694,7 +705,7 @@ AbstractPath fff::createItemPathNative(const Zstring& itemPathPhrase) //noexcept AbstractPath fff::createItemPathNativeNoFormatting(const Zstring& nativePath) //noexcept { if (const std::optional<PathComponents> comp = parsePathComponents(nativePath)) - return AbstractPath(std::make_shared<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath)); + return AbstractPath(makeSharedRef<NativeFileSystem>(comp->rootPath), AfsPath(comp->relPath)); else //path syntax broken - return AbstractPath(std::make_shared<NativeFileSystem>(nativePath), AfsPath()); + return AbstractPath(makeSharedRef<NativeFileSystem>(nativePath), AfsPath()); } diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index 8b52bacf..51eefbf1 100755 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -79,18 +79,23 @@ BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(const Zstring& const SyncResult finalStatus = [&] { if (getAbortStatus()) + { + errorLog_.logMsg(_("Stopped"), MSG_TYPE_ERROR); //= user cancel; *not* a MSG_TYPE_FATAL_ERROR! return SyncResult::ABORTED; + } else if (errorLog_.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR) > 0) return SyncResult::FINISHED_WITH_ERROR; else if (errorLog_.getItemCount(MSG_TYPE_WARNING) > 0) return SyncResult::FINISHED_WITH_WARNINGS; - else - return SyncResult::FINISHED_WITH_SUCCESS; + + if (getStatsTotal(currentPhase()) == ProgressStats()) + errorLog_.logMsg(_("Nothing to synchronize"), MSG_TYPE_INFO); + return SyncResult::FINISHED_WITH_SUCCESS; }(); assert(finalStatus == SyncResult::ABORTED || currentPhase() == PHASE_SYNCHRONIZING); - ProcessSummary summary + const ProcessSummary summary { finalStatus, jobName_, getStatsCurrent(currentPhase()), @@ -98,12 +103,6 @@ BatchStatusHandler::Result BatchStatusHandler::reportFinalStatus(const Zstring& totalTime }; - const std::wstring& finalStatusLabel = finalStatus == SyncResult::FINISHED_WITH_SUCCESS && - summary.statsTotal.items == 0 && - summary.statsTotal.bytes == 0 ? _("Nothing to synchronize") : - getFinalStatusLabel(finalStatus); - errorLog_.logMsg(finalStatusLabel, getFinalMsgType(finalStatus)); - //post sync command Zstring commandLine = [&] { diff --git a/FreeFileSync/Source/ui/cfg_grid.cpp b/FreeFileSync/Source/ui/cfg_grid.cpp index db49bee4..3d5023b4 100755 --- a/FreeFileSync/Source/ui/cfg_grid.cpp +++ b/FreeFileSync/Source/ui/cfg_grid.cpp @@ -84,7 +84,7 @@ void ConfigView::addCfgFiles(const std::vector<Zstring>& filePaths) std::tie(detail.name, detail.cfgType, detail.isLastRunCfg) = [&] { - if (equalLocalPath(filePath, lastRunConfigPath_)) + if (equalNativePath(filePath, lastRunConfigPath_)) return std::make_tuple(utfTo<Zstring>(L"<" + _("Last session") + L">"), Details::CFG_TYPE_GUI, true); const Zstring fileName = afterLast(filePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); @@ -110,9 +110,9 @@ void ConfigView::addCfgFiles(const std::vector<Zstring>& filePaths) void ConfigView::removeItems(const std::vector<Zstring>& filePaths) { - const std::set<Zstring, LessLocalPath> pathsSorted(filePaths.begin(), filePaths.end()); + const std::set<Zstring, LessNativePath> pathsSorted(filePaths.begin(), filePaths.end()); - erase_if(cfgListView_, [&](auto it) { return pathsSorted.find(it->first) != pathsSorted.end(); }); + eraseIf(cfgListView_, [&](auto it) { return pathsSorted.find(it->first) != pathsSorted.end(); }); for (const Zstring& filePath : filePaths) cfgList_.erase(filePath); @@ -581,7 +581,7 @@ void cfggrid::addAndSelect(Grid& grid, const std::vector<Zstring>& filePaths, bo grid.clearSelection(GridEventPolicy::DENY); - const std::set<Zstring, LessLocalPath> pathsSorted(filePaths.begin(), filePaths.end()); + const std::set<Zstring, LessNativePath> pathsSorted(filePaths.begin(), filePaths.end()); std::optional<size_t> selectionTopRow; for (size_t i = 0; i < grid.getRowCount(); ++i) diff --git a/FreeFileSync/Source/ui/cfg_grid.h b/FreeFileSync/Source/ui/cfg_grid.h index 66cd4dd8..3983df06 100755 --- a/FreeFileSync/Source/ui/cfg_grid.h +++ b/FreeFileSync/Source/ui/cfg_grid.h @@ -134,7 +134,7 @@ private: const Zstring lastRunConfigPath_ = getLastRunConfigPath(); //let's not use another static... - using CfgFileList = std::map<Zstring /*file path*/, Details, LessLocalPath>; + using CfgFileList = std::map<Zstring /*file path*/, Details, LessNativePath>; CfgFileList cfgList_; std::vector<CfgFileList::iterator> cfgListView_; //sorted view on cfgList_ diff --git a/FreeFileSync/Source/ui/command_box.cpp b/FreeFileSync/Source/ui/command_box.cpp index 9d8c7a9b..2ef53a51 100755 --- a/FreeFileSync/Source/ui/command_box.cpp +++ b/FreeFileSync/Source/ui/command_box.cpp @@ -75,7 +75,7 @@ void CommandBox::addItemHistory() equalNoCase(newCommand, cmd)) return; - erase_if(history_, [&](const Zstring& item) { return equalNoCase(newCommand, item); }); + eraseIf(history_, [&](const Zstring& item) { return equalNoCase(newCommand, item); }); history_.insert(history_.begin(), newCommand); @@ -192,7 +192,7 @@ void CommandBox::OnKeyEvent(wxKeyEvent& event) //this->SetSelection(wxNOT_FOUND); //delete selected row - erase_if(history_, [&](const Zstring& item) { return item == selValue; }); + eraseIf(history_, [&](const Zstring& item) { return item == selValue; }); SetString(pos, wxString()); //in contrast to Delete(), this one does not kill the drop-down list and gives a nice visual feedback! //Delete(pos); diff --git a/FreeFileSync/Source/ui/file_view.cpp b/FreeFileSync/Source/ui/file_view.cpp index fdf23760..f5d2f79e 100755 --- a/FreeFileSync/Source/ui/file_view.cpp +++ b/FreeFileSync/Source/ui/file_view.cpp @@ -262,7 +262,7 @@ void FileView::removeInvalidRows() rowPositionsFirstChild_.clear(); //remove rows that have been deleted meanwhile - erase_if(sortedRef_, [&](const RefIndex& refIdx) { return !FileSystemObject::retrieve(refIdx.objId); }); + eraseIf(sortedRef_, [&](const RefIndex& refIdx) { return !FileSystemObject::retrieve(refIdx.objId); }); } diff --git a/FreeFileSync/Source/ui/folder_history_box.cpp b/FreeFileSync/Source/ui/folder_history_box.cpp index 888d2a0f..932e6730 100755 --- a/FreeFileSync/Source/ui/folder_history_box.cpp +++ b/FreeFileSync/Source/ui/folder_history_box.cpp @@ -62,8 +62,8 @@ void FolderHistoryBox::setValueAndUpdateList(const wxString& folderPathPhrase) //populate selection list.... std::vector<wxString> dirList; { - //add some aliases to allow user changing to volume name and back, if possible - std::vector<Zstring> aliases = getDirectoryAliases(utfTo<Zstring>(folderPathPhrase)); //may block when resolving [<volume name>] + //allow user changing to volume name and back, if possible + std::vector<Zstring> aliases = getFolderPathAliases(utfTo<Zstring>(folderPathPhrase)); //may block when resolving [<volume name>] std::transform(aliases.begin(), aliases.end(), std::back_inserter(dirList), [](const Zstring& str) { return utfTo<wxString>(str); }); } if (sharedHistory_.get()) diff --git a/FreeFileSync/Source/ui/folder_history_box.h b/FreeFileSync/Source/ui/folder_history_box.h index bcbe343b..e83346e1 100755 --- a/FreeFileSync/Source/ui/folder_history_box.h +++ b/FreeFileSync/Source/ui/folder_history_box.h @@ -41,7 +41,7 @@ public: const Zstring nameTmp = zen::trimCpy(folderPathPhrase); //insert new folder or put it to the front if already existing - zen::erase_if(folderPathPhrases_, [&](const Zstring& item) { return equalNoCase(item, nameTmp); }); + zen::eraseIf(folderPathPhrases_, [&](const Zstring& item) { return equalNoCase(item, nameTmp); }); folderPathPhrases_.insert(folderPathPhrases_.begin(), nameTmp); @@ -49,7 +49,7 @@ public: folderPathPhrases_.resize(maxSize_); } - void delItem(const Zstring& folderPathPhrase) { zen::erase_if(folderPathPhrases_, [&](const Zstring& item) { return equalNoCase(item, folderPathPhrase); }); } + void delItem(const Zstring& folderPathPhrase) { zen::eraseIf(folderPathPhrases_, [&](const Zstring& item) { return equalNoCase(item, folderPathPhrase); }); } private: size_t maxSize_ = 0; diff --git a/FreeFileSync/Source/ui/folder_selector.cpp b/FreeFileSync/Source/ui/folder_selector.cpp index c050994b..7f44e50e 100755 --- a/FreeFileSync/Source/ui/folder_selector.cpp +++ b/FreeFileSync/Source/ui/folder_selector.cpp @@ -145,7 +145,7 @@ void FolderSelector::onItemPathDropped(FileDropEvent& event) try { if (AFS::getItemType(itemPath) == AFS::ItemType::FILE) //throw FileError - if (std::optional<AbstractPath> parentPath = AFS::getParentFolderPath(itemPath)) + if (std::optional<AbstractPath> parentPath = AFS::getParentPath(itemPath)) return AFS::getInitPathPhrase(*parentPath); } catch (FileError&) {} //e.g. good for inactive mapped network shares, not so nice for C:\pagefile.sys diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index 24c86623..201724f6 100755 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -1545,7 +1545,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer159->Add( bSizerCompMisc, 0, wxEXPAND, 5 ); - bSizer2561->Add( bSizer159, 1, wxEXPAND, 5 ); + bSizer2561->Add( bSizer159, 0, wxEXPAND, 5 ); m_staticlinePerformance = new wxStaticLine( m_panelComparisonSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); bSizer2561->Add( m_staticlinePerformance, 0, wxEXPAND, 5 ); @@ -1609,7 +1609,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizerPerformance->Add( bSizer260, 1, wxALL|wxEXPAND, 5 ); - bSizer2561->Add( bSizerPerformance, 0, wxEXPAND, 5 ); + bSizer2561->Add( bSizerPerformance, 1, wxEXPAND, 5 ); m_panelComparisonSettings->SetSizer( bSizer2561 ); @@ -2304,7 +2304,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer2372->Add( m_panelLogfile, 1, 0, 5 ); - bSizerSyncMisc->Add( bSizer2372, 1, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + bSizerSyncMisc->Add( bSizer2372, 1, wxALL, 10 ); m_staticline57 = new wxStaticLine( m_panelSyncSettings, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); bSizerSyncMisc->Add( m_staticline57, 0, wxEXPAND, 5 ); @@ -2315,7 +2315,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer251; bSizer251 = new wxBoxSizer( wxHORIZONTAL ); - m_staticText89 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Run a command after synchronization:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText89 = new wxStaticText( m_panelSyncSettings, wxID_ANY, _("Run a command:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText89->Wrap( -1 ); bSizer251->Add( m_staticText89, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); @@ -2334,10 +2334,10 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w bSizer247->Add( m_comboBoxPostSyncCommand, 0, wxTOP|wxEXPAND, 5 ); - bSizerSyncMisc->Add( bSizer247, 1, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); + bSizerSyncMisc->Add( bSizer247, 1, wxALL, 10 ); - bSizer232->Add( bSizerSyncMisc, 0, wxEXPAND, 5 ); + bSizer232->Add( bSizerSyncMisc, 1, wxEXPAND, 5 ); m_panelSyncSettings->SetSizer( bSizer232 ); @@ -4231,7 +4231,7 @@ OptionsDlgGenerated::OptionsDlgGenerated( wxWindow* parent, wxWindowID id, const wxBoxSizer* bSizer282; bSizer282 = new wxBoxSizer( wxHORIZONTAL ); - m_checkBoxLogFilesMaxAge = new wxCheckBox( m_panel39, wxID_ANY, _("Remove old log files after x days:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_checkBoxLogFilesMaxAge = new wxCheckBox( m_panel39, wxID_ANY, _("&Delete logs after x days:"), wxDefaultPosition, wxDefaultSize, 0 ); bSizer282->Add( m_checkBoxLogFilesMaxAge, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 ); m_spinCtrlLogFilesMaxAge = new wxSpinCtrl( m_panel39, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1, -1 ), wxSP_ARROW_KEYS, 1, 2000000000, 1 ); @@ -4466,7 +4466,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer186; bSizer186 = new wxBoxSizer( wxVERTICAL ); - m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText94 = new wxStaticText( m_panel41, wxID_ANY, _("Feedback and suggestions are welcome:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText94->Wrap( -1 ); bSizer186->Add( m_staticText94, 0, wxALL, 5 ); @@ -4727,7 +4727,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS wxBoxSizer* bSizer185; bSizer185 = new wxBoxSizer( wxVERTICAL ); - m_staticText93 = new wxStaticText( m_panel41, wxID_ANY, _("Published under the GNU General Public License"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText93 = new wxStaticText( m_panel41, wxID_ANY, _("Published under the GNU General Public License:"), wxDefaultPosition, wxDefaultSize, 0 ); m_staticText93->Wrap( -1 ); bSizer185->Add( m_staticText93, 0, wxALL, 5 ); diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp index 0a52e18a..1e0460e5 100755 --- a/FreeFileSync/Source/ui/gui_status_handler.cpp +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -9,12 +9,10 @@ #include <zen/shutdown.h> #include <wx/app.h> #include <wx/wupdlock.h> -//#include <wx+/bitmap_button.h> #include <wx+/popup_dlg.h> #include "main_dlg.h" #include "../base/generate_logfile.h" #include "../base/resolve_path.h" -//#include "../base/status_handler_impl.h" #include "../fs/concrete.h" using namespace zen; @@ -133,7 +131,10 @@ StatusHandlerTemporaryPanel::Result StatusHandlerTemporaryPanel::reportFinalStat const SyncResult finalStatus = [&] { if (getAbortStatus()) + { + errorLog_.logMsg(_("Stopped"), MSG_TYPE_ERROR); //= user cancel; *not* a MSG_TYPE_FATAL_ERROR! return SyncResult::ABORTED; + } else if (errorLog_.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR) > 0) return SyncResult::FINISHED_WITH_ERROR; else if (errorLog_.getItemCount(MSG_TYPE_WARNING) > 0) @@ -142,9 +143,7 @@ StatusHandlerTemporaryPanel::Result StatusHandlerTemporaryPanel::reportFinalStat return SyncResult::FINISHED_WITH_SUCCESS; }(); - errorLog_.logMsg(getFinalStatusLabel(finalStatus), getFinalMsgType(finalStatus)); - - ProcessSummary summary + const ProcessSummary summary { finalStatus, {} /*jobName*/, getStatsCurrent(currentPhase()), @@ -152,7 +151,6 @@ StatusHandlerTemporaryPanel::Result StatusHandlerTemporaryPanel::reportFinalStat totalTime }; - auto errorLogFinal = std::make_shared<const ErrorLog>(std::move(errorLog_)); errorLog_ = ErrorLog(); //see check in ~StatusHandlerTemporaryPanel() @@ -350,18 +348,23 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStat const SyncResult finalStatus = [&] { if (getAbortStatus()) + { + errorLog_.logMsg(_("Stopped"), MSG_TYPE_ERROR); //= user cancel; *not* a MSG_TYPE_FATAL_ERROR! return SyncResult::ABORTED; + } else if (errorLog_.getItemCount(MSG_TYPE_ERROR | MSG_TYPE_FATAL_ERROR) > 0) return SyncResult::FINISHED_WITH_ERROR; else if (errorLog_.getItemCount(MSG_TYPE_WARNING) > 0) return SyncResult::FINISHED_WITH_WARNINGS; - else - return SyncResult::FINISHED_WITH_SUCCESS; + + if (getStatsTotal(currentPhase()) == ProgressStats()) + errorLog_.logMsg(_("Nothing to synchronize"), MSG_TYPE_INFO); + return SyncResult::FINISHED_WITH_SUCCESS; }(); assert(finalStatus == SyncResult::ABORTED || currentPhase() == PHASE_SYNCHRONIZING); - ProcessSummary summary + const ProcessSummary summary { finalStatus, jobName_, getStatsCurrent(currentPhase()), @@ -369,12 +372,6 @@ StatusHandlerFloatingDialog::Result StatusHandlerFloatingDialog::reportFinalStat totalTime }; - const std::wstring& finalStatusLabel = finalStatus == SyncResult::FINISHED_WITH_SUCCESS && - summary.statsTotal.items == 0 && - summary.statsTotal.bytes == 0 ? _("Nothing to synchronize") : - getFinalStatusLabel(finalStatus); - errorLog_.logMsg(finalStatusLabel, getFinalMsgType(finalStatus)); - //post sync command Zstring commandLine = [&] { diff --git a/FreeFileSync/Source/ui/log_panel.cpp b/FreeFileSync/Source/ui/log_panel.cpp index fc7810e0..fca60a36 100755 --- a/FreeFileSync/Source/ui/log_panel.cpp +++ b/FreeFileSync/Source/ui/log_panel.cpp @@ -536,7 +536,7 @@ void LogPanel::copySelectionToClipboard() if (auto prov = m_gridMessages->getDataProvider()) { std::vector<Grid::ColAttributes> colAttr = m_gridMessages->getColumnConfig(); - erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; }); + eraseIf(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; }); if (!colAttr.empty()) for (size_t row : m_gridMessages->getSelectedRows()) { diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index 1cb67b60..92a545ee 100755 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -263,13 +263,19 @@ XmlGlobalSettings tryLoadGlobalConfig(const Zstring& globalConfigFilePath) //blo { std::wstring warningMsg; readConfig(globalConfigFilePath, globalCfg, warningMsg); //throw FileError - assert(warningMsg.empty()); //ignore parsing errors: should be migration problems only *cross-fingers* } - catch (const FileError& e) + catch (FileError&) { - if (!itemNotExisting(globalConfigFilePath)) //existing or access error + try + { + if (itemStillExists(globalConfigFilePath)) //throw FileError + throw; + } + catch (const FileError& e) + { showNotificationDialog(nullptr, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); //no parent window: main dialog not yet created! + } } return globalCfg; } @@ -284,7 +290,7 @@ void MainDialog::create(const Zstring& globalConfigFilePath) //------------------------------------------------------------------------------------------ //check existence of all files in parallel: - GetFirstResult<std::false_type> firstUnavailableFile; + AsyncFirstResult<std::false_type> firstUnavailableFile; for (const Zstring& filePath : cfgFilePaths) firstUnavailableFile.addJob([filePath]() -> std::optional<std::false_type> @@ -372,8 +378,8 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, MainDialogGenerated(nullptr), globalConfigFilePath_(globalConfigFilePath) { - m_folderPathLeft ->init(folderHistoryLeft_); - m_folderPathRight->init(folderHistoryRight_); + m_folderPathLeft ->init(folderHistoryLeft_ .ptr()); + m_folderPathRight->init(folderHistoryRight_.ptr()); //setup sash: detach + reparent: m_splitterMain->SetSizer(nullptr); //alas wxFormbuilder doesn't allow us to have child windows without a sizer, so we have to remove it here @@ -716,8 +722,8 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, //1. setConfig() indirectly calls cfggrid::addAndSelect() which changes cfg history scroll position //2. Grid::makeRowVisible() requires final window height! => do this after window resizing is complete if (m_gridCfgHistory->getRowCount() > 0) - m_gridCfgHistory->scrollTo(numeric::clampCpy<size_t>(globalSettings.gui.mainDlg.cfgGridTopRowPos, //must be set *after* wxAuiManager::LoadPerspective() to have any effect - 0, m_gridCfgHistory->getRowCount() - 1)); + m_gridCfgHistory->scrollTo(std::clamp<size_t>(globalSettings.gui.mainDlg.cfgGridTopRowPos, //must be set *after* wxAuiManager::LoadPerspective() to have any effect + 0, m_gridCfgHistory->getRowCount() - 1)); //first selected item should always be visible: const std::vector<size_t> selectedRows = m_gridCfgHistory->getSelectedRows(); @@ -764,7 +770,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFilePath, if (havePartialPair != haveFullPair) //either all pairs full or all half-filled -> validity check! { //check existence of all directories in parallel! - GetFirstResult<std::false_type> firstMissingDir; + AsyncFirstResult<std::false_type> firstMissingDir; for (const AbstractPath& folderPath : folderPathsToCheck) firstMissingDir.addJob([folderPath]() -> std::optional<std::false_type> { @@ -867,36 +873,41 @@ void MainDialog::setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings) //caveat set/get language asymmmetry! setLanguage(globalSettings.programLanguage); //throw FileError //we need to set langugabe before creating this class! + wxSize newSize(fastFromDIP(900), fastFromDIP(600)); //default window size + std::optional<wxPoint> newPos; //set dialog size and position: // - width/height are invalid if the window is minimized (eg x,y == -32000; height = 28, width = 160) // - multi-monitor setups: dialog may be placed on second monitor which is currently turned off if (globalSettings.gui.mainDlg.dlgSize.GetWidth () > 0 && globalSettings.gui.mainDlg.dlgSize.GetHeight() > 0) { + newSize = globalSettings.gui.mainDlg.dlgSize; + //calculate how much of the dialog will be visible on screen - const int dialogAreaTotal = globalSettings.gui.mainDlg.dlgSize.GetWidth() * globalSettings.gui.mainDlg.dlgSize.GetHeight(); - int dialogAreaVisible = 0; + const int dlgArea = newSize.GetWidth() * newSize.GetHeight(); + int dlgAreaMaxVisible = 0; const int monitorCount = wxDisplay::GetCount(); for (int i = 0; i < monitorCount; ++i) { - wxRect intersection = wxDisplay(i).GetClientArea().Intersect(wxRect(globalSettings.gui.mainDlg.dlgPos, globalSettings.gui.mainDlg.dlgSize)); - dialogAreaVisible = std::max(dialogAreaVisible, intersection.GetWidth() * intersection.GetHeight()); + wxRect intersection = wxDisplay(i).GetClientArea().Intersect(wxRect(globalSettings.gui.mainDlg.dlgPos, newSize)); + dlgAreaMaxVisible = std::max(dlgAreaMaxVisible, intersection.GetWidth() * intersection.GetHeight()); } - //wxGTK's wxWindow::SetSize seems unreliable and behaves like a wxWindow::SetClientSize - //=> use wxWindow::SetClientSize instead (for the record: no such issue on Windows/OS X) - SetClientSize(globalSettings.gui.mainDlg.dlgSize); - - if (dialogAreaVisible > 0.1 * dialogAreaTotal //at least 10% of the dialog should be visible! + if (dlgAreaMaxVisible > 0.1 * dlgArea //at least 10% of the dialog should be visible! ) - SetPosition(globalSettings.gui.mainDlg.dlgPos); - else - Center(); + newPos = globalSettings.gui.mainDlg.dlgPos; } - else //default window size and position + + //old comment: "wxGTK's wxWindow::SetSize seems unreliable and behaves like a wxWindow::SetClientSize + // => use wxWindow::SetClientSize instead (for the record: no such issue on Windows/OS X) + //2018-10-15: Weird new problem on Centos/Ubuntu: SetClientSize() + SetPosition() fail to set correct dialog *position*, but SetSize() + SetPosition() do! + // => old issues with SetSize() seem to be gone... => revert to SetSize() + if (newPos) + SetSize(wxRect(*newPos, newSize)); + else { - SetClientSize(wxSize(fastFromDIP(900), fastFromDIP(550))); //=~ 900 x 600 total size + SetSize(newSize); Center(); } @@ -939,8 +950,8 @@ void MainDialog::setGlobalCfgOnInit(const XmlGlobalSettings& globalSettings) //-------------------------------------------------------------------------------- //load list of last used folders - *folderHistoryLeft_ = FolderHistory(globalSettings.gui.mainDlg.folderHistoryLeft, globalSettings.gui.mainDlg.folderHistItemsMax); - *folderHistoryRight_ = FolderHistory(globalSettings.gui.mainDlg.folderHistoryRight, globalSettings.gui.mainDlg.folderHistItemsMax); + folderHistoryLeft_ .ref() = FolderHistory(globalSettings.gui.mainDlg.folderHistoryLeft, globalSettings.gui.mainDlg.folderHistItemsMax); + folderHistoryRight_.ref() = FolderHistory(globalSettings.gui.mainDlg.folderHistoryRight, globalSettings.gui.mainDlg.folderHistItemsMax); //show/hide file icons filegrid::setupIcons(*m_gridMainL, *m_gridMainC, *m_gridMainR, globalSettings.gui.mainDlg.showIcons, convert(globalSettings.gui.mainDlg.iconSize)); @@ -1015,8 +1026,8 @@ XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() globalSettings.gui.mainDlg.lastUsedConfigFiles = activeConfigFiles_; //write list of last used folders - globalSettings.gui.mainDlg.folderHistoryLeft = folderHistoryLeft_ ->getList(); - globalSettings.gui.mainDlg.folderHistoryRight = folderHistoryRight_->getList(); + globalSettings.gui.mainDlg.folderHistoryLeft = folderHistoryLeft_ .ref().getList(); + globalSettings.gui.mainDlg.folderHistoryRight = folderHistoryRight_.ref().getList(); globalSettings.gui.mainDlg.textSearchRespectCase = m_checkBoxMatchCase->GetValue(); @@ -1046,7 +1057,7 @@ XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() Maximize(false); } - globalSettings.gui.mainDlg.dlgSize = GetClientSize(); + globalSettings.gui.mainDlg.dlgSize = GetSize(); globalSettings.gui.mainDlg.dlgPos = GetPosition(); //wxGTK: returns full screen size and strange position (65/-4) @@ -1057,7 +1068,6 @@ XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() globalSettings.gui.mainDlg.dlgSize = wxSize(); globalSettings.gui.mainDlg.dlgPos = wxPoint(); } - return globalSettings; } @@ -1102,7 +1112,7 @@ void MainDialog::copySelectionToClipboard(const std::vector<const Grid*>& gridRe if (auto prov = grid->getDataProvider()) { std::vector<Grid::ColAttributes> colAttr = grid->getColumnConfig(); - erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; }); + eraseIf(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; }); if (!colAttr.empty()) for (size_t row : grid->getSelectedRows()) { @@ -1176,18 +1186,14 @@ std::vector<FileSystemObject*> MainDialog::getTreeSelection() const void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& selectionLeft, const std::vector<FileSystemObject*>& selectionRight) { - std::vector<const FileSystemObject*> rowsLeftTmp; - std::vector<const FileSystemObject*> rowsRightTmp; - std::copy_if(selectionLeft .begin(), selectionLeft .end(), std::back_inserter(rowsLeftTmp), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty< LEFT_SIDE>(); }); - std::copy_if(selectionRight.begin(), selectionRight.end(), std::back_inserter(rowsRightTmp), [](const FileSystemObject* fsObj) { return !fsObj->isEmpty<RIGHT_SIDE>(); }); - - if (rowsLeftTmp.empty() && rowsRightTmp.empty()) - return; + if (std::all_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }) && + std::all_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< RIGHT_SIDE>(); })) + return; FocusPreserver fp; if (showCopyToDialog(this, - rowsLeftTmp, rowsRightTmp, + selectionLeft, selectionRight, globalCfg_.gui.mainDlg.copyToCfg.lastUsedPath, globalCfg_.gui.mainDlg.copyToCfg.folderHistory, globalCfg_.gui.mainDlg.folderHistItemsMax, @@ -1208,7 +1214,7 @@ void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& sel guiCfg.mainCfg.automaticRetryDelay); //handle status display and error messages try { - fff::copyToAlternateFolder(rowsLeftTmp, rowsRightTmp, + fff::copyToAlternateFolder(selectionLeft, selectionRight, globalCfg_.gui.mainDlg.copyToCfg.lastUsedPath, globalCfg_.gui.mainDlg.copyToCfg.keepRelPaths, globalCfg_.gui.mainDlg.copyToCfg.overwriteIfExists, @@ -1230,17 +1236,13 @@ void MainDialog::copyToAlternateFolder(const std::vector<FileSystemObject*>& sel void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selectionLeft, const std::vector<FileSystemObject*>& selectionRight, bool moveToRecycler) { - std::vector<FileSystemObject*> rowsLeftTmp = selectionLeft; - std::vector<FileSystemObject*> rowsRightTmp = selectionRight; - erase_if(rowsLeftTmp, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); - erase_if(rowsRightTmp, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); - if (rowsLeftTmp.empty() && rowsRightTmp.empty()) - return; + if (std::all_of(selectionLeft .begin(), selectionLeft .end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }) && + std::all_of(selectionRight.begin(), selectionRight.end(), [](const FileSystemObject* fsObj) { return fsObj->isEmpty< RIGHT_SIDE>(); })) + return; FocusPreserver fp; - //sigh: do senseless vector<FileSystemObject*> -> vector<const FileSystemObject*> conversion: - if (showDeleteDialog(this, { rowsLeftTmp.begin(), rowsLeftTmp.end() }, { rowsRightTmp.begin(), rowsRightTmp.end() }, + if (showDeleteDialog(this, selectionLeft, selectionRight, moveToRecycler) != ReturnSmallDlg::BUTTON_OKAY) return; @@ -1259,7 +1261,7 @@ void MainDialog::deleteSelectedFiles(const std::vector<FileSystemObject*>& selec guiCfg.mainCfg.automaticRetryDelay); //handle status display and error messages try { - deleteFromGridAndHD(rowsLeftTmp, rowsRightTmp, + deleteFromGridAndHD(selectionLeft, selectionRight, folderCmp_, extractDirectionCfg(getConfig().mainCfg), moveToRecycler, @@ -1675,7 +1677,8 @@ void MainDialog::enableAllElements() m_panelConfig ->Enable(); m_panelViewFilter ->Enable(); - Refresh(); //at least wxWidgets on macOS fails to do this after enabling + Refresh(); //at least wxWidgets on macOS fails to do this after enabling + auiMgr_.Update(); // } @@ -2315,8 +2318,8 @@ void MainDialog::onMainGridContextRim(bool leftSide) std::vector<FileSystemObject*> nonEmptySelectionLeft = selectionLeft; std::vector<FileSystemObject*> nonEmptySelectionRight = selectionRight; - erase_if(nonEmptySelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); - erase_if(nonEmptySelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); + eraseIf(nonEmptySelectionLeft, [](const FileSystemObject* fsObj) { return fsObj->isEmpty< LEFT_SIDE>(); }); + eraseIf(nonEmptySelectionRight, [](const FileSystemObject* fsObj) { return fsObj->isEmpty<RIGHT_SIDE>(); }); menu.addSeparator(); @@ -2733,7 +2736,7 @@ void MainDialog::cfgHistoryRemoveObsolete(const std::vector<Zstring>& filePaths) void MainDialog::updateUnsavedCfgStatus() { - const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); + const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); const bool haveUnsavedCfg = lastSavedCfg_ != getConfig(); @@ -2780,7 +2783,7 @@ void MainDialog::updateUnsavedCfgStatus() void MainDialog::OnConfigSave(wxCommandEvent& event) { - const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); + const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); //if we work on a single named configuration document: save directly if changed //else: always show file dialog @@ -2834,7 +2837,7 @@ bool MainDialog::trySaveConfig(const Zstring* guiFilename) //return true if save } else { - const Zstring defaultFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstr("SyncSettings.ffs_gui"); + const Zstring defaultFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstr("SyncSettings.ffs_gui"); auto defaultFolder = utfTo<wxString>(beforeLast(defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE)); auto defaultFileName = utfTo<wxString>(afterLast (defaultFilePath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL)); @@ -2873,7 +2876,7 @@ bool MainDialog::trySaveBatchConfig(const Zstring* batchFileToUpdate) { //essentially behave like trySaveConfig(): the collateral damage of not saving GUI-only settings "m_bpButtonViewTypeSyncAction" is negligible - const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); + const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); //prepare batch config: reuse existing batch-specific settings from file if available BatchExclusiveConfig batchExCfg; @@ -2957,7 +2960,7 @@ bool MainDialog::saveOldConfig() //return false on user abort { if (lastSavedCfg_ != getConfig()) { - const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); + const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); //notify user about changed settings if (globalCfg_.confirmDlgs.popupOnConfigChange) @@ -3015,7 +3018,7 @@ bool MainDialog::saveOldConfig() //return false on user abort void MainDialog::OnConfigLoad(wxCommandEvent& event) { - const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); + const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); wxFileDialog filePicker(this, wxString(), @@ -3683,7 +3686,7 @@ void MainDialog::OnCompare(wxCommandEvent& event) const auto& guiCfg = getConfig(); const std::chrono::system_clock::time_point startTime = std::chrono::system_clock::now(); - const std::map<AbstractPath, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; + const std::map<AfsDevice, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; //handle status display and error messages StatusHandlerTemporaryPanel statusHandler(*this, startTime, @@ -3740,8 +3743,8 @@ void MainDialog::OnCompare(wxCommandEvent& event) RequestUserAttention(); //add to folder history after successful comparison only - folderHistoryLeft_ ->addItem(utfTo<Zstring>(m_folderPathLeft ->GetValue())); - folderHistoryRight_->addItem(utfTo<Zstring>(m_folderPathRight->GetValue())); + folderHistoryLeft_ .ref().addItem(utfTo<Zstring>(m_folderPathLeft ->GetValue())); + folderHistoryRight_.ref().addItem(utfTo<Zstring>(m_folderPathRight->GetValue())); assert(m_buttonCompare->GetId() != wxID_ANY); if (fp.getFocusId() == m_buttonCompare->GetId()) @@ -3773,7 +3776,8 @@ void MainDialog::updateGui() m_menuItemExportList->Enable(!folderCmp_.empty()); //a CSV without even folder names confuses users: https://freefilesync.org/forum/viewtopic.php?t=4787 - auiMgr_.Update(); //fix small display distortion, if view filter panel is empty + warn_static("still needed???") + //auiMgr_.Update(); //fix small display distortion, if view filter panel is empty } @@ -3879,13 +3883,13 @@ void MainDialog::OnStartSync(wxCommandEvent& event) globalCfg_.confirmDlgs.confirmSyncStart = !dontShowAgain; } - const std::map<AbstractPath, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; + const std::map<AfsDevice, size_t>& deviceParallelOps = guiCfg.mainCfg.deviceParallelOps; std::set<AbstractPath> logFilePathsToKeep; for (const ConfigFileItem& item : cfggrid::getDataView(*m_gridCfgHistory).get()) logFilePathsToKeep.insert(item.logFilePath); - const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalLocalPath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); + const Zstring activeCfgFilePath = activeConfigFiles_.size() == 1 && !equalNativePath(activeConfigFiles_[0], lastRunConfigPath_) ? activeConfigFiles_[0] : Zstring(); const std::chrono::system_clock::time_point syncStartTime = std::chrono::system_clock::now(); bool exitAfterSync = false; @@ -3919,18 +3923,18 @@ void MainDialog::OnStartSync(wxCommandEvent& event) std::unique_ptr<LockHolder> dirLocks; if (globalCfg_.createLockFile) { - std::set<Zstring, LessFilePath> availableDirPaths; + std::set<Zstring> folderPathsToLock; for (auto it = begin(folderCmp_); it != end(folderCmp_); ++it) { if (it->isAvailable<LEFT_SIDE>()) //do NOT check directory existence again! if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<LEFT_SIDE>())) //restrict directory locking to native paths until further - availableDirPaths.insert(*nativeFolderPath); + folderPathsToLock.insert(*nativeFolderPath); if (it->isAvailable<RIGHT_SIDE>()) if (std::optional<Zstring> nativeFolderPath = AFS::getNativeItemPath(it->getAbstractPath<RIGHT_SIDE>())) - availableDirPaths.insert(*nativeFolderPath); + folderPathsToLock.insert(*nativeFolderPath); } - dirLocks = std::make_unique<LockHolder>(availableDirPaths, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); //throw AbortProcess + dirLocks = std::make_unique<LockHolder>(folderPathsToLock, globalCfg_.warnDlgs.warnDirectoryLockFailed, statusHandler); //throw AbortProcess } //START SYNCHRONIZATION @@ -4749,8 +4753,8 @@ void MainDialog::insertAddFolderPair(const std::vector<LocalPairConfig>& newPair FolderPairPanel* newPair = new FolderPairPanel(m_scrolledWindowFolderPairs, *this); //init dropdown history - newPair->m_folderPathLeft ->init(folderHistoryLeft_); - newPair->m_folderPathRight->init(folderHistoryRight_); + newPair->m_folderPathLeft ->init(folderHistoryLeft_ .ptr()); + newPair->m_folderPathRight->init(folderHistoryRight_.ptr()); newPair->m_bpButtonFolderPairOptions->SetBitmapLabel(getResourceImage(L"button_arrow_down")); @@ -4907,9 +4911,9 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) auto colAttrCenter = m_gridMainC->getColumnConfig(); auto colAttrRight = m_gridMainR->getColumnConfig(); - erase_if(colAttrLeft, [](const Grid::ColAttributes& ca) { return !ca.visible; }); - erase_if(colAttrCenter, [](const Grid::ColAttributes& ca) { return !ca.visible || static_cast<ColumnTypeCenter>(ca.type) == ColumnTypeCenter::CHECKBOX; }); - erase_if(colAttrRight, [](const Grid::ColAttributes& ca) { return !ca.visible; }); + eraseIf(colAttrLeft, [](const Grid::ColAttributes& ca) { return !ca.visible; }); + eraseIf(colAttrCenter, [](const Grid::ColAttributes& ca) { return !ca.visible || static_cast<ColumnTypeCenter>(ca.type) == ColumnTypeCenter::CHECKBOX; }); + eraseIf(colAttrRight, [](const Grid::ColAttributes& ca) { return !ca.visible; }); if (provLeft && provCenter && provRight) { diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h index dbb0d136..fb37e667 100755 --- a/FreeFileSync/Source/ui/main_dlg.h +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -336,8 +336,8 @@ private: time_t manualTimeSpanFrom_ = 0; time_t manualTimeSpanTo_ = 0; //buffer manual time span selection at session level - std::shared_ptr<FolderHistory> folderHistoryLeft_ = std::make_shared<FolderHistory>(); //shared by all wxComboBox dropdown controls - std::shared_ptr<FolderHistory> folderHistoryRight_ = std::make_shared<FolderHistory>(); //always bound! + zen::SharedRef<FolderHistory> folderHistoryLeft_ = zen::makeSharedRef<FolderHistory>(); //shared by all wxComboBox dropdown controls + zen::SharedRef<FolderHistory> folderHistoryRight_ = zen::makeSharedRef<FolderHistory>(); //always bound! zen::AsyncGuiQueue guiQueue_; //schedule and run long-running tasks asynchronously, but process results on GUI queue diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index a2cfa0fa..573540b2 100755 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -922,7 +922,7 @@ SyncProgressDialogImpl<TopLevelDialog>::~SyncProgressDialogImpl() parentFrame_->Disconnect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncProgressDialogImpl::onParentKeyEvent), nullptr, this); parentFrame_->SetTitle(parentTitleBackup_); //restore title text - + //make sure main dialog is shown again if still "minimized to systray"! see SyncProgressDialog::closeDirectly() parentFrame_->Show(); //if (parentFrame_->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! @@ -1328,8 +1328,10 @@ void SyncProgressDialogImpl<TopLevelDialog>::showSummary(SyncResult finalStatus, const double timeDelta = std::chrono::duration<double>(stopWatch_.elapsed() - phaseStart_).count(); //we need to consider "time within current phase" not total "timeElapsed"! - const wxString overallBytesPerSecond = numeric::isNull(timeDelta) ? std::wstring() : formatFilesizeShort(numeric::round(bytesProcessed / timeDelta)) + _("/sec"); - const wxString overallItemsPerSecond = numeric::isNull(timeDelta) ? std::wstring() : replaceCpy(_("%x items/sec"), L"%x", formatThreeDigitPrecision(itemsProcessed / timeDelta)); + const wxString overallBytesPerSecond = numeric::isNull(timeDelta) ? std::wstring() : + replaceCpy(_("%x/sec"), L"%x", formatFilesizeShort(numeric::round(bytesProcessed / timeDelta))); + const wxString overallItemsPerSecond = numeric::isNull(timeDelta) ? std::wstring() : + replaceCpy(_("%x/sec"), L"%x", replaceCpy(_("%x items"), L"%x", formatThreeDigitPrecision(itemsProcessed / timeDelta))); pnl_.m_panelGraphBytes->setAttributes(pnl_.m_panelGraphBytes->getAttributes().setCornerText(overallBytesPerSecond, Graph2D::CORNER_TOP_LEFT)); pnl_.m_panelGraphItems->setAttributes(pnl_.m_panelGraphItems->getAttributes().setCornerText(overallItemsPerSecond, Graph2D::CORNER_TOP_LEFT)); diff --git a/FreeFileSync/Source/ui/search_grid.cpp b/FreeFileSync/Source/ui/search_grid.cpp index 50ee3ab0..cd03aa8d 100755 --- a/FreeFileSync/Source/ui/search_grid.cpp +++ b/FreeFileSync/Source/ui/search_grid.cpp @@ -69,7 +69,7 @@ ptrdiff_t findRow(const Grid& grid, //return -1 if no matching row found if (auto prov = grid.getDataProvider()) { std::vector<Grid::ColAttributes> colAttr = grid.getColumnConfig(); - erase_if(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; }); + eraseIf(colAttr, [](const Grid::ColAttributes& ca) { return !ca.visible; }); if (!colAttr.empty()) { const MatchFound<respectCase> matchFound(searchString); diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index 7b71fe1e..f9c5f704 100755 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -28,7 +28,7 @@ #include "../base/algorithm.h" #include "../base/synchronization.h" #include "../base/help_provider.h" -#include "../base/hard_filter.h" +#include "../base/path_filter.h" #include "../base/status_handler.h" //updateUiIsAllowed() #include "../base/generate_logfile.h" #include "../base/icon_buffer.h" @@ -175,8 +175,8 @@ class CopyToDialog : public CopyToDlgGenerated { public: CopyToDialog(wxWindow* parent, - const std::vector<const FileSystemObject*>& rowsOnLeft, - const std::vector<const FileSystemObject*>& rowsOnRight, + std::span<const FileSystemObject* const> rowsOnLeft, + std::span<const FileSystemObject* const> rowsOnRight, Zstring& lastUsedPath, const std::shared_ptr<FolderHistory>& folderHistory, bool& keepRelPaths, @@ -200,8 +200,8 @@ private: CopyToDialog::CopyToDialog(wxWindow* parent, - const std::vector<const FileSystemObject*>& rowsOnLeft, - const std::vector<const FileSystemObject*>& rowsOnRight, + std::span<const FileSystemObject* const> rowsOnLeft, + std::span<const FileSystemObject* const> rowsOnRight, Zstring& lastUsedPath, const std::shared_ptr<FolderHistory>& folderHistory, bool& keepRelPaths, @@ -291,8 +291,8 @@ void CopyToDialog::OnOK(wxCommandEvent& event) ReturnSmallDlg::ButtonPressed fff::showCopyToDialog(wxWindow* parent, - const std::vector<const FileSystemObject*>& rowsOnLeft, - const std::vector<const FileSystemObject*>& rowsOnRight, + std::span<const FileSystemObject* const> rowsOnLeft, + std::span<const FileSystemObject* const> rowsOnRight, Zstring& lastUsedPath, std::vector<Zstring>& folderPathHistory, size_t historySizeMax, @@ -315,8 +315,8 @@ class DeleteDialog : public DeleteDlgGenerated { public: DeleteDialog(wxWindow* parent, - const std::vector<const FileSystemObject*>& rowsOnLeft, - const std::vector<const FileSystemObject*>& rowsOnRight, + std::span<const FileSystemObject* const> rowsOnLeft, + std::span<const FileSystemObject* const> rowsOnRight, bool& useRecycleBin); private: @@ -329,8 +329,8 @@ private: void updateGui(); - const std::vector<const FileSystemObject*>& rowsToDeleteOnLeft_; - const std::vector<const FileSystemObject*>& rowsToDeleteOnRight_; + const std::span<const FileSystemObject* const> rowsToDeleteOnLeft_; + const std::span<const FileSystemObject* const> rowsToDeleteOnRight_; const std::chrono::steady_clock::time_point dlgStartTime_ = std::chrono::steady_clock::now(); //output-only parameters: @@ -339,8 +339,8 @@ private: DeleteDialog::DeleteDialog(wxWindow* parent, - const std::vector<const FileSystemObject*>& rowsOnLeft, - const std::vector<const FileSystemObject*>& rowsOnRight, + std::span<const FileSystemObject* const> rowsOnLeft, + std::span<const FileSystemObject* const> rowsOnRight, bool& useRecycleBin) : DeleteDlgGenerated(parent), rowsToDeleteOnLeft_(rowsOnLeft), @@ -424,8 +424,8 @@ void DeleteDialog::OnOK(wxCommandEvent& event) ReturnSmallDlg::ButtonPressed fff::showDeleteDialog(wxWindow* parent, - const std::vector<const FileSystemObject*>& rowsOnLeft, - const std::vector<const FileSystemObject*>& rowsOnRight, + std::span<const FileSystemObject* const> rowsOnLeft, + std::span<const FileSystemObject* const> rowsOnRight, bool& useRecycleBin) { DeleteDialog confirmDeletion(parent, rowsOnLeft, rowsOnRight, useRecycleBin); diff --git a/FreeFileSync/Source/ui/small_dlgs.h b/FreeFileSync/Source/ui/small_dlgs.h index c5b806ce..c03415f1 100755 --- a/FreeFileSync/Source/ui/small_dlgs.h +++ b/FreeFileSync/Source/ui/small_dlgs.h @@ -28,8 +28,8 @@ struct ReturnSmallDlg void showAboutDialog(wxWindow* parent); ReturnSmallDlg::ButtonPressed showCopyToDialog(wxWindow* parent, - const std::vector<const FileSystemObject*>& rowsOnLeft, - const std::vector<const FileSystemObject*>& rowsOnRight, + std::span<const FileSystemObject* const> rowsOnLeft, + std::span<const FileSystemObject* const> rowsOnRight, Zstring& lastUsedPath, std::vector<Zstring>& folderPathHistory, size_t historySizeMax, @@ -37,8 +37,8 @@ ReturnSmallDlg::ButtonPressed showCopyToDialog(wxWindow* parent, bool& overwriteIfExists); ReturnSmallDlg::ButtonPressed showDeleteDialog(wxWindow* parent, - const std::vector<const FileSystemObject*>& rowsOnLeft, - const std::vector<const FileSystemObject*>& rowsOnRight, + std::span<const FileSystemObject* const> rowsOnLeft, + std::span<const FileSystemObject* const> rowsOnRight, bool& useRecycleBin); ReturnSmallDlg::ButtonPressed showSyncConfirmationDlg(wxWindow* parent, diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp index e3b6a7bc..728c9bbe 100755 --- a/FreeFileSync/Source/ui/sync_cfg.cpp +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -90,8 +90,8 @@ private: CompareVariant localCmpVar_ = CompareVariant::TIME_SIZE; - std::set<AbstractPath> devicePathsForEdit_; //helper data for deviceParallelOps - std::map<AbstractPath, size_t> deviceParallelOps_; // + std::set<AfsDevice> devicesForEdit_; //helper data for deviceParallelOps + std::map<AfsDevice, size_t> deviceParallelOps_; // //------------- filter panel -------------------------- void OnHelpShowExamples(wxHyperlinkEvent& event) override { displayHelpEntry(L"exclude-items", this); } @@ -306,7 +306,7 @@ commandHistItemsMax_(commandHistItemsMax) m_staticTextCompVarDescription->SetMinSize(wxSize(fastFromDIP(CFG_DESCRIPTION_WIDTH_DIP), -1)); - m_scrolledWindowPerf->SetMinSize(wxSize(fastFromDIP(200), -1)); + m_scrolledWindowPerf->SetMinSize(wxSize(fastFromDIP(220), -1)); m_bitmapPerf->SetBitmap(perfPanelActive_ ? getResourceImage(L"speed") : greyScale(getResourceImage(L"speed"))); m_panelPerfHeader ->Enable(perfPanelActive_); m_staticTextPerfParallelOps->Enable(perfPanelActive_); @@ -1170,12 +1170,12 @@ MiscSyncConfig ConfigDialog::getMiscSyncOptions() const // - don't touch items corresponding to paths not currently used // - don't store parallel ops == 1 miscCfg.deviceParallelOps = deviceParallelOps_; - assert(fgSizerPerf->GetItemCount() == 2 * devicePathsForEdit_.size()); + assert(fgSizerPerf->GetItemCount() == 2 * devicesForEdit_.size()); int i = 0; - for (const AbstractPath& devPath : devicePathsForEdit_) + for (const AfsDevice& afsDevice : devicesForEdit_) { wxSpinCtrl* spinCtrlParallelOps = dynamic_cast<wxSpinCtrl*>(fgSizerPerf->GetItem(i * 2)->GetWindow()); - setDeviceParallelOps(miscCfg.deviceParallelOps, devPath, spinCtrlParallelOps->GetValue()); + setDeviceParallelOps(miscCfg.deviceParallelOps, afsDevice, spinCtrlParallelOps->GetValue()); ++i; } //---------------------------------------------------------------------------- @@ -1204,7 +1204,7 @@ void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg) deviceParallelOps_ = miscCfg.deviceParallelOps; assert(fgSizerPerf->GetItemCount() % 2 == 0); - const int rowsToCreate = static_cast<int>(devicePathsForEdit_.size()) - static_cast<int>(fgSizerPerf->GetItemCount() / 2); + const int rowsToCreate = static_cast<int>(devicesForEdit_.size()) - static_cast<int>(fgSizerPerf->GetItemCount() / 2); if (rowsToCreate >= 0) for (int i = 0; i < rowsToCreate; ++i) { @@ -1220,16 +1220,16 @@ void ConfigDialog::setMiscSyncOptions(const MiscSyncConfig& miscCfg) else for (int i = 0; i < -rowsToCreate * 2; ++i) fgSizerPerf->GetItem(size_t(0))->GetWindow()->Destroy(); - assert(fgSizerPerf->GetItemCount() == 2 * devicePathsForEdit_.size()); + assert(fgSizerPerf->GetItemCount() == 2 * devicesForEdit_.size()); int i = 0; - for (const AbstractPath& devPath : devicePathsForEdit_) + for (const AfsDevice& afsDevice : devicesForEdit_) { wxSpinCtrl* spinCtrlParallelOps = dynamic_cast<wxSpinCtrl*> (fgSizerPerf->GetItem(i * 2 )->GetWindow()); wxStaticText* staticTextDevice = dynamic_cast<wxStaticText*>(fgSizerPerf->GetItem(i * 2 + 1)->GetWindow()); - spinCtrlParallelOps->SetValue(static_cast<int>(getDeviceParallelOps(deviceParallelOps_, devPath))); - staticTextDevice->SetLabel(AFS::getDisplayPath(devPath)); + spinCtrlParallelOps->SetValue(static_cast<int>(getDeviceParallelOps(deviceParallelOps_, afsDevice))); + staticTextDevice->SetLabel(AFS::getDisplayPath(AbstractPath(afsDevice, AfsPath()))); ++i; } m_panelComparisonSettings->Layout(); //*after* setting text labels @@ -1277,7 +1277,7 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow) { assert(selectedPairIndexToShow_ == EMPTY_PAIR_INDEX_SELECTED); assert(newPairIndexToShow == -1 || makeUnsigned(newPairIndexToShow) < localPairCfg_.size()); - numeric::clamp(newPairIndexToShow, -1, static_cast<int>(localPairCfg_.size()) - 1); + newPairIndexToShow = std::clamp(newPairIndexToShow, -1, static_cast<int>(localPairCfg_.size()) - 1); selectedPairIndexToShow_ = newPairIndexToShow; m_listBoxFolderPair->SetSelection(newPairIndexToShow + 1); @@ -1314,12 +1314,12 @@ void ConfigDialog::selectFolderPairConfig(int newPairIndexToShow) //update the devices list for "parallel file operations" before calling setMiscSyncOptions(): // => should be enough to do this when selecting the main config // => to be "perfect" we'd have to update already when the user drags & drops a different versioning folder - devicePathsForEdit_.clear(); + devicesForEdit_.clear(); auto addDevicePath = [&](const Zstring& folderPathPhrase) { - const AbstractPath rootPath = AFS::getRootPath(createAbstractPath(folderPathPhrase)); - if (!AFS::isNullPath(rootPath)) - devicePathsForEdit_.insert(rootPath); + const AfsDevice& afsDevice = createAbstractPath(folderPathPhrase).afsDevice; + if (!AFS::isNullDevice(afsDevice)) + devicesForEdit_.insert(afsDevice); }; for (const LocalPairConfig& fpCfg : localPairCfg_) { diff --git a/FreeFileSync/Source/ui/sync_cfg.h b/FreeFileSync/Source/ui/sync_cfg.h index e660c3a0..34c4acf2 100755 --- a/FreeFileSync/Source/ui/sync_cfg.h +++ b/FreeFileSync/Source/ui/sync_cfg.h @@ -31,7 +31,7 @@ enum class SyncConfigPanel struct MiscSyncConfig { - std::map<AbstractPath, size_t> deviceParallelOps; + std::map<AfsDevice, size_t> deviceParallelOps; bool ignoreErrors = false; size_t automaticRetryCount = 0; std::chrono::seconds automaticRetryDelay{0}; diff --git a/FreeFileSync/Source/ui/tray_icon.cpp b/FreeFileSync/Source/ui/tray_icon.cpp index 4508f7a6..22d91f66 100755 --- a/FreeFileSync/Source/ui/tray_icon.cpp +++ b/FreeFileSync/Source/ui/tray_icon.cpp @@ -80,7 +80,7 @@ wxIcon FfsTrayIcon::ProgressIconGenerator::get(double fraction) return wxIcon(); const int pixelCount = logo_.GetWidth() * logo_.GetHeight(); - const int startFillPixel = numeric::clampCpy<int>(numeric::round(fraction * pixelCount), 0, pixelCount); + const int startFillPixel = std::clamp<int>(numeric::round(fraction * pixelCount), 0, pixelCount); if (startPixBuf_ != startFillPixel) { diff --git a/FreeFileSync/Source/ui/tree_grid.cpp b/FreeFileSync/Source/ui/tree_grid.cpp index bf3833b5..8d5b71a1 100755 --- a/FreeFileSync/Source/ui/tree_grid.cpp +++ b/FreeFileSync/Source/ui/tree_grid.cpp @@ -600,7 +600,7 @@ void TreeView::setData(FolderComparison& newData) folderCmp_ = newData; //remove truly empty folder pairs as early as this: we want to distinguish single/multiple folder pair cases by looking at "folderCmp" - erase_if(folderCmp_, [](const std::shared_ptr<BaseFolderPair>& baseObj) + eraseIf(folderCmp_, [](const std::shared_ptr<BaseFolderPair>& baseObj) { return AFS::isNullPath(baseObj->getAbstractPath< LEFT_SIDE>()) && AFS::isNullPath(baseObj->getAbstractPath<RIGHT_SIDE>()); diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index 0a978b6c..0074ae18 100755 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,7 @@ namespace fff { -const char ffsVersion[] = "10.5"; //internal linkage! +const char ffsVersion[] = "10.6"; //internal linkage! const char FFS_VERSION_SEPARATOR = '.'; } diff --git a/wx+/async_task.h b/wx+/async_task.h index df4c3ec6..074f5337 100755 --- a/wx+/async_task.h +++ b/wx+/async_task.h @@ -83,9 +83,9 @@ public: inRecursion_ = true; ZEN_ON_SCOPE_EXIT(inRecursion_ = false); - std::vector<std::unique_ptr<Task>> readyTasks; //Reentrancy; access to AsyncTasks::add is not protected! => evaluate outside erase_if + std::vector<std::unique_ptr<Task>> readyTasks; //Reentrancy; access to AsyncTasks::add is not protected! => evaluate outside eraseIf - erase_if(tasks_, [&](std::unique_ptr<Task>& task) + eraseIf(tasks_, [&](std::unique_ptr<Task>& task) { if (task->resultReady()) { diff --git a/wx+/graph.cpp b/wx+/graph.cpp index e00bed86..9cacd1bf 100755 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -91,7 +91,7 @@ public: int realToScreenRound(double realPos) const //returns -1 and screenSize + 1 if out of bounds! { //catch large double values: if double is larger than what int can represent => undefined behavior! - numeric::clamp(realPos, outOfBoundsLow_, outOfBoundsHigh_); + realPos = std::clamp(realPos, outOfBoundsLow_, outOfBoundsHigh_); return numeric::round(realToScreen(realPos)); } @@ -724,10 +724,10 @@ void Graph2D::render(wxDC& dc) const const wxPoint screenCurrent = activeSel_->refCurrentPos() - graphAreaOrigin; //normalize positions: a mouse selection is symmetric and *not* an half-open range! - double screenFromX = numeric::clampCpy(screenStart .x, 0, graphArea.width - 1); - double screenFromY = numeric::clampCpy(screenStart .y, 0, graphArea.height - 1); - double screenToX = numeric::clampCpy(screenCurrent.x, 0, graphArea.width - 1); - double screenToY = numeric::clampCpy(screenCurrent.y, 0, graphArea.height - 1); + double screenFromX = std::clamp(screenStart .x, 0, graphArea.width - 1); + double screenFromY = std::clamp(screenStart .y, 0, graphArea.height - 1); + double screenToX = std::clamp(screenCurrent.x, 0, graphArea.width - 1); + double screenToY = std::clamp(screenCurrent.y, 0, graphArea.height - 1); widen(&screenFromX, &screenToX); //use full pixel range for selection! widen(&screenFromY, &screenToY); @@ -782,10 +782,10 @@ void Graph2D::render(wxDC& dc) const shrink(&screenFromX, &screenToX); shrink(&screenFromY, &screenToY); - numeric::clamp(screenFromX, 0.0, graphArea.width - 1.0); - numeric::clamp(screenFromY, 0.0, graphArea.height - 1.0); - numeric::clamp(screenToX, 0.0, graphArea.width - 1.0); - numeric::clamp(screenToY, 0.0, graphArea.height - 1.0); + screenFromX = std::clamp(screenFromX, 0.0, graphArea.width - 1.0); + screenFromY = std::clamp(screenFromY, 0.0, graphArea.height - 1.0); + screenToX = std::clamp(screenToX, 0.0, graphArea.width - 1.0); + screenToY = std::clamp(screenToY, 0.0, graphArea.height - 1.0); const wxPoint pixelFrom = wxPoint(numeric::round(screenFromX), numeric::round(screenFromY)) + graphAreaOrigin; diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 65311ffa..3c19c246 100755 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -171,7 +171,6 @@ wxSize GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& if (high > 1) for (;;) { - const size_t middle = (low + high) / 2; //=> never 0 when "high - low > 1" if (high - low <= 1) { if (low == 0) @@ -181,6 +180,7 @@ wxSize GridData::drawCellText(wxDC& dc, const wxRect& rect, const std::wstring& } break; } + const size_t middle = (low + high) / 2; //=> never 0 when "high - low > 1" const std::wstring& candidate = getUnicodeSubstring(text, 0, middle) + ELLIPSIS; const wxSize extentCand = dc.GetTextExtent(candidate); //perf: most expensive call of this routine! @@ -1193,7 +1193,7 @@ private: //select current row *after* scrolling wxPoint clientPosTrimmed = clientPos; - numeric::clamp(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window! + clientPosTrimmed.y = std::clamp(clientPosTrimmed.y, 0, clientSize.GetHeight() - 1); //do not select row outside client window! const wxPoint absPos = wnd_.refParent().CalcUnscrolledPosition(clientPosTrimmed); const ptrdiff_t newRow = wnd_.rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range @@ -1357,8 +1357,8 @@ void Grid::updateWindowSizes(bool updateScrollbar) { ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y; ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ; - numeric::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1); - numeric::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1); + yFrom = std::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1); + yTo = std::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1); const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom); const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo); @@ -1465,8 +1465,8 @@ wxSize Grid::GetSizeAvailableForScrollTarget(const wxSize& size) { ptrdiff_t yFrom = CalcUnscrolledPosition(wxPoint(0, 0)).y; ptrdiff_t yTo = CalcUnscrolledPosition(wxPoint(0, mainWinHeightGross - 1)).y ; - numeric::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1); - numeric::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1); + yFrom = std::clamp<ptrdiff_t>(yFrom, 0, logicalHeight - 1); + yTo = std::clamp<ptrdiff_t>(yTo, 0, logicalHeight - 1); const ptrdiff_t rowFrom = rowLabelWin_->getRowAtPos(yFrom); const ptrdiff_t rowTo = rowLabelWin_->getRowAtPos(yTo); @@ -1499,7 +1499,7 @@ void Grid::onKeyDown(wxKeyEvent& event) { if (rowCount > 0) { - numeric::clamp<ptrdiff_t>(row, 0, rowCount - 1); + row = std::clamp<ptrdiff_t>(row, 0, rowCount - 1); setGridCursor(row, GridEventPolicy::ALLOW); } }; @@ -1508,7 +1508,7 @@ void Grid::onKeyDown(wxKeyEvent& event) { if (rowCount > 0) { - numeric::clamp<ptrdiff_t>(row, 0, rowCount - 1); + row = std::clamp<ptrdiff_t>(row, 0, rowCount - 1); selectWithCursor(row); //emits GridSelectEvent } }; @@ -2018,8 +2018,8 @@ void Grid::selectRange(ptrdiff_t rowFrom, ptrdiff_t rowTo, bool positive, const auto rowLast = std::max(rowFrom, rowTo) + 1; const size_t rowCount = getRowCount(); - numeric::clamp<ptrdiff_t>(rowFirst, 0, rowCount); - numeric::clamp<ptrdiff_t>(rowLast, 0, rowCount); + rowFirst = std::clamp<ptrdiff_t>(rowFirst, 0, rowCount); + rowLast = std::clamp<ptrdiff_t>(rowLast, 0, rowCount); selection_.selectRange(rowFirst, rowLast, positive); mainWin_->Refresh(); @@ -271,8 +271,8 @@ private: { if (rowFirst <= rowLast) { - numeric::clamp<size_t>(rowFirst, 0, selected_.size()); - numeric::clamp<size_t>(rowLast, 0, selected_.size()); + rowFirst = std::clamp<size_t>(rowFirst, 0, selected_.size()); + rowLast = std::clamp<size_t>(rowLast, 0, selected_.size()); std::fill(selected_.begin() + rowFirst, selected_.begin() + rowLast, positive); } diff --git a/wx+/image_holder.h b/wx+/image_holder.h index aada581f..b11ae451 100755 --- a/wx+/image_holder.h +++ b/wx+/image_holder.h @@ -39,9 +39,9 @@ struct ImageHolder //prepare conversion to wxImage as much as possible while sta unsigned char* releaseRgb () { return rgb_ .release(); } unsigned char* releaseAlpha() { return alpha_.release(); } +private: struct CLibFree { void operator()(unsigned char* p) const { ::free(p); } }; //use malloc/free to allow direct move into wxImage! -private: int width_ = 0; int height_ = 0; std::unique_ptr<unsigned char, CLibFree> rgb_; //optional diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index d09a188b..5bc8006f 100755 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -214,7 +214,7 @@ void GlobalBitmaps::init(const Zstring& filePath) //do NOT rely on wxConvLocal! On failure shows unhelpful popup "Cannot convert from the charset 'Unknown encoding (-1)'!" //do we need xBRZ scaling for high quality DPI images? - const int hqScale = numeric::clampCpy<int>(std::ceil(fastFromDIP(1000) / 1000.0), 1, xbrz::SCALE_FACTOR_MAX); + const int hqScale = std::clamp<int>(std::ceil(fastFromDIP(1000) / 1000.0), 1, xbrz::SCALE_FACTOR_MAX); //even for 125% DPI scaling, "2xBRZ + bilinear downscale" gives a better result than mere "125% bilinear upscale"! if (hqScale > 1) dpiScaler_ = std::make_unique<DpiParallelScaler>(hqScale); diff --git a/zen/basic_math.h b/zen/basic_math.h index 0d08f6a6..75f5d3b8 100755 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -21,14 +21,8 @@ namespace numeric template <class T> T abs(T value); template <class T> auto dist(T a, T b); template <class T> int sign(T value); //returns one of {-1, 0, 1} -template <class T> T min(T a, T b, T c); -template <class T> T max(T a, T b, T c); template <class T> bool isNull(T value); -template <class T> void clamp(T& val, T minVal, T maxVal); //make sure minVal <= val && val <= maxVal -template <class T> T clampCpy(T val, T minVal, T maxVal); -//std::clamp() available with C++17 - template <class T, class InputIterator> //precondition: range must be sorted! auto nearMatch(const T& val, InputIterator first, InputIterator last); @@ -106,51 +100,6 @@ int sign(T value) //returns one of {-1, 0, 1} return value < 0 ? -1 : (value > 0 ? 1 : 0); } - -template <class T> inline -T min(T a, T b, T c) //don't follow std::min's "const T&(const T&, const T&)" API -{ - if (a < b) - return a < c ? a : c; - else - return b < c ? b : c; - //return std::min(std::min(a, b), c); -} - - -template <class T> inline -T max(T a, T b, T c) -{ - if (a > b) - return a > c ? a : c; - else - return b > c ? b : c; - //return std::max(std::max(a, b), c); -} - - -template <class T> inline -T clampCpy(T val, T minVal, T maxVal) -{ - assert(minVal <= maxVal); - if (val < minVal) - return minVal; - else if (val > maxVal) - return maxVal; - return val; -} - -template <class T> inline -void clamp(T& val, T minVal, T maxVal) -{ - assert(minVal <= maxVal); - if (val < minVal) - val = minVal; - else if (val > maxVal) - val = maxVal; -} - - /* part of C++11 now! template <class InputIterator, class Compare> inline diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 2bb3fd26..f5ed0488 100755 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -25,7 +25,7 @@ using namespace zen; struct DirWatcher::Impl { int notifDescr = 0; - std::map<int, Zstring> watchDescrs; //watch descriptor and (sub-)directory name (postfixed with separator) -> owned by "notifDescr" + std::map<int, Zstring> watchedPaths; //watch descriptor and (sub-)directory paths -> owned by "notifDescr" }; @@ -90,7 +90,7 @@ DirWatcher::DirWatcher(const Zstring& dirPath) : //throw FileError throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtPath(subDirPath)), formatSystemError(L"inotify_add_watch", ec)); } - pimpl_->watchDescrs.emplace(wd, appendSeparator(subDirPath)); + pimpl_->watchedPaths.emplace(wd, subDirPath); } } @@ -130,12 +130,12 @@ std::vector<DirWatcher::Entry> DirWatcher::getChanges(const std::function<void() if (evt.len != 0) //exclude case: deletion of "self", already reported by parent directory watch { - auto it = pimpl_->watchDescrs.find(evt.wd); - if (it != pimpl_->watchDescrs.end()) + auto it = pimpl_->watchedPaths.find(evt.wd); + if (it != pimpl_->watchedPaths.end()) { //Note: evt.len is NOT the size of the evt.name c-string, but the array size including all padding 0 characters! //It may be even 0 in which case evt.name must not be used! - const Zstring itemPath = it->second + evt.name; + const Zstring itemPath = appendSeparator(it->second) + evt.name; if ((evt.mask & IN_CREATE) || (evt.mask & IN_MOVED_TO)) diff --git a/zen/file_access.cpp b/zen/file_access.cpp index 88b70b14..82c78760 100755 --- a/zen/file_access.cpp +++ b/zen/file_access.cpp @@ -50,32 +50,32 @@ std::optional<PathComponents> zen::parsePathComponents(const Zstring& itemPath) return {}; }; - if (startsWith(itemPath, "/")) - { - if (startsWith(itemPath, "/media/")) - { - //Ubuntu: e.g. /media/zenju/DEVICE_NAME - if (const char* username = ::getenv("USER")) - if (startsWith(itemPath, std::string("/media/") + username + "/")) - return doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/); + std::optional<PathComponents> pc; //"/media/zenju/" and "/Volumes/" should not fail to parse - //Ubuntu: e.g. /media/cdrom0 - return doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - } + if (!pc && startsWith(itemPath, "/mnt/")) //e.g. /mnt/DEVICE_NAME + pc = doParse(3 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - if (startsWith(itemPath, "/run/media/")) //Suse: e.g. /run/media/zenju/DEVICE_NAME - if (const char* username = ::getenv("USER")) - if (startsWith(itemPath, std::string("/run/media/") + username + "/")) - return doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/); + if (!pc && startsWith(itemPath, "/media/")) //Ubuntu: e.g. /media/zenju/DEVICE_NAME + if (const char* username = ::getenv("USER")) + if (startsWith(itemPath, std::string("/media/") + username + "/")) + pc = doParse(4 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - return doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/); - } + if (!pc && startsWith(itemPath, "/run/media/")) //Centos, Suse: e.g. /run/media/zenju/DEVICE_NAME + if (const char* username = ::getenv("USER")) + if (startsWith(itemPath, std::string("/run/media/") + username + "/")) + pc = doParse(5 /*sepCountVolumeRoot*/, false /*rootWithSep*/); - //we do NOT support relative paths! - return {}; -} + if (!pc && startsWith(itemPath, "/run/user/")) //Ubuntu, e.g.: /run/user/1000/gvfs/smb-share:server=192.168.62.145,share=folder + if (startsWith(itemPath, "/run/user/" + numberTo<std::string>(::getuid()) + "/gvfs/")) //::getuid() never fails + pc = doParse(6 /*sepCountVolumeRoot*/, false /*rootWithSep*/); + if (!pc && startsWith(itemPath, "/")) + pc = doParse(1 /*sepCountVolumeRoot*/, true /*rootWithSep*/); + + return pc; +} + std::optional<Zstring> zen::getParentFolderPath(const Zstring& itemPath) { @@ -108,49 +108,40 @@ ItemType zen::getItemType(const Zstring& itemPath) //throw FileError } -PathStatus zen::getPathStatus(const Zstring& itemPath) //throw FileError +std::optional<ItemType> zen::itemStillExists(const Zstring& itemPath) //throw FileError { - const std::optional<Zstring> parentPath = getParentFolderPath(itemPath); try { - return { getItemType(itemPath), itemPath, {} }; //throw FileError + return getItemType(itemPath); //throw FileError } - catch (FileError&) + catch (const FileError& e) //not existing or access error { + const std::optional<Zstring> parentPath = getParentFolderPath(itemPath); if (!parentPath) //device root throw; //else: let's dig deeper... don't bother checking Win32 codes; e.g. not existing item may have the codes: // ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_INVALID_NAME, ERROR_INVALID_DRIVE, // ERROR_NOT_READY, ERROR_INVALID_PARAMETER, ERROR_BAD_PATHNAME, ERROR_BAD_NETPATH => not reliable - } - const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); - assert(!itemName.empty()); - - PathStatus ps = getPathStatus(*parentPath); //throw FileError - if (ps.relPath.empty() && - ps.existingType != ItemType::FILE) //obscure, but possible (and not an error) - try - { - traverseFolder(*parentPath, - [&](const FileInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FILE; }, - [&](const FolderInfo& fi) { if (equalFilePath(fi.itemName, itemName)) throw ItemType::FOLDER; }, - [&](const SymlinkInfo& si) { if (equalFilePath(si.itemName, itemName)) throw ItemType::SYMLINK; }, - [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); - } - catch (const ItemType& type) { return { type, itemPath, {} }; } //yes, exceptions for control-flow are bad design... but, but... - //we're not CPU-bound here and finding the item after getItemType() previously failed is exceptional (even C:\pagefile.sys should be found) - - ps.relPath.push_back(itemName); - return ps; -} + const Zstring itemName = afterLast(itemPath, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_ALL); + assert(!itemName.empty()); -std::optional<ItemType> zen::getItemTypeIfExists(const Zstring& itemPath) //throw FileError -{ - const PathStatus ps = getPathStatus(itemPath); //throw FileError - if (ps.relPath.empty()) - return ps.existingType; - return {}; + const std::optional<ItemType> parentType = itemStillExists(*parentPath); //throw FileError + if (parentType && *parentType != ItemType::FILE /*obscure, but possible (and not an error)*/) + try + { + traverseFolder(*parentPath, + [&](const FileInfo& fi) { if (fi.itemName == itemName) throw ItemType::FILE; }, + [&](const FolderInfo& fi) { if (fi.itemName == itemName) throw ItemType::FOLDER; }, + [&](const SymlinkInfo& si) { if (si.itemName == itemName) throw ItemType::SYMLINK; }, + [](const std::wstring& errorMsg) { throw FileError(errorMsg); }); + } + catch (const ItemType&) //finding the item after getItemType() previously failed is exceptional + { + throw e; //yes, slicing + } + return {}; + } } @@ -174,16 +165,6 @@ bool zen::dirAvailable(const Zstring& dirPath) //noexcept } -bool zen::itemNotExisting(const Zstring& itemPath) -{ - try - { - return !getItemTypeIfExists(itemPath); //throw FileError - } - catch (FileError&) { return false; } -} - - namespace { } @@ -209,13 +190,13 @@ uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError, returns 0 } -VolumeId zen::getVolumeId(const Zstring& itemPath) //throw FileError +FileId /*optional*/ zen::getFileId(const Zstring& itemPath) //throw FileError { struct ::stat fileInfo = {}; if (::stat(itemPath.c_str(), &fileInfo) != 0) THROW_LAST_FILE_ERROR(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtPath(itemPath)), L"stat"); - return fileInfo.st_dev; + return extractFileId(fileInfo); } @@ -381,7 +362,7 @@ void zen::renameFile(const Zstring& pathSource, const Zstring& pathTarget) //thr const Zstring parentPathSrc = beforeLast(pathSource, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); const Zstring parentPathTrg = beforeLast(pathTarget, FILE_NAME_SEPARATOR, IF_MISSING_RETURN_NONE); //some (broken) devices may fail to rename case directly: - if (equalLocalPath(parentPathSrc, parentPathTrg)) + if (equalNativePath(parentPathSrc, parentPathTrg)) { if (fileNameSrc == fileNameTrg) return; //non-sensical request @@ -567,8 +548,18 @@ void zen::createDirectory(const Zstring& dirPath) //throw FileError, ErrorTarget void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw FileError { - if (!getParentFolderPath(dirPath)) //device root - return static_cast<void>(/*ItemType =*/ getItemType(dirPath)); //throw FileError + const std::optional<Zstring> parentPath = getParentFolderPath(dirPath); + if (!parentPath) //device root + return; + + try //generally we expect that path already exists (see: ffs_paths.cpp) => check first + { + if (getItemType(dirPath) != ItemType::FILE) //throw FileError + return; + } + catch (FileError&) {} //not yet existing or access error? let's find out... + + createDirectoryIfMissingRecursion(*parentPath); //throw FileError try { @@ -576,18 +567,15 @@ void zen::createDirectoryIfMissingRecursion(const Zstring& dirPath) //throw File } catch (FileError&) { - const PathStatus ps = getPathStatus(dirPath); //throw FileError - if (ps.existingType == ItemType::FILE) - throw; + try + { + if (getItemType(dirPath) != ItemType::FILE) //throw FileError + return; //already existing => possible, if createDirectoryIfMissingRecursion() is run in parallel + } + catch (FileError&) {} //not yet existing or access error + //catch (const FileError& e2) { throw FileError(e.toString(), e2.toString()); } -> details needed??? - //ps.relPath.size() == 1 => same createDirectory() call from above? Maybe parent folder was created by parallel thread shortly after failure! - Zstring intermediatePath = ps.existingPath; - for (const Zstring& itemName : ps.relPath) - try - { - createDirectory(intermediatePath = appendSeparator(intermediatePath) + itemName); //throw FileError, ErrorTargetExisting - } - catch (ErrorTargetExisting&) {} //possible, if createDirectoryIfMissingRecursion() is run in parallel + throw; } } diff --git a/zen/file_access.h b/zen/file_access.h index 916f23f5..514d798e 100755 --- a/zen/file_access.h +++ b/zen/file_access.h @@ -30,8 +30,6 @@ std::optional<Zstring> getParentFolderPath(const Zstring& itemPath); //POSITIVE existence checks; if false: 1. item not existing 2. different type 3.device access error or similar bool fileAvailable(const Zstring& filePath); //noexcept bool dirAvailable (const Zstring& dirPath ); // -//NEGATIVE existence checks; if false: 1. item existing 2.device access error or similar -bool itemNotExisting(const Zstring& itemPath); enum class ItemType { @@ -40,17 +38,12 @@ enum class ItemType SYMLINK, }; //(hopefully) fast: does not distinguish between error/not existing -ItemType getItemType (const Zstring& itemPath); //throw FileError +ItemType getItemType(const Zstring& itemPath); //throw FileError //execute potentially SLOW folder traversal but distinguish error/not existing -std::optional<ItemType> getItemTypeIfExists(const Zstring& itemPath); //throw FileError - -struct PathStatus -{ - ItemType existingType; - Zstring existingPath; //itemPath =: existingPath + relPath - std::vector<Zstring> relPath; // -}; -PathStatus getPathStatus(const Zstring& itemPath); //throw FileError +// assumes: - base path still exists +// - all child item path parts must correspond to folder traversal +// => we can conclude whether an item is *not* existing anymore by doing a *case-sensitive* name search => potentially SLOW! +std::optional<ItemType> itemStillExists(const Zstring& itemPath); //throw FileError enum class ProcSymlink { @@ -62,7 +55,8 @@ void setFileTime(const Zstring& filePath, time_t modTime, ProcSymlink procSl); / //symlink handling: always evaluate target uint64_t getFileSize(const Zstring& filePath); //throw FileError uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError, returns 0 if not available -VolumeId getVolumeId(const Zstring& itemPath); //throw FileError +FileId /*optional*/ getFileId(const Zstring& itemPath); //throw FileError + //get per-user directory designated for temporary files: Zstring getTempFolderPath(); //throw FileError diff --git a/zen/file_io.cpp b/zen/file_io.cpp index df47e4c5..80fb3153 100755 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -128,7 +128,7 @@ size_t FileInput::tryRead(void* buffer, size_t bytesToRead) //throw FileError, E return bytesRead; //"zero indicates end of file" } - + size_t FileInput::read(void* buffer, size_t bytesToRead) //throw FileError, ErrorFileLocked, X; return "bytesToRead" bytes unless end of stream! { /* diff --git a/zen/legacy_compiler.h b/zen/legacy_compiler.h index 16d87c53..bebf5a05 100755 --- a/zen/legacy_compiler.h +++ b/zen/legacy_compiler.h @@ -7,11 +7,47 @@ #ifndef LEGACY_COMPILER_H_839567308565656789 #define LEGACY_COMPILER_H_839567308565656789 +//#include <span> //requires C++20 + + namespace std { //https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html //https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations + + +//requires C++20! until then, this should suffice... +template <class T> +class span +{ +public: + template <class Iterator> + span(Iterator first, Iterator last) : size_(last - first), data_(first != last ? &*first : nullptr) {} + + template <class Container> + span(Container& cont) : span(cont.begin(), cont.end()) {} + + using iterator = T*; + using const_iterator = const T*; + + iterator begin() { return data_; } + iterator end () { return data_ + size_; } + + const_iterator begin() const { return data_; } + const_iterator end () const { return data_ + size_; } + + const_iterator cbegin() const { return begin(); } + const_iterator cend () const { return end (); } + + T* data() const { return data_; } + size_t size() const { return size_; } + bool empty() const { return size_ == 0; } + +private: + const size_t size_; + T* const data_; +}; } #endif //LEGACY_COMPILER_H_839567308565656789 diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 8b4389a7..8d34f262 100755 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -27,7 +27,7 @@ bool zen::recycleOrDeleteIfExists(const Zstring& itemPath) //throw FileError if (!::g_file_trash(file, nullptr, &error)) { - const std::optional<ItemType> type = getItemTypeIfExists(itemPath); //throw FileError + const std::optional<ItemType> type = itemStillExists(itemPath); //throw FileError if (!type) return false; diff --git a/zen/ring_buffer.h b/zen/ring_buffer.h index 232e17da..e3dbd55f 100755 --- a/zen/ring_buffer.h +++ b/zen/ring_buffer.h @@ -8,10 +8,7 @@ #define RING_BUFFER_H_01238467085684139453534 #include <cassert> -#include <vector> -#include <stdexcept> #include "scope_guard.h" -#include "string_tools.h" namespace zen @@ -40,13 +37,24 @@ public: ~RingBuffer() { clear(); } - reference front() { return getBufPtr()[bufStart_]; } - const_reference front() const { return getBufPtr()[bufStart_]; } + reference front() { checkInvariants(); assert(!empty()); return getBufPtr()[bufStart_]; } + const_reference front() const { checkInvariants(); assert(!empty()); return getBufPtr()[bufStart_]; } + + reference back() { checkInvariants(); assert(!empty()); return getBufPtr()[getBufPos(size_ - 1)]; } + const_reference back() const { checkInvariants(); assert(!empty()); return getBufPtr()[getBufPos(size_ - 1)]; } + + template <class U> + void push_front(U&& value) + { + reserve(size_ + 1); //throw ? + ::new (getBufPtr() + getBufPos(capacity_ - 1)) T(std::forward<U>(value)); //throw ? + ++size_; + bufStart_ = getBufPos(capacity_ - 1); + } template <class U> void push_back(U&& value) { - checkInvariants(); reserve(size_ + 1); //throw ? ::new (getBufPtr() + getBufPos(size_)) T(std::forward<U>(value)); //throw ? ++size_; @@ -54,15 +62,21 @@ public: void pop_front() { - checkInvariants(); - if (empty()) - throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); - front().~T(); - ++bufStart_; --size_; - if (size_ == 0 || bufStart_ == capacity_) + if (size_ == 0) + bufStart_ = 0; + else + bufStart_ = getBufPos(1); + } + + void pop_back() + { + back().~T(); + --size_; + + if (size_ == 0) bufStart_ = 0; } @@ -80,8 +94,6 @@ public: template <class Iterator> void insert_back(Iterator first, Iterator last) //throw ? (strong exception-safety!) { - checkInvariants(); - const size_t len = last - first; reserve(size_ + len); //throw ? @@ -100,10 +112,8 @@ public: void extract_front(Iterator first, Iterator last) //throw ? strongly exception-safe! (but only basic exception safety for [first, last) range) { checkInvariants(); - const size_t len = last - first; - if (size_ < len) - throw std::logic_error("Contract violation! " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__)); + assert(size_ >= len); const size_t frontSize = std::min(len, capacity_ - bufStart_); @@ -113,13 +123,12 @@ public: std::destroy(getBufPtr() + bufStart_, getBufPtr() + bufStart_ + frontSize); std::destroy(getBufPtr(), getBufPtr() + len - frontSize); - bufStart_ += len; - size_ -= len; + size_ -= len; if (size_ == 0) bufStart_ = 0; - else if (bufStart_ >= capacity_) - bufStart_ -= capacity_; + else + bufStart_ = getBufPos(len); } void swap(RingBuffer& other) @@ -132,6 +141,8 @@ public: void reserve(size_t minSize) //throw ? (strong exception-safety!) { + checkInvariants(); + if (minSize > capacity_) { const size_t newCapacity = std::max(minSize + minSize / 2, minSize); //no minimum capacity: just like std::vector<> implementation @@ -205,7 +216,7 @@ private: struct FreeStoreDelete { void operator()(std::byte* p) const { ::operator delete (p); } }; - T* getBufPtr() { return reinterpret_cast<T*>(rawMem_.get()); } + /**/ T* getBufPtr() { return reinterpret_cast<T*>(rawMem_.get()); } const T* getBufPtr() const { return reinterpret_cast<T*>(rawMem_.get()); } //unlike pure std::uninitialized_move, this one allows for strong exception-safety! diff --git a/zen/serialize.h b/zen/serialize.h index d34b61b2..8b4c58ea 100755 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -36,20 +36,20 @@ public: using iterator = std::vector<std::byte>::iterator; using const_iterator = std::vector<std::byte>::const_iterator; - iterator begin() { return buffer_->begin(); } - iterator end () { return buffer_->end (); } + iterator begin() { return buffer_.ref().begin(); } + iterator end () { return buffer_.ref().end (); } - const_iterator begin() const { return buffer_->begin(); } - const_iterator end () const { return buffer_->end (); } + const_iterator begin() const { return buffer_.ref().begin(); } + const_iterator end () const { return buffer_.ref().end (); } - void resize(size_t len) { buffer_->resize(len); } - size_t size() const { return buffer_->size(); } - bool empty() const { return buffer_->empty(); } + void resize(size_t len) { buffer_.ref().resize(len); } + size_t size() const { return buffer_.ref().size(); } + bool empty() const { return buffer_.ref().empty(); } - inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return *lhs.buffer_ == *rhs.buffer_; } + inline friend bool operator==(const ByteArray& lhs, const ByteArray& rhs) { return lhs.buffer_.ref() == rhs.buffer_.ref(); } private: - std::shared_ptr<std::vector<std::byte>> buffer_ = std::make_shared<std::vector<std::byte>>(); //always bound! + SharedRef<std::vector<std::byte>> buffer_ = makeSharedRef<std::vector<std::byte>>(); //perf: shared_ptr indirection irrelevant: less than 1% slower! }; diff --git a/zen/stl_tools.h b/zen/stl_tools.h index c3a9bf8f..d8bda888 100755 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -11,10 +11,11 @@ #include <map> #include <vector> #include <memory> +#include <cassert> #include <algorithm> #include <optional> #include "string_traits.h" -#include "build_info.h" +//#include "build_info.h" //enhancements for <algorithm> @@ -22,13 +23,13 @@ namespace zen { //erase selected elements from any container: template <class T, class Alloc, class Predicate> -void erase_if(std::vector<T, Alloc>& v, Predicate p); +void eraseIf(std::vector<T, Alloc>& v, Predicate p); template <class T, class LessType, class Alloc, class Predicate> -void erase_if(std::set<T, LessType, Alloc>& s, Predicate p); +void eraseIf(std::set<T, LessType, Alloc>& s, Predicate p); template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate> -void erase_if(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p); +void eraseIf(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p); //append STL containers template <class T, class Alloc, class C> @@ -48,14 +49,14 @@ void removeDuplicates(std::vector<T, Alloc>& v, CompLess less); //binary search returning an iterator template <class Iterator, class T, class CompLess> -Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less); +Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less); template <class BidirectionalIterator, class T> -BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value); +BidirectionalIterator findLast(BidirectionalIterator first, BidirectionalIterator last, const T& value); //replacement for std::find_end taking advantage of bidirectional iterators (and giving the algorithm a reasonable name) template <class BidirectionalIterator1, class BidirectionalIterator2> -BidirectionalIterator1 search_last(BidirectionalIterator1 first1, BidirectionalIterator1 last1, +BidirectionalIterator1 searchLast(BidirectionalIterator1 first1, BidirectionalIterator1 last1, BidirectionalIterator2 first2, BidirectionalIterator2 last2); template <class Num, class ByteIterator> Num hashBytes (ByteIterator first, ByteIterator last); @@ -74,17 +75,49 @@ struct StringHash }; -//why, oh wy is there no std::optional<T>::get()??? +//why, oh why is there no std::optional<T>::get()??? template <class T> inline T* get( std::optional<T>& opt) { return opt ? &*opt : nullptr; } template <class T> inline const T* get(const std::optional<T>& opt) { return opt ? &*opt : nullptr; } +//=========================================================================== +template <class T> class SharedRef; +template <class T, class... Args> SharedRef<T> makeSharedRef(Args&& ... args); + +template <class T> +class SharedRef //why is there no std::shared_ref??? +{ +public: + SharedRef() = delete; //no suprise memory allocations => always construct with makeSharedRef() + + template <class U> + SharedRef(const SharedRef<U>& other) : ref_(other.ref_) {} + + /**/ T& ref() { return *ref_; }; + const T& ref() const { return *ref_; }; + + std::shared_ptr<T> ptr() { return ref_; }; + +private: + explicit SharedRef(std::shared_ptr<T>&& ptr) : ref_(std::move(ptr)) { assert(ref_); } + + template <class U, class... Args> friend SharedRef<U> makeSharedRef(Args&& ... args); + template <class U> friend class SharedRef; + + std::shared_ptr<T> ref_; //always bound +}; + +template <class T, class... Args> inline +SharedRef<T> makeSharedRef(Args&&... args) { return SharedRef<T>(std::make_shared<T>(std::forward<Args>(args)...)); } +//=========================================================================== + + //######################## implementation ######################## template <class T, class Alloc, class Predicate> inline -void erase_if(std::vector<T, Alloc>& v, Predicate p) +void eraseIf(std::vector<T, Alloc>& v, Predicate p) { v.erase(std::remove_if(v.begin(), v.end(), p), v.end()); } @@ -93,7 +126,7 @@ void erase_if(std::vector<T, Alloc>& v, Predicate p) namespace impl { template <class S, class Predicate> inline -void set_or_map_erase_if(S& s, Predicate p) +void setOrMapEraseIf(S& s, Predicate p) { for (auto it = s.begin(); it != s.end();) if (p(*it)) @@ -105,11 +138,11 @@ void set_or_map_erase_if(S& s, Predicate p) template <class T, class LessType, class Alloc, class Predicate> inline -void erase_if(std::set<T, LessType, Alloc>& s, Predicate p) { impl::set_or_map_erase_if(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!! +void eraseIf(std::set<T, LessType, Alloc>& s, Predicate p) { impl::setOrMapEraseIf(s, p); } //don't make this any more generic! e.g. must not compile for std::vector!!! template <class KeyType, class ValueType, class LessType, class Alloc, class Predicate> inline -void erase_if(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p) { impl::set_or_map_erase_if(m, p); } +void eraseIf(std::map<KeyType, ValueType, LessType, Alloc>& m, Predicate p) { impl::setOrMapEraseIf(m, p); } template <class T, class Alloc, class C> inline @@ -147,7 +180,7 @@ void removeDuplicates(std::vector<T, Alloc>& v) template <class Iterator, class T, class CompLess> inline -Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess less) +Iterator binarySearch(Iterator first, Iterator last, const T& value, CompLess less) { static_assert(std::is_same_v<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>); @@ -160,7 +193,7 @@ Iterator binary_search(Iterator first, Iterator last, const T& value, CompLess l template <class BidirectionalIterator, class T> inline -BidirectionalIterator find_last(const BidirectionalIterator first, const BidirectionalIterator last, const T& value) +BidirectionalIterator findLast(const BidirectionalIterator first, const BidirectionalIterator last, const T& value) { for (BidirectionalIterator it = last; it != first;) //reverse iteration: 1. check 2. decrement 3. evaluate { @@ -174,7 +207,7 @@ BidirectionalIterator find_last(const BidirectionalIterator first, const Bidirec template <class BidirectionalIterator1, class BidirectionalIterator2> inline -BidirectionalIterator1 search_last(const BidirectionalIterator1 first1, BidirectionalIterator1 last1, +BidirectionalIterator1 searchLast(const BidirectionalIterator1 first1, BidirectionalIterator1 last1, const BidirectionalIterator2 first2, const BidirectionalIterator2 last2) { const BidirectionalIterator1 itNotFound = last1; diff --git a/zen/string_base.h b/zen/string_base.h index 9632eba4..91a6d5bd 100755 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -410,7 +410,7 @@ size_t Zbase<Char, SP>::rfind(Char ch, size_t pos) const assert(pos == npos || pos <= length()); const size_t len = length(); const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + 1, len)); - const Char* it = find_last(begin(), currEnd, ch); + const Char* it = findLast(begin(), currEnd, ch); return it == currEnd ? npos : it - begin(); } @@ -422,7 +422,7 @@ size_t Zbase<Char, SP>::rfind(const Char* str, size_t pos) const const size_t strLen = strLength(str); const size_t len = length(); const Char* currEnd = begin() + (pos == npos ? len : std::min(pos + strLen, len)); - const Char* it = search_last(begin(), currEnd, + const Char* it = searchLast(begin(), currEnd, str, str + strLen); return it == currEnd ? npos : it - begin(); } diff --git a/zen/string_tools.h b/zen/string_tools.h index 657c70d5..8579a460 100755 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -104,26 +104,19 @@ template <class T, class S> T copyStringTo(S&& str); //---------------------- implementation ---------------------- -template <> inline -bool isWhiteSpace(char c) -{ - assert(c != 0); //std C++ does not consider 0 as white space - //caveat 1: std::isspace() takes an int, but expects an unsigned char - //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC) - return static_cast<unsigned char>(c) < 128 && - std::isspace(static_cast<unsigned char>(c)) != 0; -} - -template <> inline -bool isWhiteSpace(wchar_t c) +template <class Char> inline +bool isWhiteSpace(Char c) { + static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>); assert(c != 0); //std C++ does not consider 0 as white space - return std::iswspace(c) != 0; + return c == static_cast<Char>(' ') || (static_cast<Char>('\t') <= c && c <= static_cast<Char>('\r')); + //following std::isspace() for default locale but without the interface insanity: + // - std::isspace() takes an int, but expects an unsigned char + // - some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC) } - template <class Char> inline -bool isDigit(Char c) //similar to implmenetation of std::isdigit()! +bool isDigit(Char c) //similar to implementation of std::isdigit()! { static_assert(std::is_same_v<Char, char> || std::is_same_v<Char, wchar_t>); return static_cast<Char>('0') <= c && c <= static_cast<Char>('9'); @@ -306,7 +299,7 @@ S afterLast(const S& str, const T& term, FailureReturnVal rv) const auto* const strLast = strFirst + strLength(str); const auto* const termFirst = strBegin(term); - const auto* it = search_last(strFirst, strLast, + const auto* it = searchLast(strFirst, strLast, termFirst, termFirst + termLen); if (it == strLast) return rv == IF_MISSING_RETURN_ALL ? str : S(); @@ -327,7 +320,7 @@ S beforeLast(const S& str, const T& term, FailureReturnVal rv) const auto* const strLast = strFirst + strLength(str); const auto* const termFirst = strBegin(term); - const auto* it = search_last(strFirst, strLast, + const auto* it = searchLast(strFirst, strLast, termFirst, termFirst + termLen); if (it == strLast) return rv == IF_MISSING_RETURN_ALL ? str : S(); @@ -725,7 +718,7 @@ Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to i number += c - static_cast<CharType>('0'); } else //rest of string should contain whitespace only, it's NOT a bug if there is something else! - break; //assert(std::all_of(iter, last, &isWhiteSpace<CharType>)); -> this is NO assert situation + break; //assert(std::all_of(iter, last, isWhiteSpace<CharType>)); -> this is NO assert situation } return number; } diff --git a/zen/string_traits.h b/zen/string_traits.h index cd7dbf1b..93cfd81c 100755 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -53,7 +53,7 @@ public: private: const size_t len_; - Char* str_; + Char* const str_; }; diff --git a/zen/thread.h b/zen/thread.h index 7f3d216c..809bc771 100755 --- a/zen/thread.h +++ b/zen/thread.h @@ -90,10 +90,10 @@ bool isReady(const std::future<T>& f) { return f.wait_for(std::chrono::seconds(0 //wait until first job is successful or all failed: substitute until std::when_any is available template <class T> -class GetFirstResult +class AsyncFirstResult { public: - GetFirstResult(); + AsyncFirstResult(); template <class Fun> void addJob(Fun&& f); //f must return a std::optional<T> containing a value if successful @@ -161,12 +161,15 @@ public: ThreadGroup& operator=(ThreadGroup&& tmp) noexcept { swap(tmp); return *this; } //noexcept *required* to support move for reallocations in std::vector and std::swap!!! //context of controlling OR worker thread, non-blocking: - void run(Function&& wi /*should throw ThreadInterruption when needed*/) + void run(Function&& wi /*should throw ThreadInterruption when needed*/, bool insertFront = false) { { std::lock_guard<std::mutex> dummy(workLoad_->lock); - workLoad_->tasks.push_back(std::move(wi)); + if (insertFront) + workLoad_->tasks.push_front(std::move(wi)); + else + workLoad_->tasks.push_back(std::move(wi)); const size_t tasksPending = ++(workLoad_->tasksPending); if (worker_.size() < std::min(tasksPending, threadCountMax_)) @@ -318,7 +321,7 @@ bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& template <class T> -class GetFirstResult<T>::AsyncResult +class AsyncFirstResult<T>::AsyncResult { public: //context: worker threads @@ -361,12 +364,12 @@ private: template <class T> inline -GetFirstResult<T>::GetFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {} +AsyncFirstResult<T>::AsyncFirstResult() : asyncResult_(std::make_shared<AsyncResult>()) {} template <class T> template <class Fun> inline -void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> containing a value on success +void AsyncFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> containing a value on success { std::thread t([asyncResult = this->asyncResult_, f = std::forward<Fun>(f)] { asyncResult->reportFinished(f()); }); ++jobsTotal_; @@ -376,11 +379,11 @@ void GetFirstResult<T>::addJob(Fun&& f) //f must return a std::optional<T> conta template <class T> template <class Duration> inline -bool GetFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); } +bool AsyncFirstResult<T>::timedWait(const Duration& duration) const { return asyncResult_->waitForResult(jobsTotal_, duration); } template <class T> inline -std::optional<T> GetFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); } +std::optional<T> AsyncFirstResult<T>::get() const { return asyncResult_->getResult(jobsTotal_); } //------------------------------------------------------------------------------------------ diff --git a/zen/zstring.cpp b/zen/zstring.cpp index 68609030..f8a34045 100755 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -77,22 +77,19 @@ Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const { if (oldTerm.empty()) return str; - - Zstring strU = str; - Zstring oldU = oldTerm; - - for (Zchar& c : strU) c = asciiToUpper(c); //can't use makeUpperCopy(): input/output sizes may differ! - for (Zchar& c : oldU) c = asciiToUpper(c); // Zstring output; for (size_t pos = 0;;) { - const size_t posFound = strU.find(oldU, pos); - if (posFound == Zstring::npos) + const size_t posFound = std::search(str.begin() + pos, str.end(), //can't use makeUpperCopy(): input/output sizes may differ! + oldTerm.begin(), oldTerm.end(), + [](Zchar charL, Zchar charR) { return asciiToUpper(charL) == asciiToUpper(charR); }) - str.begin(); + + if (posFound == str.size()) { if (pos == 0) //optimize "oldTerm not found": return ref-counted copy - return str; + return str; output.append(str.begin() + pos, str.end()); return output; } @@ -126,7 +123,7 @@ OS X (UTF8 char) ________________________ time per call | function */ -int compareLocalPath(const Zstring& lhs, const Zstring& rhs) +int compareNativePath(const Zstring& lhs, const Zstring& rhs) { assert(lhs.find(Zchar('\0')) == Zstring::npos); //don't expect embedded nulls! assert(rhs.find(Zchar('\0')) == Zstring::npos); // @@ -250,8 +247,3 @@ int compareNatural(const Zstring& lhs, const Zstring& rhs) } } - - -warn_static("clean up implementation of these two:") -//template <> inline bool isWhiteSpace(char c) -//template <> inline bool isWhiteSpace(wchar_t c) diff --git a/zen/zstring.h b/zen/zstring.h index 20cf968d..9fecdce3 100755 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -35,24 +35,18 @@ Zstring getUnicodeNormalForm(const Zstring& str); Zstring replaceCpyAsciiNoCase(const Zstring& str, const Zstring& oldTerm, const Zstring& newTerm); +struct LessUnicodeNormal { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return getUnicodeNormalForm(lhs) < getUnicodeNormalForm(rhs);} }; + //------------------------------------------------------------------------------------------ -//inline -//int compareNoCase(const Zstring& lhs, const Zstring& rhs) -//{ -// return zen::compareString(makeUpperCopy(lhs), makeUpperCopy(rhs)); -// //avoid eager optimization bugs: e.g. "if (isAsciiString()) compareAsciiNoCase()" might model a different order! -//} inline bool equalNoCase(const Zstring& lhs, const Zstring& rhs) { return makeUpperCopy(lhs) == makeUpperCopy(rhs); } struct ZstringNoCase //use as STL container key: avoid needless upper-case conversions during std::map<>::find() { - ZstringNoCase(const Zstring& str) : upperCase(makeUpperCopy(str)) {} - Zstring upperCase; + ZstringNoCase(const Zstring& str) : upperCase(makeUpperCopy(str)) {} + Zstring upperCase; }; -inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { return lhs.upperCase < rhs.upperCase; } - -//struct LessNoCase { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareNoCase(lhs, rhs) < 0; } }; +inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { return lhs.upperCase < rhs.upperCase; } //------------------------------------------------------------------------------------------ @@ -60,11 +54,11 @@ inline bool operator<(const ZstringNoCase& lhs, const ZstringNoCase& rhs) { retu // Windows: igore case // Linux: byte-wise comparison // macOS: igore case + Unicode normalization forms -int compareLocalPath(const Zstring& lhs, const Zstring& rhs); +int compareNativePath(const Zstring& lhs, const Zstring& rhs); -inline bool equalLocalPath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs) == 0; } +inline bool equalNativePath(const Zstring& lhs, const Zstring& rhs) { return compareNativePath(lhs, rhs) == 0; } -struct LessLocalPath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareLocalPath(lhs, rhs) < 0; } }; +struct LessNativePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareNativePath(lhs, rhs) < 0; } }; //------------------------------------------------------------------------------------------ int compareNatural(const Zstring& lhs, const Zstring& rhs); @@ -73,11 +67,7 @@ struct LessNaturalSort { bool operator()(const Zstring& lhs, const Zstring rhs) //------------------------------------------------------------------------------------------ warn_static("get rid:") -inline int compareFilePath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs); } - -inline bool equalFilePath(const Zstring& lhs, const Zstring& rhs) { return compareLocalPath(lhs, rhs) == 0; } - -struct LessFilePath { bool operator()(const Zstring& lhs, const Zstring& rhs) const { return compareLocalPath(lhs, rhs) < 0; } }; +inline bool equalFilePath(const Zstring& lhs, const Zstring& rhs) { return compareNativePath(lhs, rhs) == 0; } //------------------------------------------------------------------------------------------ @@ -92,18 +82,50 @@ Zstring appendSeparator(Zstring path) //support rvalue references! inline +Zstring appendPaths(const Zstring& basePath, const Zstring& relPath, Zchar pathSep) +{ + using namespace zen; + + assert(!startsWith(relPath, pathSep) && !endsWith(relPath, pathSep)); + if (relPath.empty()) + return basePath; + if (basePath.empty()) + return relPath; + + if (startsWith(relPath, pathSep)) + { + if (relPath.size() == 1) + return basePath; + + if (endsWith(basePath, pathSep)) + return basePath + (relPath.c_str() + 1); + } + else if (!endsWith(basePath, pathSep)) + { + Zstring output = basePath; + output.reserve(basePath.size() + 1 + relPath.size()); //append all three strings using a single memory allocation + return std::move(output) + pathSep + relPath; // + } + + return basePath + relPath; +} + +inline Zstring nativeAppendPaths(const Zstring& basePath, const Zstring& relPath) { return appendPaths(basePath, relPath, FILE_NAME_SEPARATOR); } + + +inline Zstring getFileExtension(const Zstring& filePath) { //const Zstring fileName = afterLast(filePath, FILE_NAME_SEPARATOR, zen::IF_MISSING_RETURN_ALL); //return afterLast(fileName, Zstr('.'), zen::IF_MISSING_RETURN_NONE); - auto it = zen::find_last(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR); + auto it = zen::findLast(filePath.begin(), filePath.end(), FILE_NAME_SEPARATOR); if (it == filePath.end()) it = filePath.begin(); else ++it; - auto it2 = zen::find_last(it, filePath.end(), Zstr('.')); + auto it2 = zen::findLast(it, filePath.end(), Zstr('.')); if (it2 != filePath.end()) ++it2; diff --git a/zenXml/zenxml/parser.h b/zenXml/zenxml/parser.h index a90d163a..70bf6654 100755 --- a/zenXml/zenxml/parser.h +++ b/zenXml/zenxml/parser.h @@ -316,7 +316,7 @@ public: Token getNextToken() //throw XmlParsingError { //skip whitespace - pos_ = std::find_if(pos_, stream_.end(), std::not_fn(isWhiteSpace<char>)); + pos_ = std::find_if_not(pos_, stream_.end(), isWhiteSpace<char>); if (pos_ == stream_.end()) return Token::TK_END; diff --git a/zenXml/zenxml/xml.h b/zenXml/zenxml/xml.h index 5eddc462..15d635bd 100755 --- a/zenXml/zenxml/xml.h +++ b/zenXml/zenxml/xml.h @@ -277,12 +277,12 @@ public: { bool success = readStruc(*refList_[refIndex_], value); if (!success) - log_->notifyConversionError(getNameFormatted()); + log_.ref().notifyConversionError(getNameFormatted()); return success; } else { - log_->notifyMissingElement(getNameFormatted()); + log_.ref().notifyMissingElement(getNameFormatted()); return false; } } @@ -312,12 +312,12 @@ public: { bool success = refList_[refIndex_]->getAttribute(name, value); if (!success) - log_->notifyMissingAttribute(getNameFormatted(), utfTo<std::string>(name)); + log_.ref().notifyMissingAttribute(getNameFormatted(), utfTo<std::string>(name)); return success; } else { - log_->notifyMissingElement(getNameFormatted()); + log_.ref().notifyMissingElement(getNameFormatted()); return false; } } @@ -357,7 +357,7 @@ public: However be aware that the chain of connected proxy instances will be broken once you call XmlIn::get() to retrieve the underlying pointer. Errors that occur when working with this pointer are not logged by the original set of related instances. */ - bool haveErrors() const { return !log_->elementList().empty(); } + bool haveErrors() const { return !log_.ref().elementList().empty(); } ///Get a list of XML element and attribute names which failed to convert to user data. /** @@ -368,13 +368,13 @@ public: std::vector<String> getErrorsAs() const { std::vector<String> output; - const auto& elements = log_->elementList(); + const auto& elements = log_.ref().elementList(); std::transform(elements.begin(), elements.end(), std::back_inserter(output), [](const std::string& str) { return utfTo<String>(str); }); return output; } private: - XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const std::shared_ptr<ErrorLog>& sharedlog) : + XmlIn(const std::vector<const XmlElement*>& siblingList, const std::string& elementNameFmt, const SharedRef<ErrorLog>& sharedlog) : refList_(siblingList), formattedName_(elementNameFmt), log_(sharedlog) { assert((!siblingList.empty() && elementNameFmt.empty()) || (siblingList.empty() && !elementNameFmt.empty())); } @@ -423,7 +423,7 @@ private: std::vector<const XmlElement*> refList_; //all sibling elements with same name (all pointers bound!) size_t refIndex_ = 0; //this sibling's index in refList_ std::string formattedName_; //contains full and formatted element name if (and only if) refList_ is empty - std::shared_ptr<ErrorLog> log_{ std::make_shared<ErrorLog>() }; //always bound + mutable SharedRef<ErrorLog> log_ = makeSharedRef<ErrorLog>(); }; |