diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:13:13 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:13:13 +0200 |
commit | 7f23ee90fd545995a29e2175f15e8b97e59ca67a (patch) | |
tree | f8d0afac51995032e58b9a475ccbbc73ba207baf | |
parent | 3.19 (diff) | |
download | FreeFileSync-7f23ee90fd545995a29e2175f15e8b97e59ca67a.tar.gz FreeFileSync-7f23ee90fd545995a29e2175f15e8b97e59ca67a.tar.bz2 FreeFileSync-7f23ee90fd545995a29e2175f15e8b97e59ca67a.zip |
3.20
216 files changed, 3127 insertions, 2399 deletions
diff --git a/Application.cpp b/Application.cpp index a9628646..dcd125bc 100644 --- a/Application.cpp +++ b/Application.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "application.h" #include "ui/main_dlg.h" #include <wx/msgdlg.h> @@ -286,7 +286,7 @@ void Application::runBatchMode(const wxString& filename, xmlAccess::XmlGlobalSet { const wxString soundFile = zen::getResourceDir() + wxT("Sync_Complete.wav"); if (fileExists(toZ(soundFile))) - wxSound::Play(soundFile, wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! + wxSound::Play(soundFile, wxSOUND_ASYNC); //warning: this may fail and show a wxWidgets error message! => must not play when running FFS as a service! } } catch (BatchAbortProcess&) //exit used by statusHandler diff --git a/Application.h b/Application.h index 79b4017d..d26fd59f 100644 --- a/Application.h +++ b/Application.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FREEFILESYNCAPP_H #define FREEFILESYNCAPP_H diff --git a/BUILD/Changelog.txt b/BUILD/Changelog.txt index 36999147..b55ca854 100644 --- a/BUILD/Changelog.txt +++ b/BUILD/Changelog.txt @@ -2,10 +2,22 @@ |FreeFileSync| -------------- +Changelog v3.20 +--------------- +Scan multiple directories in parallel +Automatically resolve disconnected network maps +Fixed temporal hang when dropping large files on main dialog +<Automatic> mode: Fixed issue regarding directory names differing in case during first sync +Delete permanently if recycle bin is not available (Linux) +Keep FreeFileSync responsive when trying to access non-existent network folder +Support for Ubuntu Unity Launcher (Linux) +RealtimeSync: Failure notification if command line is invalid (Linux) + + Changelog v3.19 --------------- -Exclude directories from synchronization which cannot be accessed during comparison -Warning if Recycle Bin is not available instead of deleting silently +Exclude subdirectories from synchronization which cannot be accessed during comparison +Warning if Recycle Bin is not available instead of deleting silently (Windows) Adapted log message if missing recycler leads to permanent deletion (Windows) Revert to per file recycle bin handling if creating temp recycler folder fails Avoid orphaned deletion temp directories on network drives diff --git a/BUILD/FreeFileSync.chm b/BUILD/FreeFileSync.chm Binary files differindex b5c6039c..536b4cee 100644 --- a/BUILD/FreeFileSync.chm +++ b/BUILD/FreeFileSync.chm diff --git a/BUILD/Languages/chinese_simple.lng b/BUILD/Languages/chinese_simple.lng index 50dc50b5..3559ad38 100644 --- a/BUILD/Languages/chinese_simple.lng +++ b/BUILD/Languages/chinese_simple.lng @@ -7,6 +7,8 @@ <plural definition>0</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>在Explorer中显示</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>实时同步 - 自动同步</target> <source>Browse</source> <target>浏览</target> -<source>Invalid commandline: "%x"</source> -<target>非法命令行:"%x"</target> +<source>Invalid commandline: %x</source> +<target>非法命令行:%x</target> <source>Error resolving symbolic link:</source> <target>解决符号链接出错:</target> <source>Show popup</source> @@ -108,10 +110,10 @@ <target>初始化同步:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>FreeFileSync数据库文件其中一个不存在:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>以下文件中的FreeFileSync数据库入口其中一个不存在:</target> <source>Error reading from synchronization database:</source> <target>从同步数据库中读取时出错:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>发生异常!</target> <source>Error deleting file:</source> @@ -133,6 +135,15 @@ <target>信息</target> <source>Fatal Error</source> <target>致命错误</target> +<source>Scanning:</source> +<target>扫描中:</target> +<source>Encoding extended time information: %x</source> +<target>正在编码扩展时间信息:%x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>无效的 FreeFileSync 配置文件!</target> <source>File does not exist:</source> @@ -224,20 +235,18 @@ The command line is executed each time: <target>监视激活...</target> <source>Waiting for missing directories...</source> <target>正在等待丢失的目录...</target> -<source>Could not initialize directory monitoring:</source> -<target>不能初始化目录监视:</target> <source>A directory input field is empty.</source> <target>有一个目录输入字段是空的.</target> -<source>Error when monitoring directories.</source> -<target>监视目录时出错.</target> <source>Drag && drop</source> <target>拖放</target> +<source>Could not initialize directory monitoring:</source> +<target>不能初始化目录监视:</target> +<source>Error when monitoring directories.</source> +<target>监视目录时出错.</target> <source>Conversion error:</source> <target>转换错误:</target> <source>Error moving file:</source> <target>移动文件时出错:</target> -<source>Operation aborted!</source> -<target>操作已取消!</target> <source>Target file already existing!</source> <target>目标文件已经存在!</target> <source>Error moving directory:</source> @@ -268,6 +277,8 @@ The command line is executed each time: <target>写入文件出错:</target> <source>Error reading file:</source> <target>读取文件出错:</target> +<source>Operation aborted!</source> +<target>操作已取消!</target> <source>Endless loop when traversing directory:</source> <target>遍历目录时出现无限循环:</target> <source>Error traversing directory:</source> @@ -408,24 +419,22 @@ The command line is executed each time: <target>为自动同步创建一个批处理. 要开始批处理模式只需简单地双击批处理文件或通过命令行执行:FreeFileSync.exe <批处理文件名>. 此外还可以通过操作系统的计划任务来定时执行.</target> <source>Help</source> <target>帮助</target> -<source>Configuration overview:</source> -<target>配置概览:</target> <source>Filter files</source> <target>过滤文件</target> -<source>Status feedback</source> -<target>状况反馈</target> -<source>Silent mode</source> -<target>静默模式</target> -<source>Start minimized and write status information to a logfile</source> -<target>以最小化开始运行并将信息写到日志文件</target> <source>Error handling</source> <target>错误处理</target> <source>Overview</source> <target>摘要</target> -<source>Select logfile directory:</source> -<target>选择日志保存位置:</target> +<source>Status feedback</source> +<target>状况反馈</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>最大的日志文件数:</target> +<source>Select logfile directory:</source> +<target>选择日志保存位置:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>保存(&S)</target> <source>&Load</source> @@ -691,10 +700,6 @@ Transfer file and directory permissions <target>文件夹对</target> <source>Select view</source> <target>选择视图</target> -<source>Folder Comparison and Synchronization</source> -<target>文件夹比较与同步</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>此系统上不支持回收站!</target> <source>Set direction:</source> <target>设置方向:</target> <source>Exclude temporarily</source> @@ -729,6 +734,8 @@ Transfer file and directory permissions <target>保存更改到当前配置?</target> <source>Configuration loaded!</source> <target>配置已加载!</target> +<source>Folder Comparison and Synchronization</source> +<target>文件夹比较与同步</target> <source>Hide files that exist on left side only</source> <target>隐藏仅在左侧的文件</target> <source>Show files that exist on left side only</source> @@ -931,10 +938,6 @@ Transfer file and directory permissions <target>设置默认的同步方向:旧文件会被新文件覆盖.</target> <source>The file does not contain a valid configuration:</source> <target>该文件不包含有效的配置:</target> -<source>Scanning:</source> -<target>扫描中:</target> -<source>Encoding extended time information: %x</source> -<target>正在编码扩展时间信息:%x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>你可以忽略这个错误而认为目录是空的.</target> <source>Directory does not exist:</source> @@ -1011,8 +1014,6 @@ Transfer file and directory permissions <target>更新 %x 的属性</target> <source>Source directory does not exist anymore:</source> <target>源目录已经不存在:</target> -<source>Generating database...</source> -<target>正在生成数据库...</target> <source>Nothing to synchronize according to configuration!</source> <target>根据配置没有任何同步!</target> <source>Target directory name must not be empty!</source> @@ -1033,10 +1034,14 @@ Transfer file and directory permissions <target>需要可用的磁盘空间:</target> <source>Free disk space available:</source> <target>可用磁盘空间:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>有一个目录将被修改,而它是多个文件夹对的一部分!请重新检查同步设置!</target> <source>Processing folder pair:</source> <target>正在处理成对文件夹:</target> +<source>Generating database...</source> +<target>正在生成数据库...</target> <source>Error copying locked file %x!</source> <target>复制已锁定的文件时出错 %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/chinese_traditional.lng b/BUILD/Languages/chinese_traditional.lng index 54f3d843..9f1da986 100644 --- a/BUILD/Languages/chinese_traditional.lng +++ b/BUILD/Languages/chinese_traditional.lng @@ -7,6 +7,8 @@ <plural definition>0</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>在資源管理器中顯示</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>即時同步 - 自動同步</target> <source>Browse</source> <target>瀏覽</target> -<source>Invalid commandline: "%x"</source> -<target>無效的命令列:"%x"</target> +<source>Invalid commandline: %x</source> +<target>無效的命令列:%x</target> <source>Error resolving symbolic link:</source> <target>解析錯誤的符號連結:</target> <source>Show popup</source> @@ -108,10 +110,10 @@ <target>初始化同步:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>其中一個 FreeFileSync 資料庫檔案不存在:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>以下資料庫條目的其中一個 FreeFileSync 檔案不存在:</target> <source>Error reading from synchronization database:</source> <target>讀取同步資料庫錯誤:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>發生異常!</target> <source>Error deleting file:</source> @@ -133,6 +135,15 @@ <target>訊息</target> <source>Fatal Error</source> <target>嚴重錯誤</target> +<source>Scanning:</source> +<target>掃瞄中:</target> +<source>Encoding extended time information: %x</source> +<target>編碼延長時間資訊:%x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>無效的 FreeFileSync 配置檔案!</target> <source>File does not exist:</source> @@ -224,20 +235,18 @@ The command line is executed each time: <target>監測活動...</target> <source>Waiting for missing directories...</source> <target>等待缺少的目錄...</target> -<source>Could not initialize directory monitoring:</source> -<target>無法初始化目錄監測:</target> <source>A directory input field is empty.</source> <target>目錄輸入的欄位為空</target> -<source>Error when monitoring directories.</source> -<target>監測目錄錯誤。</target> <source>Drag && drop</source> <target>拖放</target> +<source>Could not initialize directory monitoring:</source> +<target>無法初始化目錄監測:</target> +<source>Error when monitoring directories.</source> +<target>監測目錄錯誤。</target> <source>Conversion error:</source> <target>轉換錯誤:</target> <source>Error moving file:</source> <target>移動檔案錯誤:</target> -<source>Operation aborted!</source> -<target>中止操作!</target> <source>Target file already existing!</source> <target>目標檔案已存在!</target> <source>Error moving directory:</source> @@ -268,6 +277,8 @@ The command line is executed each time: <target>寫入檔案錯誤:</target> <source>Error reading file:</source> <target>讀取檔案錯誤:</target> +<source>Operation aborted!</source> +<target>中止操作!</target> <source>Endless loop when traversing directory:</source> <target>當遍歷目錄時無限循環:</target> <source>Error traversing directory:</source> @@ -408,24 +419,22 @@ The command line is executed each time: <target>新建一個自動同步的批次檔。若要開始批次處理模式只需按兩下此檔或通過命令列執行:FreeFileSync.exe <batchfile>。還可以安排在您的作業系統的任務計畫中。</target> <source>Help</source> <target>說明</target> -<source>Configuration overview:</source> -<target>配置概述:</target> <source>Filter files</source> <target>篩選檔案</target> -<source>Status feedback</source> -<target>狀態回報</target> -<source>Silent mode</source> -<target>靜音模式</target> -<source>Start minimized and write status information to a logfile</source> -<target>啟動最小化和寫入狀態資訊到日誌檔</target> <source>Error handling</source> <target>錯誤處理</target> <source>Overview</source> <target>摘要</target> -<source>Select logfile directory:</source> -<target>選擇日誌檔目錄:</target> +<source>Status feedback</source> +<target>狀態回報</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>日誌檔的最大數目:</target> +<source>Select logfile directory:</source> +<target>選擇日誌檔目錄:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>儲存(&S)</target> <source>&Load</source> @@ -682,10 +691,6 @@ Transfer file and directory permissions <target>資料夾對</target> <source>Select view</source> <target>選擇檢視</target> -<source>Folder Comparison and Synchronization</source> -<target>資料夾比對和同步</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>資源回收筒尚未支援此系統!</target> <source>Set direction:</source> <target>設定方向:</target> <source>Exclude temporarily</source> @@ -720,6 +725,8 @@ Transfer file and directory permissions <target>要儲存目前配置的更改嗎?</target> <source>Configuration loaded!</source> <target>已載入配置!</target> +<source>Folder Comparison and Synchronization</source> +<target>資料夾比對和同步</target> <source>Hide files that exist on left side only</source> <target>隱藏只存在於左邊的檔案</target> <source>Show files that exist on left side only</source> @@ -922,10 +929,6 @@ Transfer file and directory permissions <target>設定預設同步方向:舊檔案會被較新的檔案覆蓋。</target> <source>The file does not contain a valid configuration:</source> <target>該檔案不包含有效的配置:</target> -<source>Scanning:</source> -<target>掃瞄中:</target> -<source>Encoding extended time information: %x</source> -<target>編碼延長時間資訊:%x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>您可以忽略此錯誤,考慮該目錄為空。</target> <source>Directory does not exist:</source> @@ -1002,8 +1005,6 @@ Transfer file and directory permissions <target>更新 %x 個的屬性</target> <source>Source directory does not exist anymore:</source> <target>來源目錄不存在:</target> -<source>Generating database...</source> -<target>產生資料庫...</target> <source>Nothing to synchronize according to configuration!</source> <target>根據配置没有任何同步!</target> <source>Target directory name must not be empty!</source> @@ -1024,10 +1025,14 @@ Transfer file and directory permissions <target>所需要的可用磁碟空間:</target> <source>Free disk space available:</source> <target>可用磁碟空間:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>一個目錄將被修改,這是多對資料夾的一部份!請檢閱同步設定!</target> <source>Processing folder pair:</source> <target>處理一對資料夾:</target> +<source>Generating database...</source> +<target>產生資料庫...</target> <source>Error copying locked file %x!</source> <target>複製已鎖定檔案錯誤 %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/czech.lng b/BUILD/Languages/czech.lng index 367221a2..223b3e6f 100644 --- a/BUILD/Languages/czech.lng +++ b/BUILD/Languages/czech.lng @@ -7,6 +7,8 @@ <plural definition>n==1 ? 0 : n>=2 && n<=4 ? 1 : 2</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Zobrazit v Průzkumníkovi</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Automatická synchronizace</target> <source>Browse</source> <target>Procházet</target> -<source>Invalid commandline: "%x"</source> -<target>Neplatný příkaz: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Neplatný příkaz: %x</target> <source>Error resolving symbolic link:</source> <target>Chyba odkazu zástupce:</target> <source>Show popup</source> @@ -110,10 +112,10 @@ <target>Prvotní synchronizace:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Některý z databázových souborů FreeFileSync neexistuje:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Některá z položek databáze FreeFileSync k následujícímu souboru ještě neexistuje:</target> <source>Error reading from synchronization database:</source> <target>Chyba čtení synchronizační databáze:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Vyskytla se chyba!</target> <source>Error deleting file:</source> @@ -137,6 +139,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Závažná chyba</target> +<source>Scanning:</source> +<target>Zpracováváno:</target> +<source>Encoding extended time information: %x</source> +<target>Zpracování rozšířené informace o čase: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Chybný konfigurační soubor FreeFileSync!</target> <source>File does not exist:</source> @@ -234,20 +245,18 @@ Příkazová řádka je spuštěna pokaždé když: <target>Sledování zapnuto...</target> <source>Waiting for missing directories...</source> <target>Čekání na nedostupné adresáře...</target> -<source>Could not initialize directory monitoring:</source> -<target>Nelze nastavit monitorování adresáře:</target> <source>A directory input field is empty.</source> <target>Není zadán vstupní adresář.</target> -<source>Error when monitoring directories.</source> -<target>Chyba při sledování adresářů.</target> <source>Drag && drop</source> <target>Drag && Drop</target> +<source>Could not initialize directory monitoring:</source> +<target>Nelze nastavit monitorování adresáře:</target> +<source>Error when monitoring directories.</source> +<target>Chyba při sledování adresářů.</target> <source>Conversion error:</source> <target>Chyba konverze:</target> <source>Error moving file:</source> <target>Chyba přesouvání souboru:</target> -<source>Operation aborted!</source> -<target>Operace zrušena!</target> <source>Target file already existing!</source> <target>Cílový soubor již existuje!</target> <source>Error moving directory:</source> @@ -278,6 +287,8 @@ Příkazová řádka je spuštěna pokaždé když: <target>Chyba zápisu souboru:</target> <source>Error reading file:</source> <target>Chyba čtení souboru:</target> +<source>Operation aborted!</source> +<target>Operace zrušena!</target> <source>Endless loop when traversing directory:</source> <target>Zacyklení při procházení adresáře:</target> <source>Error traversing directory:</source> @@ -418,24 +429,22 @@ Příkazová řádka je spuštěna pokaždé když: <target>Vytvoří dávkový souboru pro automatický provoz. Ke spuštění dávky jednoduše poklikejte na vytvořený soubor nebo jejho jméno zadejte jako parametr při spuštění FreeFileSync: FreeFileSync.exe <batchfile>. Stejně tak můžete ke spuštění využít plánovač úloh vašeho operačního systému.</target> <source>Help</source> <target>Nápověda</target> -<source>Configuration overview:</source> -<target>Přehled konfigurace:</target> <source>Filter files</source> <target>Filtr souborů</target> -<source>Status feedback</source> -<target>Běh programu</target> -<source>Silent mode</source> -<target>Tichý mód</target> -<source>Start minimized and write status information to a logfile</source> -<target>Spustit minimalizovaný a zapisovat informace do záznamového souboru</target> <source>Error handling</source> <target>Zpracování chyb</target> <source>Overview</source> <target>Přehled</target> -<source>Select logfile directory:</source> -<target>Vyberte adresář pro záznamové soubory:</target> +<source>Status feedback</source> +<target>Běh programu</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Maximální počet záznamových soubor</target> +<source>Select logfile directory:</source> +<target>Vyberte adresář pro záznamové soubory:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Uložit</target> <source>&Load</source> @@ -700,10 +709,6 @@ Přenést přístupová oprávnění souborů a adresářů <target>Adresářové páry</target> <source>Select view</source> <target>Vyberte zobrazení</target> -<source>Folder Comparison and Synchronization</source> -<target>Porovnání a Synchronizace adresářů</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Koš není na tomto systému zatím podporován!</target> <source>Set direction:</source> <target>Nastavit adresář:</target> <source>Exclude temporarily</source> @@ -738,6 +743,8 @@ Přenést přístupová oprávnění souborů a adresářů <target>Uložit změny do aktuální konfigurace?</target> <source>Configuration loaded!</source> <target>Konfigurace načtena.</target> +<source>Folder Comparison and Synchronization</source> +<target>Porovnání a Synchronizace adresářů</target> <source>Hide files that exist on left side only</source> <target>Skrýt soubory existující pouze vlevo</target> <source>Show files that exist on left side only</source> @@ -952,10 +959,6 @@ Přenést přístupová oprávnění souborů a adresářů <target>Nastaven výchozí způsob synchronizace: Staré soubory budou nahrazeny novými.</target> <source>The file does not contain a valid configuration:</source> <target>Soubor neobsahuje platnou konfiguraci:</target> -<source>Scanning:</source> -<target>Zpracováváno:</target> -<source>Encoding extended time information: %x</source> -<target>Zpracování rozšířené informace o čase: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Tuto chybu můžete ignorovat a považovat neexistující adresář jako prázdný.</target> <source>Directory does not exist:</source> @@ -1032,8 +1035,6 @@ Přenést přístupová oprávnění souborů a adresářů <target>Aktualizace atributů souboru %x</target> <source>Source directory does not exist anymore:</source> <target>Zdrojový adresář již neexistuje:</target> -<source>Generating database...</source> -<target>Vytváření databáze...</target> <source>Nothing to synchronize according to configuration!</source> <target>Podle dané konfigurace není co synchronizovat!</target> <source>Target directory name must not be empty!</source> @@ -1054,10 +1055,14 @@ Přenést přístupová oprávnění souborů a adresářů <target>Požadované volné místo na disku:</target> <source>Free disk space available:</source> <target>Volné místo k dispozici:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Bude změně adresář, který je součástí adresářového páru vícenásobného porovnání! Prosím zkontrolujte si nastavení synchronizace!</target> <source>Processing folder pair:</source> <target>Zpracovávání adresářové páru:</target> +<source>Generating database...</source> +<target>Vytváření databáze...</target> <source>Error copying locked file %x!</source> <target>Chyba kopírování zamčeného souboru %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/danish.lng b/BUILD/Languages/danish.lng index 4c0c3f65..eb879c05 100644 --- a/BUILD/Languages/danish.lng +++ b/BUILD/Languages/danish.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Vis i Explorer</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSynk - Automatisk Synkronisering</target> <source>Browse</source> <target>Gennemse</target> -<source>Invalid commandline: "%x"</source> -<target>Ugyldig kommando: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Ugyldig kommando: %x</target> <source>Error resolving symbolic link:</source> <target>Error resolving symbolic link:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Indledende synkronisering:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>En af FreeFileSync database filerne findes ikke endnu:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>En af FreeFileSync database posterne i den følgende fil findes ikke endnu:</target> <source>Error reading from synchronization database:</source> <target>Fejl i læsning fra synkroniserings databasen:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>En undtagelse er opstået!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Uoprettelig Fejl</target> +<source>Scanning:</source> +<target>Skanner:</target> +<source>Encoding extended time information: %x</source> +<target>Finder udvidet tids information: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Ugyldig FreeFileSync config fil!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ Kommando linjen bliver afviklet hver gang: <target>Overvågning aktiv...</target> <source>Waiting for missing directories...</source> <target>Venter på manglende biblioteker...</target> -<source>Could not initialize directory monitoring:</source> -<target>Kunne ikke initialiserer biblioteks overvågningen:</target> <source>A directory input field is empty.</source> <target>Et biblioteks felt er tomt.</target> -<source>Error when monitoring directories.</source> -<target>Fejl i overvågning af biblioteker.</target> <source>Drag && drop</source> <target>Træk && slip</target> +<source>Could not initialize directory monitoring:</source> +<target>Kunne ikke initialiserer biblioteks overvågningen:</target> +<source>Error when monitoring directories.</source> +<target>Fejl i overvågning af biblioteker.</target> <source>Conversion error:</source> <target>Konverterings fejl:</target> <source>Error moving file:</source> <target>Fejl i flytning af fil:</target> -<source>Operation aborted!</source> -<target>Operation afbrudt!</target> <source>Target file already existing!</source> <target>Filen findes i forvejen!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ Kommando linjen bliver afviklet hver gang: <target>Fejl i at skrive fil:</target> <source>Error reading file:</source> <target>Fejl i læsning af fil:</target> +<source>Operation aborted!</source> +<target>Operation afbrudt!</target> <source>Endless loop when traversing directory:</source> <target>Uendelig løkke ved gennemgang af bibliotek:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ Kommando linjen bliver afviklet hver gang: <target>Opret en batch fil til automatisk synkronisering. For at starte i batch mode dobbelt-click på filen eller afvikle med kommandoen: FreeFileSync.exe <batchfil>. Dette kan også planlægges i dit systems opgavestyring.</target> <source>Help</source> <target>Hjælp</target> -<source>Configuration overview:</source> -<target>Overblik over konfiguration:</target> <source>Filter files</source> <target>Filter filer</target> -<source>Status feedback</source> -<target>Status feedback</target> -<source>Silent mode</source> -<target>Lydløs tilstand</target> -<source>Start minimized and write status information to a logfile</source> -<target>Start minimeret og skriv status information til en log fil</target> <source>Error handling</source> <target>Fejl håndtering</target> <source>Overview</source> <target>Overblik</target> -<source>Select logfile directory:</source> -<target>Vælg log fil bibliotek:</target> +<source>Status feedback</source> +<target>Status feedback</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Maksimalt antal log filer:</target> +<source>Select logfile directory:</source> +<target>Vælg log fil bibliotek:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Gem</target> <source>&Load</source> @@ -695,10 +704,6 @@ Overfør fil og biblioteks tilladelser <target>Mappe par</target> <source>Select view</source> <target>Vælg udseende</target> -<source>Folder Comparison and Synchronization</source> -<target>Mappe sammenligning og synkronisering</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Skraldespanden er ikke understøttet for dette system endnu!</target> <source>Set direction:</source> <target>Sæt handlevejen:</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Overfør fil og biblioteks tilladelser <target>Gem ændringer til nuværende konfiguration?</target> <source>Configuration loaded!</source> <target>Konfiguration hentet!</target> +<source>Folder Comparison and Synchronization</source> +<target>Mappe sammenligning og synkronisering</target> <source>Hide files that exist on left side only</source> <target>Skjul filder der kun findes på venstre side</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Overfør fil og biblioteks tilladelser <target>Sætter standard synkroniseringsvej: Gamle filer bliver overskrevet med nyere.</target> <source>The file does not contain a valid configuration:</source> <target>Filen indeholder ikke en gyldig konfiguration:</target> -<source>Scanning:</source> -<target>Skanner:</target> -<source>Encoding extended time information: %x</source> -<target>Finder udvidet tids information: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Du kan ignorer denne fejl, og opfatte biblioteket som tomt.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Overfør fil og biblioteks tilladelser <target>Opdaterer attributter af %x</target> <source>Source directory does not exist anymore:</source> <target>Kilde biblioteket findes ikke mere:</target> -<source>Generating database...</source> -<target>Opretter database...</target> <source>Nothing to synchronize according to configuration!</source> <target>Intet at synkroniserer ifølge dokumentationen!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Overfør fil og biblioteks tilladelser <target>Krævet ledig diskplads:</target> <source>Free disk space available:</source> <target>Ledig diskplads tilgængelig:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Et bibliotek vil blive ændret som er en del af et mappe par! Gennemse venligst synkroniserings indstillingerne!</target> <source>Processing folder pair:</source> <target>Behandler mappe par:</target> +<source>Generating database...</source> +<target>Opretter database...</target> <source>Error copying locked file %x!</source> <target>Fejl i kopiering af låst fil %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/dutch.lng b/BUILD/Languages/dutch.lng index f7000f4d..eea01f72 100644 --- a/BUILD/Languages/dutch.lng +++ b/BUILD/Languages/dutch.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Toon in de verkenner</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Geautomatiseerde Synchronisatie</target> <source>Browse</source> <target>Verkennen</target> -<source>Invalid commandline: "%x"</source> -<target>Ongeldige invoer: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Ongeldige invoer: %x</target> <source>Error resolving symbolic link:</source> <target>Fout tijdens opzoeken van symbolische koppeling:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Initiële synchronisatie:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Eén van de FreeFileSync database bestanden bestaat nog niet:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Eén van de FreeFileSync database ingangen van het volgende bestand bestaat nog niet:</target> <source>Error reading from synchronization database:</source> <target>Fout tijdens uitlezen van synchronisatie-database:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Er heeft een uitzondering plaatsgevonden!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Fatale fout</target> +<source>Scanning:</source> +<target>Doorzoekt:</target> +<source>Encoding extended time information: %x</source> +<target>Coderen uitgebreide tijd informatie: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Foutief FreeFileSync configuratiebestand!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ De opdrachtregel wordt telkens uitgevoerd indien: <target>Observeren actief...</target> <source>Waiting for missing directories...</source> <target>Wacht op missende mappen...</target> -<source>Could not initialize directory monitoring:</source> -<target>Initaliseren van map-observatie niet mogelijk:</target> <source>A directory input field is empty.</source> <target>Een tekstveld over de map is leeg.</target> -<source>Error when monitoring directories.</source> -<target>Fout tijdens observeren van mappen.</target> <source>Drag && drop</source> <target>Drag en drop</target> +<source>Could not initialize directory monitoring:</source> +<target>Initaliseren van map-observatie niet mogelijk:</target> +<source>Error when monitoring directories.</source> +<target>Fout tijdens observeren van mappen.</target> <source>Conversion error:</source> <target>Converteer fout:</target> <source>Error moving file:</source> <target>Fout tijdens verplaatsen van bestand:</target> -<source>Operation aborted!</source> -<target>Bewerking afgebroken!</target> <source>Target file already existing!</source> <target>Doelbestand bestaat al!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ De opdrachtregel wordt telkens uitgevoerd indien: <target>Fout tijdens schrijven van bestand:</target> <source>Error reading file:</source> <target>Fout tijdens lezen van bestand:</target> +<source>Operation aborted!</source> +<target>Bewerking afgebroken!</target> <source>Endless loop when traversing directory:</source> <target>Oneindige lus bij het doorlopen van map:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ De opdrachtregel wordt telkens uitgevoerd indien: <target>Creëer een taakbestand voor geautomatiseerde synchronisatie. Om te starten in taakmodus dubbelklik op het bestand of uitvoeren via opdrachtregel: FreeFileSync.exe <takenbestand>. Dit kan ook gepland worden met de taakplanner van uw OS.</target> <source>Help</source> <target>Help</target> -<source>Configuration overview:</source> -<target>Configuratie overzicht:</target> <source>Filter files</source> <target>Filter bestanden</target> -<source>Status feedback</source> -<target>Status terugkoppeling</target> -<source>Silent mode</source> -<target>Stille modus</target> -<source>Start minimized and write status information to a logfile</source> -<target>Start geminimaliseerd en schrijf status informatie naar een logbestand</target> <source>Error handling</source> <target>Fout afhandeling</target> <source>Overview</source> <target>Overzicht</target> -<source>Select logfile directory:</source> -<target>Selecteer een map voor het logbestand:</target> +<source>Status feedback</source> +<target>Status terugkoppeling</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Maximale aantal van logbestanden:</target> +<source>Select logfile directory:</source> +<target>Selecteer een map voor het logbestand:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Opslaan</target> <source>&Load</source> @@ -694,11 +703,7 @@ Zet bestand en map permissies over <source>Folder pairs</source> <target>Map paren</target> <source>Select view</source> -<target>Kies kijk</target> -<source>Folder Comparison and Synchronization</source> -<target>Mappen vergelijken en synchroniseren</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Prullenbak is op dit systeem nog niet ondersteund!</target> +<target>Kies weergave</target> <source>Set direction:</source> <target>Stel richting in:</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Zet bestand en map permissies over <target>Veranderingen opslaan in de huidige configuratie?</target> <source>Configuration loaded!</source> <target>Configuratie geladen!</target> +<source>Folder Comparison and Synchronization</source> +<target>Mappen vergelijken en synchroniseren</target> <source>Hide files that exist on left side only</source> <target>Verberg bestanden die alleen aan de linkerzijde bestaan</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Zet bestand en map permissies over <target>Stel standaard synchronisatie richtingen in: Oude bestanden worden door nieuwere bestanden overschreven.</target> <source>The file does not contain a valid configuration:</source> <target>Het bestand bevat geen geldige configuratie:</target> -<source>Scanning:</source> -<target>Doorzoekt:</target> -<source>Encoding extended time information: %x</source> -<target>Coderen uitgebreide tijd informatie: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Je kan deze error negeren als de map leeg is.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Zet bestand en map permissies over <target>Attributen bijwerken van %x</target> <source>Source directory does not exist anymore:</source> <target>Bronmap bestaat niet meer:</target> -<source>Generating database...</source> -<target>Genereren van database...</target> <source>Nothing to synchronize according to configuration!</source> <target>Volgens de configuratie hoeft er niets gesynchroniseerd te worden!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Zet bestand en map permissies over <target>Vrije ruimte op harde schrijf nodig:</target> <source>Free disk space available:</source> <target>Beschikbare vrije schijfruimte :</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Een map wordt bewerkt die deel is van meerdere mappen! Controleer de synchronisatie instellingen!</target> <source>Processing folder pair:</source> <target>Verwerking van gekoppelde mappen:</target> +<source>Generating database...</source> +<target>Genereren van database...</target> <source>Error copying locked file %x!</source> <target>Fout tijdens kopiëren van vergrendeld bestand %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/english_uk.lng b/BUILD/Languages/english_uk.lng index c2c776f9..fb0f9ee6 100644 --- a/BUILD/Languages/english_uk.lng +++ b/BUILD/Languages/english_uk.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target>Searching for directory %x...</target> <source>Show in Explorer</source> <target>Show in Explorer</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Automated Synchronisation</target> <source>Browse</source> <target>Browse</target> -<source>Invalid commandline: "%x"</source> -<target>Invalid commandline: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Invalid commandline: %x</target> <source>Error resolving symbolic link:</source> <target>Error resolving symbolic link:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Initial synchronisation:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>One of the FreeFileSync database files is not yet existing:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>One of the FreeFileSync database entries within the following file is not yet existing:</target> <source>Error reading from synchronization database:</source> <target>Error reading from synchronisation database:</target> +<source>Database files do not share a common synchronization session:</source> +<target>Database files do not share a common synchronisation session:</target> <source>An exception occurred!</source> <target>An exception occurred!</target> <source>Error deleting file:</source> @@ -135,6 +137,18 @@ <target>Info</target> <source>Fatal Error</source> <target>Fatal Error</target> +<source>Scanning:</source> +<target>Scanning:</target> +<source>Encoding extended time information: %x</source> +<target>Encoding extended time information: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</target> <source>Invalid FreeFileSync config file!</source> <target>Invalid FreeFileSync config file!</target> <source>File does not exist:</source> @@ -229,20 +243,18 @@ The command line is executed each time: <target>Monitoring active...</target> <source>Waiting for missing directories...</source> <target>Waiting for missing directories...</target> -<source>Could not initialize directory monitoring:</source> -<target>Could not initialise directory monitoring:</target> <source>A directory input field is empty.</source> <target>A directory input field is empty.</target> -<source>Error when monitoring directories.</source> -<target>Error when monitoring directories.</target> <source>Drag && drop</source> <target>Drag && drop</target> +<source>Could not initialize directory monitoring:</source> +<target>Could not initialise directory monitoring:</target> +<source>Error when monitoring directories.</source> +<target>Error when monitoring directories.</target> <source>Conversion error:</source> <target>Conversion error:</target> <source>Error moving file:</source> <target>Error moving file:</target> -<source>Operation aborted!</source> -<target>Operation aborted!</target> <source>Target file already existing!</source> <target>Target file already existing!</target> <source>Error moving directory:</source> @@ -273,6 +285,8 @@ The command line is executed each time: <target>Error writing file:</target> <source>Error reading file:</source> <target>Error reading file:</target> +<source>Operation aborted!</source> +<target>Operation aborted!</target> <source>Endless loop when traversing directory:</source> <target>Endless loop when traversing directory:</target> <source>Error traversing directory:</source> @@ -413,24 +427,22 @@ The command line is executed each time: <target>Create a batch file for automated synchronisation. To start in batch mode simply double-click the file or execute via command line: FreeFileSync.exe <batchfile>. This can also be scheduled in your operating system's task planner.</target> <source>Help</source> <target>Help</target> -<source>Configuration overview:</source> -<target>Configuration overview:</target> <source>Filter files</source> <target>Filter files</target> -<source>Status feedback</source> -<target>Status feedback</target> -<source>Silent mode</source> -<target>Silent mode</target> -<source>Start minimized and write status information to a logfile</source> -<target>Start minimized and write status information to a logfile</target> <source>Error handling</source> <target>Error handling</target> <source>Overview</source> <target>Overview</target> -<source>Select logfile directory:</source> -<target>Select logfile directory:</target> +<source>Status feedback</source> +<target>Status feedback</target> +<source>Run minimized</source> +<target>Run minimised</target> <source>Maximum number of logfiles:</source> <target>Maximum number of logfiles:</target> +<source>Select logfile directory:</source> +<target>Select logfile directory:</target> +<source>Batch settings</source> +<target>Batch settings</target> <source>&Save</source> <target>&Save</target> <source>&Load</source> @@ -695,10 +707,6 @@ Transfer file and directory permissions <target>Folder pairs</target> <source>Select view</source> <target>Select view</target> -<source>Folder Comparison and Synchronization</source> -<target>Folder Comparison and Synchronisation</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Recycle Bin not yet supported for this system!</target> <source>Set direction:</source> <target>Set direction:</target> <source>Exclude temporarily</source> @@ -733,6 +741,8 @@ Transfer file and directory permissions <target>Save changes to current configuration?</target> <source>Configuration loaded!</source> <target>Configuration loaded!</target> +<source>Folder Comparison and Synchronization</source> +<target>Folder Comparison and Synchronisation</target> <source>Hide files that exist on left side only</source> <target>Hide files that exist on left side only</target> <source>Show files that exist on left side only</source> @@ -941,10 +951,6 @@ Transfer file and directory permissions <target>Setting default synchronisation directions: Old files will be overwritten with newer files.</target> <source>The file does not contain a valid configuration:</source> <target>The file does not contain a valid configuration:</target> -<source>Scanning:</source> -<target>Scanning:</target> -<source>Encoding extended time information: %x</source> -<target>Encoding extended time information: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>You can ignore this error to consider the directory as empty.</target> <source>Directory does not exist:</source> @@ -1021,8 +1027,6 @@ Transfer file and directory permissions <target>Updating attributes of %x</target> <source>Source directory does not exist anymore:</source> <target>Source directory does not exist anymore:</target> -<source>Generating database...</source> -<target>Generating database...</target> <source>Nothing to synchronize according to configuration!</source> <target>Nothing to synchronise according to configuration!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1047,14 @@ Transfer file and directory permissions <target>Free disk space required:</target> <source>Free disk space available:</source> <target>Free disk space available:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>A directory will be modified which is part of multiple folder pairs! Please review synchronisation settings!</target> <source>Processing folder pair:</source> <target>Processing folder pair:</target> +<source>Generating database...</source> +<target>Generating database...</target> <source>Error copying locked file %x!</source> <target>Error copying locked file %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/finnish.lng b/BUILD/Languages/finnish.lng index a0ddf34a..286c889b 100644 --- a/BUILD/Languages/finnish.lng +++ b/BUILD/Languages/finnish.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Näytä Explorerissa</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Automaattinen täsmäytys</target> <source>Browse</source> <target>Selaa</target> -<source>Invalid commandline: "%x"</source> -<target>Virheellinen komento: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Virheellinen komento: %x</target> <source>Error resolving symbolic link:</source> <target>Virhe selvittäessä symbolista linkkiä:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Ensi täsmäytys:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Jokin FreeFileSynk tietokannan tiedostoista puuttuu vielä:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Jokin FreeFileSynk tietokannan olioista puuttuu vielä tiedostossa:</target> <source>Error reading from synchronization database:</source> <target>Virhe lukiessa täsmäytyksen tietokantaa:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Virhe havaittu!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Kohtalokas virhe</target> +<source>Scanning:</source> +<target>Haen:</target> +<source>Encoding extended time information: %x</source> +<target>Tulkitaan laajennettua aikatietoa: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Virheellinen FreeFileSync asetustiedosto!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ Komento suoritetaan kun: <target>Seuranta käynnissä...</target> <source>Waiting for missing directories...</source> <target>Odottaa puuttuvia hakemistoja...</target> -<source>Could not initialize directory monitoring:</source> -<target>Hakemiston tarkkailua ei voitu käynnistää:</target> <source>A directory input field is empty.</source> <target>Hekemistokenttä on tyhjä.</target> -<source>Error when monitoring directories.</source> -<target>Virhe seuratessa hakemistoa.</target> <source>Drag && drop</source> <target>Vedä ja pudota</target> +<source>Could not initialize directory monitoring:</source> +<target>Hakemiston tarkkailua ei voitu käynnistää:</target> +<source>Error when monitoring directories.</source> +<target>Virhe seuratessa hakemistoa.</target> <source>Conversion error:</source> <target>Konversio virhe:</target> <source>Error moving file:</source> <target>Virhe siirtäessä tiedostoa:</target> -<source>Operation aborted!</source> -<target>Toiminto lopetettiin!</target> <source>Target file already existing!</source> <target>Kohde tiedosto on jo olemassa!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ Komento suoritetaan kun: <target>Virhe kirjottaessa tiedostoa:</target> <source>Error reading file:</source> <target>Virhe lukiessa tiedostoa:</target> +<source>Operation aborted!</source> +<target>Toiminto lopetettiin!</target> <source>Endless loop when traversing directory:</source> <target>Suorita hakemiston läpikulku jatkuvana:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ Komento suoritetaan kun: <target>Luo automaattisen täsmäytyksen eräajotiedosto. Käynnistä se kaksois klikillä tai komennolla: FreeFileSync.exe <eräajotiedosto>. Käyttöjärjestelmän tehtävien ajoitustoiminnolla voit ajastaa suorittaminen.</target> <source>Help</source> <target>Ohje</target> -<source>Configuration overview:</source> -<target>Asetukset:</target> <source>Filter files</source> <target>Suodata tiedostoja</target> -<source>Status feedback</source> -<target>Tilan palaute</target> -<source>Silent mode</source> -<target>Hiljainen suoritus</target> -<source>Start minimized and write status information to a logfile</source> -<target>Käynnistä minimoituna ja kirjoita statukset lokiin</target> <source>Error handling</source> <target>Virhe käsitellessä</target> <source>Overview</source> <target>Yleiskatsaus</target> -<source>Select logfile directory:</source> -<target>Hakemisto lokitiedostoille:</target> +<source>Status feedback</source> +<target>Tilan palaute</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Lokitiedostojen enimmäismäärä:</target> +<source>Select logfile directory:</source> +<target>Hakemisto lokitiedostoille:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Tallenna</target> <source>&Load</source> @@ -695,10 +704,6 @@ Siirrä tiedosto- ja hekimisto-oikeuksia <target>Hakemistoparit</target> <source>Select view</source> <target>Valitse näkymä</target> -<source>Folder Comparison and Synchronization</source> -<target>Hakemistojen vertailu ja täsmäytys</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Tämä järjestelmä ei vielä tue Roskakoria!</target> <source>Set direction:</source> <target>Aseta suunta:</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Siirrä tiedosto- ja hekimisto-oikeuksia <target>Tallenna asetuksiin tehdyt muutokset?</target> <source>Configuration loaded!</source> <target>Asetukset ladattu!</target> +<source>Folder Comparison and Synchronization</source> +<target>Hakemistojen vertailu ja täsmäytys</target> <source>Hide files that exist on left side only</source> <target>Piilota vain vasemmalla olevat tiedostot</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Siirrä tiedosto- ja hekimisto-oikeuksia <target>Aseta oletettu suunta täsmäytykselle: Vanhat tiedostot ylikirjoitetaan uudemilla tiedostoilla.</target> <source>The file does not contain a valid configuration:</source> <target>Asetustiedosto ei ole kelvollinen:</target> -<source>Scanning:</source> -<target>Haen:</target> -<source>Encoding extended time information: %x</source> -<target>Tulkitaan laajennettua aikatietoa: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Jätää virhe huomiotta ja tulkitse hakemisto tyhjäksi.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Siirrä tiedosto- ja hekimisto-oikeuksia <target>Päivitän %x:n ominaisuudet</target> <source>Source directory does not exist anymore:</source> <target>Lähdehakemisto puuttuu:</target> -<source>Generating database...</source> -<target>Luodaan tietokantaa...</target> <source>Nothing to synchronize according to configuration!</source> <target>Asetusten mukaan ei löydy täsmäytettävää!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Siirrä tiedosto- ja hekimisto-oikeuksia <target>Tarvittava vapaa levytila:</target> <source>Free disk space available:</source> <target>Levytilaa jäljellä:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Moniosaisen hakemistoparin hakemisto muutetaan! Tarkista täsmäytys asetuksia!</target> <source>Processing folder pair:</source> <target>Käsitellään hakemistoparia:</target> +<source>Generating database...</source> +<target>Luodaan tietokantaa...</target> <source>Error copying locked file %x!</source> <target>Virhe kopioitaessa lukittua tiedostoa %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/french.lng b/BUILD/Languages/french.lng index 261503f3..b2a1ceed 100644 --- a/BUILD/Languages/french.lng +++ b/BUILD/Languages/french.lng @@ -7,6 +7,8 @@ <plural definition>n <= 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Montrer dans l'explorateur</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Synchronisation Automatisée</target> <source>Browse</source> <target>Parcourir</target> -<source>Invalid commandline: "%x"</source> -<target>Ligne de commande invalide: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Ligne de commande invalide: %x</target> <source>Error resolving symbolic link:</source> <target>Erreur lors de la résolution du lien symbolique :</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Première synchronisation :</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>L'un des fichiers de la base de données FreeFileSync n'existe plus :</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>L'une des entrées de la base de données FreeFileSync n'existe plus dans le fichier :</target> <source>Error reading from synchronization database:</source> <target>Erreur lors de la lecture de la base de données de synchro :</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Une erreur s'est produite !</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Erreur Fatale</target> +<source>Scanning:</source> +<target>Lecture en cours :</target> +<source>Encoding extended time information: %x</source> +<target>Codage de l'heure au format étendu : %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Fichier de configuration FreeFileSync invalide !</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ La ligne de commande est exécutée chaque fois que : <target>Surveillance en cours...</target> <source>Waiting for missing directories...</source> <target>En attente des répertoires absents ...</target> -<source>Could not initialize directory monitoring:</source> -<target>Impossible d'initialiser la surveillance des dossiers :</target> <source>A directory input field is empty.</source> <target>Un champ répertoire est vide</target> -<source>Error when monitoring directories.</source> -<target>Erreur lors de la surveillance des répertoires.</target> <source>Drag && drop</source> <target>Glisser && Déposer</target> +<source>Could not initialize directory monitoring:</source> +<target>Impossible d'initialiser la surveillance des dossiers :</target> +<source>Error when monitoring directories.</source> +<target>Erreur lors de la surveillance des répertoires.</target> <source>Conversion error:</source> <target>Erreur de conversion :</target> <source>Error moving file:</source> <target>Erreur lors du déplacement du fichier :</target> -<source>Operation aborted!</source> -<target>Opération abandonnée !</target> <source>Target file already existing!</source> <target>Le fichier de destination existe déjà !</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ La ligne de commande est exécutée chaque fois que : <target>Erreur lors de l'écriture du fichier :</target> <source>Error reading file:</source> <target>Erreur lors de la lecture du fichier :</target> +<source>Operation aborted!</source> +<target>Opération abandonnée !</target> <source>Endless loop when traversing directory:</source> <target>Boucle sans fin lors du parcours du répertoire :</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ La ligne de commande est exécutée chaque fois que : <target>Créer un fichier de commandes pour la synchronisation automatique. Pour démarrer en mode batch il suffit de double-cliquez sur le fichier de commandes ou d'exécuter en ligne de commande: FreeFileSync.exe <fichier de commandes>. Vous pouvez aussi le programmer à l'aide du planificateur de tâches de votre système d'exploitation.</target> <source>Help</source> <target>Aide</target> -<source>Configuration overview:</source> -<target>Contrôle des paramètres :</target> <source>Filter files</source> <target>Filtrage des fichiers</target> -<source>Status feedback</source> -<target>Retour d'informations</target> -<source>Silent mode</source> -<target>Mode silencieux</target> -<source>Start minimized and write status information to a logfile</source> -<target>Démarrage en mode réduit et écriture des informations d'état dans un fichier log</target> <source>Error handling</source> <target>Erreur de gestion de fichiers</target> <source>Overview</source> <target>Présentation</target> -<source>Select logfile directory:</source> -<target>Choisissez un dossier pour le fichier .log :</target> +<source>Status feedback</source> +<target>Retour d'informations</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Nombre maximum de fichiers Log</target> +<source>Select logfile directory:</source> +<target>Choisissez un dossier pour le fichier .log :</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Sauvegarder</target> <source>&Load</source> @@ -695,10 +704,6 @@ Transfert des attributs système des fichiers et des répertoires <target>Paires de dossiers</target> <source>Select view</source> <target>Choisissez une vue</target> -<source>Folder Comparison and Synchronization</source> -<target>Comparaison de dossiers et Synchronisation</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>La Corbeille n'est pas supportée par ce système !</target> <source>Set direction:</source> <target>Choix de la direction :</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Transfert des attributs système des fichiers et des répertoires <target>Voulez-vous enregistrer les modifications dans la configuration actuelle ?</target> <source>Configuration loaded!</source> <target>Configuration chargée !</target> +<source>Folder Comparison and Synchronization</source> +<target>Comparaison de dossiers et Synchronisation</target> <source>Hide files that exist on left side only</source> <target>Masquer les fichiers n'existant qu'à gauche</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Transfert des attributs système des fichiers et des répertoires <target>Directions de la synchronisation par défaut : les anciens fichiers seront remplacés par les nouveaux.</target> <source>The file does not contain a valid configuration:</source> <target>Le fichier ne contient pas de configuration valide :</target> -<source>Scanning:</source> -<target>Lecture en cours :</target> -<source>Encoding extended time information: %x</source> -<target>Codage de l'heure au format étendu : %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Vous pouvez ignorer cette erreur en considérant le répertoire comme vide.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Transfert des attributs système des fichiers et des répertoires <target>Mise à jour des attributs de %x</target> <source>Source directory does not exist anymore:</source> <target>Le répertoire source n'existe plus :</target> -<source>Generating database...</source> -<target>Génération de la base de données...</target> <source>Nothing to synchronize according to configuration!</source> <target>Rien à synchroniser dans cette configuration !</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Transfert des attributs système des fichiers et des répertoires <target>Espace disque nécessaire :</target> <source>Free disk space available:</source> <target>Espace disque disponible :</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Un répertoire faisant partie de paires multiples doit être modifié ! Vérifiez les paramètres de la synchronisation !</target> <source>Processing folder pair:</source> <target>Traitement de la paire de dossiers :</target> +<source>Generating database...</source> +<target>Génération de la base de données...</target> <source>Error copying locked file %x!</source> <target>Erreur lors de la copie du fichier verrouillé %x !</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/german.lng b/BUILD/Languages/german.lng index b9e4ffe2..5aff5c15 100644 --- a/BUILD/Languages/german.lng +++ b/BUILD/Languages/german.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target>Suche Verzeichnis %x...</target> <source>Show in Explorer</source> <target>Im Explorer anzeigen</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Automatisierte Synchronisation</target> <source>Browse</source> <target>Auswählen</target> -<source>Invalid commandline: "%x"</source> -<target>Ungültige Befehlszeile: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Ungültige Befehlszeile: %x</target> <source>Error resolving symbolic link:</source> <target>Fehler beim Auflösen des Symbolischen Links:</target> <source>Show popup</source> @@ -111,8 +113,8 @@ <target>Eine der FreeFileSync Datenbankdateien existiert noch nicht:</target> <source>Error reading from synchronization database:</source> <target>Fehler beim Lesen der Synchronisationsdatenbank:</target> -<source>No matching synchronization session found in database files:</source> -<target></target> +<source>Database files do not share a common synchronization session:</source> +<target>Die Datenbankdateien enthalten keine gemeinsame Synchronisationssitzung:</target> <source>An exception occurred!</source> <target>Eine Ausnahme ist aufgetreten!</target> <source>Error deleting file:</source> @@ -135,6 +137,18 @@ <target>Info</target> <source>Fatal Error</source> <target>Schwerer Fehler</target> +<source>Scanning:</source> +<target>Suche Dateien:</target> +<source>Encoding extended time information: %x</source> +<target>Speichere erweiterte Zeitinformation: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</target> <source>Invalid FreeFileSync config file!</source> <target>Ungültige FreeFileSync Konfigurationsdatei!</target> <source>File does not exist:</source> @@ -229,20 +243,18 @@ Die Befehlszeile wird ausgeführt wenn: <target>Überwachung aktiv...</target> <source>Waiting for missing directories...</source> <target>Warte auf fehlende Verzeichnisse...</target> -<source>Could not initialize directory monitoring:</source> -<target>Die Verzeichnisüberwachung konnte nicht gestartet werden:</target> <source>A directory input field is empty.</source> <target>Ein Verzeichniseingabefeld ist leer.</target> -<source>Error when monitoring directories.</source> -<target>Fehler beim Überwachen der Verzeichnisse.</target> <source>Drag && drop</source> <target>Drag && Drop</target> +<source>Could not initialize directory monitoring:</source> +<target>Die Verzeichnisüberwachung konnte nicht gestartet werden:</target> +<source>Error when monitoring directories.</source> +<target>Fehler beim Überwachen der Verzeichnisse.</target> <source>Conversion error:</source> <target>Fehler bei Konvertierung:</target> <source>Error moving file:</source> <target>Fehler beim Verschieben der Datei:</target> -<source>Operation aborted!</source> -<target>Vorgang abgebrochen!</target> <source>Target file already existing!</source> <target>Die Zieldatei existiert bereits!</target> <source>Error moving directory:</source> @@ -273,6 +285,8 @@ Die Befehlszeile wird ausgeführt wenn: <target>Fehler beim Schreiben der Datei:</target> <source>Error reading file:</source> <target>Fehler beim Lesen der Datei:</target> +<source>Operation aborted!</source> +<target>Vorgang abgebrochen!</target> <source>Endless loop when traversing directory:</source> <target>Endlosschleife beim Lesen des Verzeichnisses:</target> <source>Error traversing directory:</source> @@ -410,27 +424,25 @@ Die Befehlszeile wird ausgeführt wenn: <source>Batch job</source> <target>Batch-Job</target> <source>Create a batch file for automated synchronization. To start in batch mode simply double-click the file or execute via command line: FreeFileSync.exe <batchfile>. This can also be scheduled in your operating system's task planner.</source> -<target>Erzeuge eine Batchdatei für die automatisierte Synchronisation. Um den Batch-Modus zu starten, einfach auf die Datei doppelklicken oder über die Befehlszeile ausführen: FreeFileSync.exe <Batchdatei>. Dies kann auch in den Taskplaner des Betriebssystems eingetragen werden.</target> +<target>Erzeuge eine Batchdatei für die automatisierte Synchronisation. Um den Batchmodus zu starten, einfach auf die Datei doppelklicken oder über die Befehlszeile ausführen: FreeFileSync.exe <Batchdatei>. Dies kann auch in den Taskplaner des Betriebssystems eingetragen werden.</target> <source>Help</source> <target>Hilfe</target> -<source>Configuration overview:</source> -<target>Konfigurationsübersicht:</target> <source>Filter files</source> <target>Dateien filtern</target> -<source>Status feedback</source> -<target>Statusrückmeldung</target> -<source>Silent mode</source> -<target>Stiller Modus</target> -<source>Start minimized and write status information to a logfile</source> -<target>Minimiert starten und Statusinformationen in eine Logdatei schreiben</target> <source>Error handling</source> <target>Fehlerbehandlung</target> <source>Overview</source> <target>Übersicht</target> -<source>Select logfile directory:</source> -<target>Verzeichnis für Logdatei wählen:</target> +<source>Status feedback</source> +<target>Statusrückmeldung</target> +<source>Run minimized</source> +<target>Minimiert ausführen</target> <source>Maximum number of logfiles:</source> <target>Maximale Anzahl an Logdateien:</target> +<source>Select logfile directory:</source> +<target>Verzeichnis für Logdatei wählen:</target> +<source>Batch settings</source> +<target>Batch Einstellungen</target> <source>&Save</source> <target>&Speichern</target> <source>&Load</source> @@ -884,7 +896,7 @@ Transfer file and directory permissions <source>Follow</source> <target>Folgen</target> <source>Integrate external applications into context menu. The following macros are available:</source> -<target>Integriert externe Anwendungen in das Kontextmenu. Die folgenden Makros stehen zur Verfügung:</target> +<target>Integriert externe Anwendungen in das Kontextmenü. Die folgenden Makros stehen zur Verfügung:</target> <source>- full file or directory name</source> <target>- kompletter Datei oder Verzeichnisname</target> <source>- directory part only</source> @@ -939,10 +951,6 @@ Transfer file and directory permissions <target>Setze Standardwerte für Synchronisationsrichtungen: Alte Dateien werden durch neuere überschrieben.</target> <source>The file does not contain a valid configuration:</source> <target>Die Datei enthält keine gültige Konfiguration:</target> -<source>Scanning:</source> -<target>Suche Dateien:</target> -<source>Encoding extended time information: %x</source> -<target>Speichere erweiterte Zeitinformation: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Dieser Fehler kann ignoriert werden, um das Verzeichnis als leer anzusehen.</target> <source>Directory does not exist:</source> @@ -1019,8 +1027,6 @@ Transfer file and directory permissions <target>Aktualisiere Attribute von %x</target> <source>Source directory does not exist anymore:</source> <target>Quellverzeichnis existiert nicht mehr:</target> -<source>Generating database...</source> -<target>Erzeuge Synchronisationsdatenbank...</target> <source>Nothing to synchronize according to configuration!</source> <target>Nichts zu synchronisieren gemäß den aktuellen Einstellungen!</target> <source>Target directory name must not be empty!</source> @@ -1042,11 +1048,13 @@ Transfer file and directory permissions <source>Free disk space available:</source> <target>Verfügbarer freier Speicherplatz:</target> <source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> -<target></target> +<target>Der Papierkorb ist auf nachfolgenden Verzeichnissen nicht verfügbar! Die Dateien werden stattdessen permanent gelöscht:</target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Es wird ein Verzeichnis verändert werden, das Teil mehrerer Verzeichnispaare ist! Bitte die Synchronisationseinstellungen prüfen!</target> <source>Processing folder pair:</source> <target>Bearbeite Verzeichnispaar:</target> +<source>Generating database...</source> +<target>Erzeuge Synchronisationsdatenbank...</target> <source>Error copying locked file %x!</source> <target>Fehler beim Kopieren der gesperrten Datei %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/greek.lng b/BUILD/Languages/greek.lng index 5cd4ca4c..99c02a4e 100644 --- a/BUILD/Languages/greek.lng +++ b/BUILD/Languages/greek.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Εμφάνιση στην Εξερεύνηση</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Αυτοματοποιημένος Συγχρονισμός</target> <source>Browse</source> <target>Αναζήτηση</target> -<source>Invalid commandline: "%x"</source> -<target>Εσφαλμένη εντολή: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Εσφαλμένη εντολή: %x</target> <source>Error resolving symbolic link:</source> <target>Σφάλμα κατά την επίλυση του συμβολικού δεσμού:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Αρχικός συγχρονισμός:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Μια από τις βάσεις δεδομένων του FreeFileSync δεν υπάρχει ακόμα:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Μια από τις εγγραφές στη βάση δεδομένων του FreeFileSync σχετικά με το ακόλουθο αρχείο δεν υπάρχει ακόμη:</target> <source>Error reading from synchronization database:</source> <target>Σφάλμα κατά την ανάγνωση από τη βάση δεδομένων συγχρονισμού:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Παρουσιάστηκε σφάλμα!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Πληροφορίες</target> <source>Fatal Error</source> <target>Σημαντικό Σφάλμα</target> +<source>Scanning:</source> +<target>Ανίχνευση:</target> +<source>Encoding extended time information: %x</source> +<target>Κωδικοποίηση εκτεταμένων πληροφοριών για την ώρα: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Το αρχείο διάταξης του FreeFileSync δεν είναι έγκυρο!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ The command line is executed each time: <target>Ενεργή παρακολούθηση...</target> <source>Waiting for missing directories...</source> <target>Αναμονή για τους υποκαταλόγους που απουσιάζουν...</target> -<source>Could not initialize directory monitoring:</source> -<target>Δεν ήταν δυνατό να γίνει έναρξη παρακολούθησης του υποκαταλόγου:</target> <source>A directory input field is empty.</source> <target>Ένα πεδίο εισόδου είναι κενό.</target> -<source>Error when monitoring directories.</source> -<target>Σφάλμα κατά την παρακολούθηση υποκαταλόγων.</target> <source>Drag && drop</source> <target>Μεταφορά && Απόθεση</target> +<source>Could not initialize directory monitoring:</source> +<target>Δεν ήταν δυνατό να γίνει έναρξη παρακολούθησης του υποκαταλόγου:</target> +<source>Error when monitoring directories.</source> +<target>Σφάλμα κατά την παρακολούθηση υποκαταλόγων.</target> <source>Conversion error:</source> <target>Σφάλμα μετατροπής:</target> <source>Error moving file:</source> <target>Σφάλμα κατά τη μεταφορά του αρχείου:</target> -<source>Operation aborted!</source> -<target>Η λειτουργία ματαιώθηκε!</target> <source>Target file already existing!</source> <target>Το αρχείο προορισμού υπάρχει ήδη!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ The command line is executed each time: <target>Σφάλμα κατά την εγγραφή του αρχείου:</target> <source>Error reading file:</source> <target>Σφάλμα κατά την ανάγνωση του αρχείου:</target> +<source>Operation aborted!</source> +<target>Η λειτουργία ματαιώθηκε!</target> <source>Endless loop when traversing directory:</source> <target>Ατέρμονος βρόχος κατά την την ανάλυση του υποκαταλόγου:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ The command line is executed each time: <target>Για την αυτοματοποίηση του συγχρονισμού δημιουργείστε ένα αρχείο δέσμης ενεργειών. Για να ξεκινήσετε σε κατάσταση λειτουργίας της δέσμης, απλώς κάντε διπλό κλικ στο αρχείο ή γράψτε στη γραμμή εντολών: FreeFileSync.exe <batchfile>. Μπορείτε επίσης το προγραμματίσετε αυτήν την εργασία, χρησιμοποιώντας το χρονοδιάγραμμα εργασιών του λειτουργικού σας συστήματος.</target> <source>Help</source> <target>Βοήθεια</target> -<source>Configuration overview:</source> -<target>Σύνοψη της διάταξης:</target> <source>Filter files</source> <target>Φιλτράρισμα</target> -<source>Status feedback</source> -<target>Αναφορά κατάστασης</target> -<source>Silent mode</source> -<target>Σιωπηλή λειτουργία</target> -<source>Start minimized and write status information to a logfile</source> -<target>Έναρξη σε ελαχιστοποίηση και εγγραφή πληροφοριών κατάστασης σε αρχείο καταγραφής.</target> <source>Error handling</source> <target>Διαχείριση σφαλμάτων</target> <source>Overview</source> <target>Σύνοψη</target> -<source>Select logfile directory:</source> -<target>Επιλέξτε έναν κατάλογο για το αρχείο καταγραφής:</target> +<source>Status feedback</source> +<target>Αναφορά κατάστασης</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Μέγιστος αριθμός αρχείων καταγραφής:</target> +<source>Select logfile directory:</source> +<target>Επιλέξτε έναν κατάλογο για το αρχείο καταγραφής:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Αποθήκευση</target> <source>&Load</source> @@ -695,10 +704,6 @@ Transfer file and directory permissions <target>Ζεύγη υποκαταλόγων</target> <source>Select view</source> <target>Επιλογή εμφάνισης</target> -<source>Folder Comparison and Synchronization</source> -<target>Σύγκριση υποκαταλόγων και Συγχρονισμός</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Ο Κάδος Ανακύκλωσης δεν υποστηρίζεται σε αυτό το σύστημα!</target> <source>Set direction:</source> <target>Επιλογή κατεύθυνσης:</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Transfer file and directory permissions <target>Να αποθηκευτούν οι αλλαγές στην τρέχουσα διάταξη;</target> <source>Configuration loaded!</source> <target>Η διάταξη έχει ανοιχθεί!</target> +<source>Folder Comparison and Synchronization</source> +<target>Σύγκριση υποκαταλόγων και Συγχρονισμός</target> <source>Hide files that exist on left side only</source> <target>Απόκρυψη των αρχείων που υπάρχουν μόνο στα αριστερά</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Transfer file and directory permissions <target>Ρύθμιση προεπιλεγμένης κατεύθυνσης συγχρονισμού: Τα νεότερα αρχεία θα αντικαταστήσουν τα παλιότερα.</target> <source>The file does not contain a valid configuration:</source> <target>Το αρχείο δεν περιέχει μια έγκυρη διάταξη:</target> -<source>Scanning:</source> -<target>Ανίχνευση:</target> -<source>Encoding extended time information: %x</source> -<target>Κωδικοποίηση εκτεταμένων πληροφοριών για την ώρα: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Μπορείτε να αγνοήσετε αυτό το σφάλμα και να θεωρήσετε τον υποκατάλογο κενό.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Transfer file and directory permissions <target>Ενημέρωση των χαρακτηριστικών αρχείου του %x</target> <source>Source directory does not exist anymore:</source> <target>Ο υποκατάλογος προέλευσης δεν υπάρχει πλέον:</target> -<source>Generating database...</source> -<target>Δημιουργία βάσης δεδομένων...</target> <source>Nothing to synchronize according to configuration!</source> <target>Τίποτα προς συγχρονισμό με βάση τη διάταξη!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Transfer file and directory permissions <target>Απαιτούμενος ελεύθερος χώρος δίσκου:</target> <source>Free disk space available:</source> <target>Διαθέσιμος ελεύθερος χώρος δίσκου:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Θα τροποποιηθεί ένας υποκατάλογος που ανήκει σε πολλαπλά ζεύγη υποκαταλόγων! Παρακαλώ αναθεωρείστε τις ρυθμίσεις συγχρονισμού.</target> <source>Processing folder pair:</source> <target>Σύγκριση του ζεύγους υποκαταλόγων:</target> +<source>Generating database...</source> +<target>Δημιουργία βάσης δεδομένων...</target> <source>Error copying locked file %x!</source> <target>Σφάλμα κατά την αντιγραφή του κλειδωμένου αρχείου %x !</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/hebrew.lng b/BUILD/Languages/hebrew.lng index 22de79f5..7e26c0fb 100644 --- a/BUILD/Languages/hebrew.lng +++ b/BUILD/Languages/hebrew.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>הראה בסייר הקבצים</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - סינכרון אוטומטי</target> <source>Browse</source> <target>עיין</target> -<source>Invalid commandline: "%x"</source> -<target>שורת פקודות שגויה: "%x"</target> +<source>Invalid commandline: %x</source> +<target>שורת פקודות שגויה: %x</target> <source>Error resolving symbolic link:</source> <target>שגיאה בפענוח קישור סימבולי (Symbolic Link)</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>סנכרון ראשוני:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>אחד מקובצי בסיס הנתונים הבא עדיין לא קיים</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>אחת מהכניסות בבסיס הנתונים בקובץ הבא עדיין לא קיימת</target> <source>Error reading from synchronization database:</source> <target>שגיאה בקריאה מבסיס הנתונים של הסנכרון:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>אירוע חריג!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>מידע</target> <source>Fatal Error</source> <target>שגיאה פטלית</target> +<source>Scanning:</source> +<target>סורק:</target> +<source>Encoding extended time information: %x</source> +<target>מקודד אינפורמצית זמן מורחבת: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>קובץ תצורה בלתי חוקי!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ The command line is executed each time: <target>נטר ארכיב...</target> <source>Waiting for missing directories...</source> <target>מחכה למחיצות חסרות...</target> -<source>Could not initialize directory monitoring:</source> -<target>לא יכול לאתחל ניטור מחיצה:</target> <source>A directory input field is empty.</source> <target>שדה כניסת מחיצה ריק.</target> -<source>Error when monitoring directories.</source> -<target>שגיאה בזמן ניטור מחיצות.</target> <source>Drag && drop</source> <target>גרור והשלך</target> +<source>Could not initialize directory monitoring:</source> +<target>לא יכול לאתחל ניטור מחיצה:</target> +<source>Error when monitoring directories.</source> +<target>שגיאה בזמן ניטור מחיצות.</target> <source>Conversion error:</source> <target>שגיאה בהסבה:</target> <source>Error moving file:</source> <target>שגיאה בהעברת קובץ:</target> -<source>Operation aborted!</source> -<target>הפעולה בוטלה!</target> <source>Target file already existing!</source> <target>קובץ מטרה כבר קיים!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ The command line is executed each time: <target>שגיאה בכתיבת קובץ:</target> <source>Error reading file:</source> <target>שגיאה בקריאת קובץ:</target> +<source>Operation aborted!</source> +<target>הפעולה בוטלה!</target> <source>Endless loop when traversing directory:</source> <target>נוצרת לולאה אינסופית בחצית מחיצות</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ The command line is executed each time: <target>יצור קובץ אצווה לסנכרון אוטומטי. להפעלה באצווה העבר את שם הקובץ כפרמטר לתוכנה FreeFileSync.exe <batchfile>. אפשר גם להשתמש במנהל המשימות המתוזמנות של המערכת.</target> <source>Help</source> <target>עזרה</target> -<source>Configuration overview:</source> -<target>סקירת תצורה:</target> <source>Filter files</source> <target>קבצי המסנן</target> -<source>Status feedback</source> -<target>משוב מצב</target> -<source>Silent mode</source> -<target>אופן פעולה שקט</target> -<source>Start minimized and write status information to a logfile</source> -<target>הפעל במצב ממוזער ורשום מצב לקובץ יומן</target> <source>Error handling</source> <target>טיפול בשגיאות</target> <source>Overview</source> <target>מבט כללי</target> -<source>Select logfile directory:</source> -<target>בחר מחיצה לקבצי יומן</target> +<source>Status feedback</source> +<target>משוב מצב</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>מספר מכסימלי של קבצי יומן</target> +<source>Select logfile directory:</source> +<target>בחר מחיצה לקבצי יומן</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&שמירה</target> <source>&Load</source> @@ -695,10 +704,6 @@ Transfer file and directory permissions <target>זוגות מחיצות</target> <source>Select view</source> <target>בחר תצוגה</target> -<source>Folder Comparison and Synchronization</source> -<target>סנכרון קבצים ומחיצות</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>סל המחזור לא נתמך כרגע במערכת הפעלה זו!</target> <source>Set direction:</source> <target>בחר כוון:</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Transfer file and directory permissions <target>שמור שינויים לתצורה נוכחית?</target> <source>Configuration loaded!</source> <target>תצורה הוטענה!</target> +<source>Folder Comparison and Synchronization</source> +<target>סנכרון קבצים ומחיצות</target> <source>Hide files that exist on left side only</source> <target>הסתר קבצים הקימים אך ורק בצד שמאל</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Transfer file and directory permissions <target>בחר ברירת מחדל של סנכרון: קבצים ישנים ידרסו ע"י קבצים חדשים יותר.</target> <source>The file does not contain a valid configuration:</source> <target>קובץ זה לא מכיל קונפיגורציה ברת תוקף</target> -<source>Scanning:</source> -<target>סורק:</target> -<source>Encoding extended time information: %x</source> -<target>מקודד אינפורמצית זמן מורחבת: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>אפשר להתעלם משגיאה זו ולהניח כי המחיצה ריקה.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Transfer file and directory permissions <target>מעדכן תכונות של %x</target> <source>Source directory does not exist anymore:</source> <target>מחיצת המקור לא נמצאת:</target> -<source>Generating database...</source> -<target>מיצר בסיס נתונים...</target> <source>Nothing to synchronize according to configuration!</source> <target>אין מה לסנכרן ע"פ הקונפיגורציה!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Transfer file and directory permissions <target>מקום דיסק פנוי נדרש:</target> <source>Free disk space available:</source> <target>מקום פנוי בדיסק:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>מחיצה שתשתנה היא חלק מריבוי זוגות מחיצות! בבקשה בדוק הגדרות סינכרון!</target> <source>Processing folder pair:</source> <target>מבצע זוג מחיצות:</target> +<source>Generating database...</source> +<target>מיצר בסיס נתונים...</target> <source>Error copying locked file %x!</source> <target>שגיאה בהעתקת קובץ נעול %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/hungarian.lng b/BUILD/Languages/hungarian.lng index 2ab1e43f..06e05ba4 100644 --- a/BUILD/Languages/hungarian.lng +++ b/BUILD/Languages/hungarian.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Mutatás az Intézőben</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Automatikus szinkronizálás</target> <source>Browse</source> <target>Tallózás</target> -<source>Invalid commandline: "%x"</source> -<target>Érvénytelen parancssor: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Érvénytelen parancssor: %x</target> <source>Error resolving symbolic link:</source> <target>A szimbolikus link feloldása sikertelen:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Első szinkronizáció:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>A FreeFileSync egyik adatbázisfájlja nem létezik:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>A FreeFileSync a következő fájlban lévő egyik adatbázisbejegyzése nem található:</target> <source>Error reading from synchronization database:</source> <target>Hiba történt a szinkronizációs adatbázis olvasása közben:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Kivétel keletkezett!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Információ</target> <source>Fatal Error</source> <target>Kritikus hiba</target> +<source>Scanning:</source> +<target>Vizsgálat:</target> +<source>Encoding extended time information: %x</source> +<target>Kibővített időinformációk kódolása: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Érvénytelen a FreeFileSync beállításait tartalmazó fájl!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ A parancssor végrehajtódik minden alkalommal, ha: <target>Figyelés aktív...</target> <source>Waiting for missing directories...</source> <target>Várakozás a hiányzó mappákra...</target> -<source>Could not initialize directory monitoring:</source> -<target>A mappafigyelés inicializálása sikertelen:</target> <source>A directory input field is empty.</source> <target>Valamelyik mappa megadására szolgáló mező üres.</target> -<source>Error when monitoring directories.</source> -<target>Hiba történt a mappák figyelése közben.</target> <source>Drag && drop</source> <target>Húzd && Ejtsd</target> +<source>Could not initialize directory monitoring:</source> +<target>A mappafigyelés inicializálása sikertelen:</target> +<source>Error when monitoring directories.</source> +<target>Hiba történt a mappák figyelése közben.</target> <source>Conversion error:</source> <target>Konverziós hiba:</target> <source>Error moving file:</source> <target>Hiba a fájl mozgatásakor:</target> -<source>Operation aborted!</source> -<target>Művelet megszakítva!</target> <source>Target file already existing!</source> <target>A célként megadott fájl már létezik!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ A parancssor végrehajtódik minden alkalommal, ha: <target>A fájl írása sikertelen:</target> <source>Error reading file:</source> <target>A fájl olvasása sikertelen:</target> +<source>Operation aborted!</source> +<target>Művelet megszakítva!</target> <source>Endless loop when traversing directory:</source> <target>Végtelen hurok a mappák bejárásakor:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ A parancssor végrehajtódik minden alkalommal, ha: <target>Kötegelt feladat fájl létrehozása az automatizált szinkronizációhoz. Kötegelt feladat módban való indításhoz csak simán duplát kell kattintani a fájlon vagy a parancssorból kell futtatni: FreeFileSync.exe <kötegelt feladat fájl>.futtasd egyszerűen meg kell adni a fájl nevét a FreeFileSync.exe-nek: FreeFileSync.exe <kötegelt feladat fájl>. Ezt ütemezni is lehet az operációs rendszer feladatkezelőjével.</target> <source>Help</source> <target>Súgó</target> -<source>Configuration overview:</source> -<target>Beállítások áttekintése:</target> <source>Filter files</source> <target>Fájlok szűrése</target> -<source>Status feedback</source> -<target>Státusz visszajelzés</target> -<source>Silent mode</source> -<target>Csendes mód</target> -<source>Start minimized and write status information to a logfile</source> -<target>Indítás lekicsinyítve és a státuszinformációk mentése naplófájlba</target> <source>Error handling</source> <target>Hibakezelés</target> <source>Overview</source> <target>Összefoglaló</target> -<source>Select logfile directory:</source> -<target>Naplófájl mappájának kiválasztása:</target> +<source>Status feedback</source> +<target>Státusz visszajelzés</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Naplófájlok maximális száma:</target> +<source>Select logfile directory:</source> +<target>Naplófájl mappájának kiválasztása:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Mentés</target> <source>&Load</source> @@ -698,10 +707,6 @@ Fájlok és mappák jogosultságainak átvitele <target>Mappa párok</target> <source>Select view</source> <target>Nézet kiválasztása</target> -<source>Folder Comparison and Synchronization</source> -<target>Mappa összehasonlítás és szinkronizáció</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>A Lomtár (Recycle Bin) nem támogatott ezen a rendszeren!</target> <source>Set direction:</source> <target>Irány</target> <source>Exclude temporarily</source> @@ -736,6 +741,8 @@ Fájlok és mappák jogosultságainak átvitele <target>Mentsük a beállítások változásait?</target> <source>Configuration loaded!</source> <target>Beállítások betöltve!</target> +<source>Folder Comparison and Synchronization</source> +<target>Mappa összehasonlítás és szinkronizáció</target> <source>Hide files that exist on left side only</source> <target>Csak a bal oldalon létező fájlok elrejtése</target> <source>Show files that exist on left side only</source> @@ -944,10 +951,6 @@ Fájlok és mappák jogosultságainak átvitele <target>Alapértelmezett szinkronizációs irányok beállítása: a régebbi fájlok felülíródnak az újabbakkal.</target> <source>The file does not contain a valid configuration:</source> <target>A következő fájl nem tartalmaz érvényes beállításokat:</target> -<source>Scanning:</source> -<target>Vizsgálat:</target> -<source>Encoding extended time information: %x</source> -<target>Kibővített időinformációk kódolása: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Figyelmen kívül hagyhatod ezt a hibát, üresnek tekintve a mappát.</target> <source>Directory does not exist:</source> @@ -1024,8 +1027,6 @@ Fájlok és mappák jogosultságainak átvitele <target>A(z) %x attribútumainak frissítése</target> <source>Source directory does not exist anymore:</source> <target>A forrásként megadott mappa többé nem létezik:</target> -<source>Generating database...</source> -<target>Adatbázis generálása...</target> <source>Nothing to synchronize according to configuration!</source> <target>A beállításoknak megfelelően nincs mit szinkronizálni!</target> <source>Target directory name must not be empty!</source> @@ -1046,10 +1047,14 @@ Fájlok és mappák jogosultságainak átvitele <target>Szükséges szabad lemezterület:</target> <source>Free disk space available:</source> <target>Szabad lemezterület:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Egy olyan mappa kerül módosításra, amely tagja több mappa párosnak! Kérjük, nézd át a szinkronizációs beállításokat!</target> <source>Processing folder pair:</source> <target>Mappapár feldolgozása:</target> +<source>Generating database...</source> +<target>Adatbázis generálása...</target> <source>Error copying locked file %x!</source> <target>Hiba történt a(z) %x zárolt fájl másolása közben!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/italian.lng b/BUILD/Languages/italian.lng index 32324996..77c8d6b4 100644 --- a/BUILD/Languages/italian.lng +++ b/BUILD/Languages/italian.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Mostra in Esplora Risorse</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Sincronizzazione Automatizzata</target> <source>Browse</source> <target>Sfoglia</target> -<source>Invalid commandline: "%x"</source> -<target>Comando non valido: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Comando non valido: %x</target> <source>Error resolving symbolic link:</source> <target>Errore nella risoluzione di collegamento simbolico:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Prima sincronizzazione:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Un file database di FreeFileSync non è ancora stato creato:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Una voce del database di FreeFileSync nel seguente file non e' ancora stata creata:</target> <source>Error reading from synchronization database:</source> <target>Errore in lettura dal database di sincronizzione:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Si è verificato un problema!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Errore Fatale</target> +<source>Scanning:</source> +<target>Analisi in corso:</target> +<source>Encoding extended time information: %x</source> +<target>Codifica estesa informazioni orario: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>File di configurazione FreeFileSync non valido!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ La linea di comando è eseguita ogni volta che: <target>Monitoraggio attivo...</target> <source>Waiting for missing directories...</source> <target>In attesa delle directory mancanti...</target> -<source>Could not initialize directory monitoring:</source> -<target>Monitoraggio directory non inizializzabile:</target> <source>A directory input field is empty.</source> <target>Un campo directory di input è vuoto.</target> -<source>Error when monitoring directories.</source> -<target>Errore durante il monitoraggio directory.</target> <source>Drag && drop</source> <target>Drag && drop</target> +<source>Could not initialize directory monitoring:</source> +<target>Monitoraggio directory non inizializzabile:</target> +<source>Error when monitoring directories.</source> +<target>Errore durante il monitoraggio directory.</target> <source>Conversion error:</source> <target>Errore di conversione:</target> <source>Error moving file:</source> <target>Errore nello spostamento file:</target> -<source>Operation aborted!</source> -<target>Operazione abortita!</target> <source>Target file already existing!</source> <target>File destinazione già esistente!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ La linea di comando è eseguita ogni volta che: <target>Errore durante la scrittura del file:</target> <source>Error reading file:</source> <target>Errore durante la lettura del file:</target> +<source>Operation aborted!</source> +<target>Operazione abortita!</target> <source>Endless loop when traversing directory:</source> <target>Loop senza fine attraverso le directory:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ La linea di comando è eseguita ogni volta che: <target>Crea un file batch per sincronizzazione automatica. Per partire in modalità batch effettua semplicemente doppio click sul file o esegui da linea di comando: FreeFileSync.exe <batchfile>. Puoi anche schedulare il batch nelle operazioni pianificate del tuo sistema operativo.</target> <source>Help</source> <target>Aiuto</target> -<source>Configuration overview:</source> -<target>Controllo configurazione:</target> <source>Filter files</source> <target>Filtro dei files</target> -<source>Status feedback</source> -<target>Feedback di stato</target> -<source>Silent mode</source> -<target>Modalità Silenziosa</target> -<source>Start minimized and write status information to a logfile</source> -<target>Lancia minimizzato e scrivi informazioni di stato in un file log</target> <source>Error handling</source> <target>Gestione degli errori</target> <source>Overview</source> <target>Controllo generale</target> -<source>Select logfile directory:</source> -<target>Seleziona cartella per il file di log:</target> +<source>Status feedback</source> +<target>Feedback di stato</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Numero massimo di logfiles:</target> +<source>Select logfile directory:</source> +<target>Seleziona cartella per il file di log:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Salva</target> <source>&Load</source> @@ -694,10 +703,6 @@ Trasferisci file e permessi sulle cartelle <target>Coppia di cartelle</target> <source>Select view</source> <target>Seleziona vista</target> -<source>Folder Comparison and Synchronization</source> -<target>Comparazione di Cartelle e Sincronizzazione</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Cestino non ancora supportato per questo sistema!</target> <source>Set direction:</source> <target>Imposta direzione:</target> <source>Exclude temporarily</source> @@ -732,6 +737,8 @@ Trasferisci file e permessi sulle cartelle <target>Salvare i cambiamenti alla configurazione corrente?</target> <source>Configuration loaded!</source> <target>Configurazione caricata!</target> +<source>Folder Comparison and Synchronization</source> +<target>Comparazione di Cartelle e Sincronizzazione</target> <source>Hide files that exist on left side only</source> <target>Nascondi i file esistenti solo a sinistra</target> <source>Show files that exist on left side only</source> @@ -940,10 +947,6 @@ Trasferisci file e permessi sulle cartelle <target>Imposta direzioni di sincronizzazione di default: I vecchi file saranno sovrascritti dai nuovi.</target> <source>The file does not contain a valid configuration:</source> <target>Il file non contiene una configurazione valida</target> -<source>Scanning:</source> -<target>Analisi in corso:</target> -<source>Encoding extended time information: %x</source> -<target>Codifica estesa informazioni orario: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Puoi ignorare questo errore per considerare la directory come vuota.</target> <source>Directory does not exist:</source> @@ -1020,8 +1023,6 @@ Trasferisci file e permessi sulle cartelle <target>Aggiornamento attributi di %x</target> <source>Source directory does not exist anymore:</source> <target>La directory sorgente non è più esistente:</target> -<source>Generating database...</source> -<target>Generazione database...</target> <source>Nothing to synchronize according to configuration!</source> <target>Niente da sincronizzare in questa configurazione!</target> <source>Target directory name must not be empty!</source> @@ -1042,10 +1043,14 @@ Trasferisci file e permessi sulle cartelle <target>Spazio libero su disco richiesto:</target> <source>Free disk space available:</source> <target>Spazio libero su disco disponibile:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Una directory che fa parte di più coppie di cartelle sarà modificata! Ricontrolla le impostazioni di sincronizzazione!</target> <source>Processing folder pair:</source> <target>Elaborazione coppia di cartelle:</target> +<source>Generating database...</source> +<target>Generazione database...</target> <source>Error copying locked file %x!</source> <target>Errore durante la copia del file bloccato %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/japanese.lng b/BUILD/Languages/japanese.lng index a5fd933c..2530420e 100644 --- a/BUILD/Languages/japanese.lng +++ b/BUILD/Languages/japanese.lng @@ -7,6 +7,8 @@ <plural definition>0</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>エクスプローラで表示</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>リアルタイム同期 - 自動同期</target> <source>Browse</source> <target>参照</target> -<source>Invalid commandline: "%x"</source> -<target>無効なコマンドライン: "%x"</target> +<source>Invalid commandline: %x</source> +<target>無効なコマンドライン: %x</target> <source>Error resolving symbolic link:</source> <target>シンボリックリンクの解決に失敗:</target> <source>Show popup</source> @@ -108,10 +110,10 @@ <target>同期処理の初期化:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>FreeFileSync データベースファイルが存在しません:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>FreeFileSync データベースに、以下のファイルに関するエントリは存在しません:</target> <source>Error reading from synchronization database:</source> <target>同期データベースからの読み込みエラー:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>例外が発生しました!</target> <source>Error deleting file:</source> @@ -133,6 +135,15 @@ <target>情報</target> <source>Fatal Error</source> <target>致命的なエラー</target> +<source>Scanning:</source> +<target>スキャン:</target> +<source>Encoding extended time information: %x</source> +<target>拡張された時間情報のエンコーディング: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>無効な FreeFileSync 構成ファイルです!</target> <source>File does not exist:</source> @@ -224,20 +235,18 @@ The command line is executed each time: <target>監視を開始します...</target> <source>Waiting for missing directories...</source> <target>見失ったディレクトリの待機中</target> -<source>Could not initialize directory monitoring:</source> -<target>監視するディレクトリを初期化できません:</target> <source>A directory input field is empty.</source> <target>ディレクトリが入力されていません</target> -<source>Error when monitoring directories.</source> -<target>ディレクトリの監視エラー</target> <source>Drag && drop</source> <target>ドラッグ && ドロップ</target> +<source>Could not initialize directory monitoring:</source> +<target>監視するディレクトリを初期化できません:</target> +<source>Error when monitoring directories.</source> +<target>ディレクトリの監視エラー</target> <source>Conversion error:</source> <target>変換エラー:</target> <source>Error moving file:</source> <target>ファイルの移動に失敗:</target> -<source>Operation aborted!</source> -<target>操作の中断!</target> <source>Target file already existing!</source> <target>対象ファイルは既に存在します!</target> <source>Error moving directory:</source> @@ -268,6 +277,8 @@ The command line is executed each time: <target>ファイル書き込みエラー:</target> <source>Error reading file:</source> <target>ファイル読み込みエラー:</target> +<source>Operation aborted!</source> +<target>操作の中断!</target> <source>Endless loop when traversing directory:</source> <target>ディレクトリ移動中に無限ループが発生:</target> <source>Error traversing directory:</source> @@ -408,24 +419,22 @@ The command line is executed each time: <target>同期を一括で自動的に実行するためのバッチファイルを作成します。実行は.BAT ファイルをダブルクリック、または次のコマンドラインを実行します: FreeFileSync.exe<batchfile>.BAT ファイルは、OS のタスクスケジューラで実行することも可能です</target> <source>Help</source> <target>ヘルプ</target> -<source>Configuration overview:</source> -<target>構成設定の概要:</target> <source>Filter files</source> <target>ファイルフィルター</target> -<source>Status feedback</source> -<target>状態 フィードバック</target> -<source>Silent mode</source> -<target>サイレントモード</target> -<source>Start minimized and write status information to a logfile</source> -<target>最小化状態で起動して、ステータス情報をログファイルに書き出す</target> <source>Error handling</source> <target>ハンドリングのエラー時:</target> <source>Overview</source> <target>概要</target> -<source>Select logfile directory:</source> -<target>ログファイルの保存先を選択:</target> +<source>Status feedback</source> +<target>状態 フィードバック</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>ログファイルの最大数:</target> +<source>Select logfile directory:</source> +<target>ログファイルの保存先を選択:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>保存(&S)</target> <source>&Load</source> @@ -690,10 +699,6 @@ Transfer file and directory permissions <target>フォルダ・ペア</target> <source>Select view</source> <target>表示選択</target> -<source>Folder Comparison and Synchronization</source> -<target>フォルダの比較と同期</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>このシステムでは、ゴミ箱に対応していません!</target> <source>Set direction:</source> <target>方向の設定:</target> <source>Exclude temporarily</source> @@ -728,6 +733,8 @@ Transfer file and directory permissions <target>現在の構成の変更を保存しますか?</target> <source>Configuration loaded!</source> <target>構成設定を読み込み中!</target> +<source>Folder Comparison and Synchronization</source> +<target>フォルダの比較と同期</target> <source>Hide files that exist on left side only</source> <target>左側のみに存在するファイルを非表示</target> <source>Show files that exist on left side only</source> @@ -930,10 +937,6 @@ Transfer file and directory permissions <target>同期方向のデフォルト設定: 古いファイルに新しいファイルを上書き</target> <source>The file does not contain a valid configuration:</source> <target>このファイルには有効な構成が含まれていません:</target> -<source>Scanning:</source> -<target>スキャン:</target> -<source>Encoding extended time information: %x</source> -<target>拡張された時間情報のエンコーディング: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>ディレクトリが空と考慮する場合、このエラーは無視できます。</target> <source>Directory does not exist:</source> @@ -1010,8 +1013,6 @@ Transfer file and directory permissions <target>%x の属性を更新</target> <source>Source directory does not exist anymore:</source> <target>ソースディレクトリが存在しません:</target> -<source>Generating database...</source> -<target>データベースを作成中...</target> <source>Nothing to synchronize according to configuration!</source> <target>構成設定に対応する同期がみつかりません!</target> <source>Target directory name must not be empty!</source> @@ -1032,10 +1033,14 @@ Transfer file and directory permissions <target>必要なディスク空き容量:</target> <source>Free disk space available:</source> <target>利用可能なディスク空き容量:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>複数ペアのディレクトリ設定部分に変更された箇所があります! 同期設定を再確認してみてください!</target> <source>Processing folder pair:</source> <target>フォルダペアを処理中:</target> +<source>Generating database...</source> +<target>データベースを作成中...</target> <source>Error copying locked file %x!</source> <target>コピーに失敗! ファイル%x はロックされています!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/korean.lng b/BUILD/Languages/korean.lng index a476f52c..50ccc158 100644 --- a/BUILD/Languages/korean.lng +++ b/BUILD/Languages/korean.lng @@ -7,6 +7,8 @@ <plural definition>0</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>탐색기에 표시</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>실시간 동기화 - 자동 동기화</target> <source>Browse</source> <target>찾아보기</target> -<source>Invalid commandline: "%x"</source> -<target>잘못된 커맨드라인</target> +<source>Invalid commandline: %x</source> +<target></target> <source>Error resolving symbolic link:</source> <target>심볼릭 링크를 해결하던 중 발생한 오류 :</target> <source>Show popup</source> @@ -108,10 +110,10 @@ <target>초기 동기화 :</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>FreeFileSync 데이터베이스 파일 중 하나가 아직 존재하지 않습니다 :</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>다음 파일 내에 있는 FreeFileSync 데이터베이스 엔트리 중 하나가 아직 존재하지 않습니다 :</target> <source>Error reading from synchronization database:</source> <target>동기화 데이터베이스로부터 읽어 들이던 중 발생한 오류 :</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>예외 발생!</target> <source>Error deleting file:</source> @@ -133,6 +135,15 @@ <target>정보</target> <source>Fatal Error</source> <target>치명적 오류</target> +<source>Scanning:</source> +<target>스캔 :</target> +<source>Encoding extended time information: %x</source> +<target>인코딩 확장 시간 정보 : %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>잘못된 FreeFileSync 설정 파일!</target> <source>File does not exist:</source> @@ -224,20 +235,18 @@ The command line is executed each time: <target>모니터링 활성화...</target> <source>Waiting for missing directories...</source> <target>누락 디렉토리 대기 중...</target> -<source>Could not initialize directory monitoring:</source> -<target>디렉토리 모니터링을 초기화 할 수 없습니다 :</target> <source>A directory input field is empty.</source> <target>디렉토리 입력 필드가 비어 있습니다.</target> -<source>Error when monitoring directories.</source> -<target>디렉토리 모니터링 중 발생한 오류 :</target> <source>Drag && drop</source> <target>드래그 && 드랍</target> +<source>Could not initialize directory monitoring:</source> +<target>디렉토리 모니터링을 초기화 할 수 없습니다 :</target> +<source>Error when monitoring directories.</source> +<target>디렉토리 모니터링 중 발생한 오류 :</target> <source>Conversion error:</source> <target>변환 오류 :</target> <source>Error moving file:</source> <target>파일 이동 중 발생한 오류 :</target> -<source>Operation aborted!</source> -<target>작업 중단!</target> <source>Target file already existing!</source> <target>대상 파일이 이미 존재합니다!</target> <source>Error moving directory:</source> @@ -268,6 +277,8 @@ The command line is executed each time: <target>파일을 쓰던 중 발생한 오류 :</target> <source>Error reading file:</source> <target>파일을 읽던 중 발생한 오류 :</target> +<source>Operation aborted!</source> +<target>작업 중단!</target> <source>Endless loop when traversing directory:</source> <target>디렉토리 이동 중 무한 루프 발생 :</target> <source>Error traversing directory:</source> @@ -408,24 +419,22 @@ The command line is executed each time: <target>자동 동기화를 위한 일괄파일 생성. 일괄모드 작업은 해당 파일을 더블클릭 하거나 커맨드라인: FreeFileSync.exe <일괄파일>을 통해 실행 가능합니다. 또한 운영체제(O/S)의 작업관리자에서도 예약할 수 있습니다.</target> <source>Help</source> <target>도움말</target> -<source>Configuration overview:</source> -<target>구성설정 개요 :</target> <source>Filter files</source> <target>파일 필터</target> -<source>Status feedback</source> -<target>상태 피드백</target> -<source>Silent mode</source> -<target>사일런트 모드</target> -<source>Start minimized and write status information to a logfile</source> -<target>최소화 상태로 시작하고, 로그파일에 상태 정보를 기록</target> <source>Error handling</source> <target>오류 발생시 :</target> <source>Overview</source> <target>개요</target> -<source>Select logfile directory:</source> -<target>로그파일 디렉토리 선택 :</target> +<source>Status feedback</source> +<target>상태 피드백</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>최대 로그파일 개수 :</target> +<source>Select logfile directory:</source> +<target>로그파일 디렉토리 선택 :</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>저장(&S)</target> <source>&Load</source> @@ -688,10 +697,6 @@ Transfer file and directory permissions <target>폴더 페어(짝)</target> <source>Select view</source> <target>보기 선택</target> -<source>Folder Comparison and Synchronization</source> -<target>폴더 비교 및 동기화</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>본 시스템에서는 휴지통을 아직 지원하지 않습니다.</target> <source>Set direction:</source> <target>방향 설정 :</target> <source>Exclude temporarily</source> @@ -726,6 +731,8 @@ Transfer file and directory permissions <target>현재 설정의 변경 내용을 저장하시겠습니까?</target> <source>Configuration loaded!</source> <target>설정 로드 완료!</target> +<source>Folder Comparison and Synchronization</source> +<target>폴더 비교 및 동기화</target> <source>Hide files that exist on left side only</source> <target>좌측에만 존재하는 파일 숨기기</target> <source>Show files that exist on left side only</source> @@ -928,10 +935,6 @@ Transfer file and directory permissions <target>기본값 동기화 방향 설정 : 이전 파일들은 신규 파일들로 덮어 쓰여집니다.</target> <source>The file does not contain a valid configuration:</source> <target>이 파일은 유효한 설정 값을 갖고 있지 않습니다 :</target> -<source>Scanning:</source> -<target>스캔 :</target> -<source>Encoding extended time information: %x</source> -<target>인코딩 확장 시간 정보 : %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>디렉토리가 비었다는 오류는 무시 가능합니다.</target> <source>Directory does not exist:</source> @@ -1008,8 +1011,6 @@ Transfer file and directory permissions <target>%x 속성 업데이트</target> <source>Source directory does not exist anymore:</source> <target>소스 디렉토리가 더 이상 존재하지 않습니다 :</target> -<source>Generating database...</source> -<target>데이터베이스 생성 중...</target> <source>Nothing to synchronize according to configuration!</source> <target>구성 설정에 따라 동기화 할 내용이 없습니다!</target> <source>Target directory name must not be empty!</source> @@ -1030,10 +1031,14 @@ Transfer file and directory permissions <target>필요한 디스크 여유 공간 :</target> <source>Free disk space available:</source> <target>사용 가능한 디스크 여유 공간 :</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>다중폴더 페어의 일부인 디렉토리가 변경됩니다. 동기화 설정을 재검토해 주세요.</target> <source>Processing folder pair:</source> <target>폴더 페어 처리 중 :</target> +<source>Generating database...</source> +<target>데이터베이스 생성 중...</target> <source>Error copying locked file %x!</source> <target>복사 실패! 파일 %x 에 락이 걸려 있습니다.</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/polish.lng b/BUILD/Languages/polish.lng index a4fec878..d815e09c 100644 --- a/BUILD/Languages/polish.lng +++ b/BUILD/Languages/polish.lng @@ -7,6 +7,8 @@ <plural definition>n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Wyświetl w explorerze</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Automatyczna Synchronizacja</target> <source>Browse</source> <target>Przeglądaj</target> -<source>Invalid commandline: "%x"</source> -<target>Nieprawidłowa komenda: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Nieprawidłowa komenda: %x</target> <source>Error resolving symbolic link:</source> <target>Błąd odczytu dowiązania symbolicznego:</target> <source>Show popup</source> @@ -110,10 +112,10 @@ <target>Wstępna synchronizacja:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Jeden z plików bazy danych FreeFileSync nie istnieje:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Jeden z wpisów bazy danych FreeFileSync dotyczący pliku nie istnieje:</target> <source>Error reading from synchronization database:</source> <target>Błąd odczytu z bazy danych synchronizacji:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Wystąpił wyjątek!</target> <source>Error deleting file:</source> @@ -137,6 +139,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Błąd krytyczny</target> +<source>Scanning:</source> +<target>Skanowanie:</target> +<source>Encoding extended time information: %x</source> +<target>Odkodowywanie rozszerzonych informacji o czasie: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Nieprawidłowy plik konfiguracyjny!</target> <source>File does not exist:</source> @@ -234,20 +245,18 @@ Komenda jest wykonwywana za każdym razem gdy: <target>Monitorowanie aktywne...</target> <source>Waiting for missing directories...</source> <target>Oczekiwanie na brakujące katalogi...</target> -<source>Could not initialize directory monitoring:</source> -<target>Nie można uruchomić monitora katalogów:</target> <source>A directory input field is empty.</source> <target>Pole ze ścieżką katalogu jest puste.</target> -<source>Error when monitoring directories.</source> -<target>Błąd podczas monitorowania katalogów.</target> <source>Drag && drop</source> <target>Drag && Drop</target> +<source>Could not initialize directory monitoring:</source> +<target>Nie można uruchomić monitora katalogów:</target> +<source>Error when monitoring directories.</source> +<target>Błąd podczas monitorowania katalogów.</target> <source>Conversion error:</source> <target>Błąd konwersji:</target> <source>Error moving file:</source> <target>Błąd podczas przenoszenia pliku:</target> -<source>Operation aborted!</source> -<target>Operacja przerwana!</target> <source>Target file already existing!</source> <target>Plik docelowy już istnieje!</target> <source>Error moving directory:</source> @@ -278,6 +287,8 @@ Komenda jest wykonwywana za każdym razem gdy: <target>Błąd zapisu pliku:</target> <source>Error reading file:</source> <target>Błąd odczytu pliku:</target> +<source>Operation aborted!</source> +<target>Operacja przerwana!</target> <source>Endless loop when traversing directory:</source> <target>Zapętlenie podczas przeglądania katalogu:</target> <source>Error traversing directory:</source> @@ -418,24 +429,22 @@ Komenda jest wykonwywana za każdym razem gdy: <target>Utwórz plik wsadowy dla automatycznej synchronizacji. Aby uruchomić synchronizację w trybie wsadowym po prostu kliknij dwa razy na plik lub uruchom go z konsoli: FreeFileSync.exe <plikwsadowy>. Możesz to dodać do programu planującego zadania w Twoim systemie.</target> <source>Help</source> <target>Pomoc</target> -<source>Configuration overview:</source> -<target>Przegląd konfiguracji:</target> <source>Filter files</source> <target>Filtruj pliki</target> -<source>Status feedback</source> -<target>Opinia statusu</target> -<source>Silent mode</source> -<target>Tryb Cichy</target> -<source>Start minimized and write status information to a logfile</source> -<target>Uruchom zminimalizowane i zapisuj informacje do pliku logów</target> <source>Error handling</source> <target>Obsługa błędów</target> <source>Overview</source> <target>Przegląd</target> -<source>Select logfile directory:</source> -<target>Wybierz katalog z logami:</target> +<source>Status feedback</source> +<target>Opinia statusu</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Maksymalna liczba plików z logami:</target> +<source>Select logfile directory:</source> +<target>Wybierz katalog z logami:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Zapisz</target> <source>&Load</source> @@ -700,10 +709,6 @@ Transfer uprawnień plików i katalogów <target>Pary folderów</target> <source>Select view</source> <target>Określ widok</target> -<source>Folder Comparison and Synchronization</source> -<target>Porównywanie i Synchronizacja folderów</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Kosz nie jest jeszcze wspierany dla tej wersji systemu!</target> <source>Set direction:</source> <target>Kierunek synchronizacji:</target> <source>Exclude temporarily</source> @@ -738,6 +743,8 @@ Transfer uprawnień plików i katalogów <target>Zapisać zmiany obecnej konfiguracji?</target> <source>Configuration loaded!</source> <target>Konfiguracja wczytana!</target> +<source>Folder Comparison and Synchronization</source> +<target>Porównywanie i Synchronizacja folderów</target> <source>Hide files that exist on left side only</source> <target>Ukryj pliki, które istnieją tylko po lewej stronie</target> <source>Show files that exist on left side only</source> @@ -952,10 +959,6 @@ Transfer uprawnień plików i katalogów <target>Ustawianie domyślnego kierunku synchronizacji: Stare pliki zostaną nadpisane nowszymi.</target> <source>The file does not contain a valid configuration:</source> <target>Nieprawidłowy format pliku:</target> -<source>Scanning:</source> -<target>Skanowanie:</target> -<source>Encoding extended time information: %x</source> -<target>Odkodowywanie rozszerzonych informacji o czasie: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Możesz zignorować ten błąd uznająć jeden z katalogów jako pusty.</target> <source>Directory does not exist:</source> @@ -1032,8 +1035,6 @@ Transfer uprawnień plików i katalogów <target>Aktualizowanie atrybutów %x</target> <source>Source directory does not exist anymore:</source> <target>Katalog źródłowy nie istnieje:</target> -<source>Generating database...</source> -<target>Generowanie bazy danych...</target> <source>Nothing to synchronize according to configuration!</source> <target>Brak elementów do synchronizacji!</target> <source>Target directory name must not be empty!</source> @@ -1054,10 +1055,14 @@ Transfer uprawnień plików i katalogów <target>Wymagane wolne miejsce:</target> <source>Free disk space available:</source> <target>Wolne miejsce:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Katalog zostanie zmodyfikowany w efekcie synchronizacji wielu par folderów! Zweryfikuj ustawienia synchronizacji!</target> <source>Processing folder pair:</source> <target>Przetwarzanie folderów:</target> +<source>Generating database...</source> +<target>Generowanie bazy danych...</target> <source>Error copying locked file %x!</source> <target>Błąd podczas kopiowania zablokowanego pliku %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/portuguese.lng b/BUILD/Languages/portuguese.lng index d64f02f5..88d2b092 100644 --- a/BUILD/Languages/portuguese.lng +++ b/BUILD/Languages/portuguese.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Mostrar no Explorer</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Sincronização Automática</target> <source>Browse</source> <target>Procurar</target> -<source>Invalid commandline: "%x"</source> -<target>Comando inválido: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Comando inválido: %x</target> <source>Error resolving symbolic link:</source> <target>Erro na resolução do link simbólico:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Sincronização inicial:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Um dos ficheiros da base de dados não existe:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Uma das entradas da base de dados dentro do seguinte ficheiro não existe:</target> <source>Error reading from synchronization database:</source> <target>Erro ao ler a base de dados de sincronização:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Ocorreu uma excepção!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Erro fatal</target> +<source>Scanning:</source> +<target>A pesquisar:</target> +<source>Encoding extended time information: %x</source> +<target>A codificar dados temporais extendidos: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Ficheiro de configuração do FreeFileSync inválido!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ A linha de comandos é executada cada vez que: <target>Monitorizar activado...</target> <source>Waiting for missing directories...</source> <target>Aguardar pelos directórios em falta...</target> -<source>Could not initialize directory monitoring:</source> -<target>Não é possível iniciar monitorização do directório:</target> <source>A directory input field is empty.</source> <target>Um campo de directório está vazio.</target> -<source>Error when monitoring directories.</source> -<target>Erro ao monitorizar os directórios.</target> <source>Drag && drop</source> <target>Arrastar && Largar</target> +<source>Could not initialize directory monitoring:</source> +<target>Não é possível iniciar monitorização do directório:</target> +<source>Error when monitoring directories.</source> +<target>Erro ao monitorizar os directórios.</target> <source>Conversion error:</source> <target>Erro de conversão:</target> <source>Error moving file:</source> <target>Erro ao mover o ficheiro:</target> -<source>Operation aborted!</source> -<target>Operação abortada!</target> <source>Target file already existing!</source> <target>Ficheiro de destino já existe!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ A linha de comandos é executada cada vez que: <target>Erro de escrita no ficheiro:</target> <source>Error reading file:</source> <target>Erro de leitura de ficheiro:</target> +<source>Operation aborted!</source> +<target>Operação abortada!</target> <source>Endless loop when traversing directory:</source> <target>Loop infinito ao percorrer directório:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ A linha de comandos é executada cada vez que: <target>Criar um batch para sincronização automática. Para iniciar o batch, basta fazer duplo-clique no ficheiro ou executar via linha de comandos: FreeFileSync.exe <batch>. Também pode ser inserido no programador de tarefas do sistema operativo.</target> <source>Help</source> <target>Ajuda</target> -<source>Configuration overview:</source> -<target>Parametros de configuração:</target> <source>Filter files</source> <target>Filtrar ficheiros</target> -<source>Status feedback</source> -<target>Retorno de estado</target> -<source>Silent mode</source> -<target>Modo silencioso</target> -<source>Start minimized and write status information to a logfile</source> -<target>Iniciar minimizado e escrever info de estado para o log</target> <source>Error handling</source> <target>Controlador de erros</target> <source>Overview</source> <target>Vista</target> -<source>Select logfile directory:</source> -<target>Seleccione directório para ficheiro log:</target> +<source>Status feedback</source> +<target>Retorno de estado</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Número máximo de ficheiros log:</target> +<source>Select logfile directory:</source> +<target>Seleccione directório para ficheiro log:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Guardar</target> <source>&Load</source> @@ -693,10 +702,6 @@ Transferir ficheiro e permissões <target>Par de directório</target> <source>Select view</source> <target>Seleccionar vista</target> -<source>Folder Comparison and Synchronization</source> -<target>Comparação e Sincronização de pastas</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Reciclagem não é suportada para este sistema!</target> <source>Set direction:</source> <target>Escolher direcção:</target> <source>Exclude temporarily</source> @@ -731,6 +736,8 @@ Transferir ficheiro e permissões <target>Guardar alterações à configuração?</target> <source>Configuration loaded!</source> <target>Configuração carregada!</target> +<source>Folder Comparison and Synchronization</source> +<target>Comparação e Sincronização de pastas</target> <source>Hide files that exist on left side only</source> <target>Ocultar ficheiros existentes somente à esquerda</target> <source>Show files that exist on left side only</source> @@ -939,10 +946,6 @@ Transferir ficheiro e permissões <target>Escolher direcção de sincronização por defeito: Os ficheiros antigos serão substituídos pelos novos.</target> <source>The file does not contain a valid configuration:</source> <target>O ficheiro não contém uma configuração válida:</target> -<source>Scanning:</source> -<target>A pesquisar:</target> -<source>Encoding extended time information: %x</source> -<target>A codificar dados temporais extendidos: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Pode ignorar este erro para considerar o directório como vazio.</target> <source>Directory does not exist:</source> @@ -1019,8 +1022,6 @@ Transferir ficheiro e permissões <target>Actualizar atributos de %x</target> <source>Source directory does not exist anymore:</source> <target>A pasta de origem já não existe:</target> -<source>Generating database...</source> -<target>A gerar base de dados...</target> <source>Nothing to synchronize according to configuration!</source> <target>Nada a sincronizar de acordo com a configuração!</target> <source>Target directory name must not be empty!</source> @@ -1041,10 +1042,14 @@ Transferir ficheiro e permissões <target>Espaço livre necessário em disco:</target> <source>Free disk space available:</source> <target>Espaço livre em disco:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Um directório que pertencente a conjunto de vários directórios vai ser alterado! Por favor, verifique as opções de sincronização!</target> <source>Processing folder pair:</source> <target>A processar o par do directorio:</target> +<source>Generating database...</source> +<target>A gerar base de dados...</target> <source>Error copying locked file %x!</source> <target>Erro ao copiar ficheiro bloqueado %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/portuguese_br.lng b/BUILD/Languages/portuguese_br.lng index 57a63970..c0228212 100644 --- a/BUILD/Languages/portuguese_br.lng +++ b/BUILD/Languages/portuguese_br.lng @@ -7,6 +7,8 @@ <plural definition>n <= 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Mostrar no Explorer</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Sincronização Automatizada</target> <source>Browse</source> <target>Procurar</target> -<source>Invalid commandline: "%x"</source> -<target>Comando de linha inválido: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Comando de linha inválido: %x</target> <source>Error resolving symbolic link:</source> <target>Erro na resolução de link simbólico:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Sincronização inicial:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Um dos arquivos de banco de dados do FreeFileSync ainda não existe:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Um das entradas do banco de dados do FreeFileSync dentro do seguinte arquivo ainda não existe:</target> <source>Error reading from synchronization database:</source> <target>Erro ao ler do banco de dados de sincronização:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Ocorreu uma exceção!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Erro fatal</target> +<source>Scanning:</source> +<target>Pesquisando:</target> +<source>Encoding extended time information: %x</source> +<target>Codificando informações adicionais de tempo: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Arquivo de configuração do FreeFileSync inválido!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ A linha de comando é executada cada vez que: <target>Monitoramento ativo...</target> <source>Waiting for missing directories...</source> <target>Esperando por diretórios faltantes...</target> -<source>Could not initialize directory monitoring:</source> -<target>Não foi possível inicializar o monitoramento de diretórios:</target> <source>A directory input field is empty.</source> <target>Um campo de entrada de diretório está vazio.</target> -<source>Error when monitoring directories.</source> -<target>Erro ao monitorar os diretórios.</target> <source>Drag && drop</source> <target>Arrastar && Soltar</target> +<source>Could not initialize directory monitoring:</source> +<target>Não foi possível inicializar o monitoramento de diretórios:</target> +<source>Error when monitoring directories.</source> +<target>Erro ao monitorar os diretórios.</target> <source>Conversion error:</source> <target>Erro de conversão:</target> <source>Error moving file:</source> <target>Erro ao mover arquivo:</target> -<source>Operation aborted!</source> -<target>Operação cancelada!</target> <source>Target file already existing!</source> <target>Arquivo de destino já existe!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ A linha de comando é executada cada vez que: <target>Erro ao escrever arquivo:</target> <source>Error reading file:</source> <target>Erro ao ler arquivo:</target> +<source>Operation aborted!</source> +<target>Operação cancelada!</target> <source>Endless loop when traversing directory:</source> <target>Loop infinito quando percorrendo diretório:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ A linha de comando é executada cada vez que: <target>Cria um arquivo batch para sincronização automática. Para iniciar o modo batch, simplesmente dê um duplo click no arquivo ou execute via linha de comando: FreeFileSync.exe <arquivo batch>. Também pode ser agendado pelo agendador de tarefas do sistema operacional.</target> <source>Help</source> <target>Ajuda</target> -<source>Configuration overview:</source> -<target>Parâmetros de configuração:</target> <source>Filter files</source> <target>Filtrar arquivos</target> -<source>Status feedback</source> -<target>Informação do Status</target> -<source>Silent mode</source> -<target>Modo silencioso</target> -<source>Start minimized and write status information to a logfile</source> -<target>Iniciar minimizado e escrever informação de status em um arquivo de log</target> <source>Error handling</source> <target>Tratamento de erros</target> <source>Overview</source> <target>Parâmetros</target> -<source>Select logfile directory:</source> -<target>Escolha um diretório para salvar o arquivo de log:</target> +<source>Status feedback</source> +<target>Informação do Status</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Número máximo de arquivos de log:</target> +<source>Select logfile directory:</source> +<target>Escolha um diretório para salvar o arquivo de log:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Salvar</target> <source>&Load</source> @@ -695,10 +704,6 @@ Transferir permissões de arquivos e diretórios <target>Pares de pastas</target> <source>Select view</source> <target>Selecionar visualização</target> -<source>Folder Comparison and Synchronization</source> -<target>Comparação e Sincronização de Pastas</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Lixeira ainda não suportada por esse sistema!</target> <source>Set direction:</source> <target>Configurar direção</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Transferir permissões de arquivos e diretórios <target>Salvar modificações para a configuração atual?</target> <source>Configuration loaded!</source> <target>Configuração carregada!</target> +<source>Folder Comparison and Synchronization</source> +<target>Comparação e Sincronização de Pastas</target> <source>Hide files that exist on left side only</source> <target>Ocultar arquivos que existem somente à esquerda</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Transferir permissões de arquivos e diretórios <target>Configurando direções padrões de sincronização: Arquivos antigos serão substituídos por arquivos mais novos.</target> <source>The file does not contain a valid configuration:</source> <target>O arquivo não contém uma configuração válida:</target> -<source>Scanning:</source> -<target>Pesquisando:</target> -<source>Encoding extended time information: %x</source> -<target>Codificando informações adicionais de tempo: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Você pode ignorar esse erro para considerar o diretório como vazio.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Transferir permissões de arquivos e diretórios <target>Atualizando atributos de %x</target> <source>Source directory does not exist anymore:</source> <target>Diretório de origem não existe mais:</target> -<source>Generating database...</source> -<target>Gerando banco de dados...</target> <source>Nothing to synchronize according to configuration!</source> <target>Nada para sincronizar de acordo com a configuração!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Transferir permissões de arquivos e diretórios <target>Espaço livre em disco requerido:</target> <source>Free disk space available:</source> <target>Espaço livre em disco:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Um diretório que faz parte de múltiplos pares de pastas será modificado! Por favor revise suas configurações de sincronização!</target> <source>Processing folder pair:</source> <target>Processando par de pastas:</target> +<source>Generating database...</source> +<target>Gerando banco de dados...</target> <source>Error copying locked file %x!</source> <target>Erro ao copiar arquivo bloqueado %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/romanian.lng b/BUILD/Languages/romanian.lng index 2c7da055..91e1f2e9 100644 --- a/BUILD/Languages/romanian.lng +++ b/BUILD/Languages/romanian.lng @@ -7,6 +7,8 @@ <plural definition>n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Arată în Explorator</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Sincronizare Inteligentă</target> <source>Browse</source> <target>Explorează</target> -<source>Invalid commandline: "%x"</source> -<target>Linie de comandă nevalidă: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Linie de comandă nevalidă: %x</target> <source>Error resolving symbolic link:</source> <target>Eroare la rezolvarea legăturii simbolice:</target> <source>Show popup</source> @@ -110,10 +112,10 @@ <target>Sincronizare inițială:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Una dintre părți nu are o filă de tip bază de date FreeFileSync:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Una dintre intrările în baza de date FreeFileSync din fila următoare nu există:</target> <source>Error reading from synchronization database:</source> <target>Eroare la citirea din baza de date a sincronizării:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>A apărut o excepție !</target> <source>Error deleting file:</source> @@ -137,6 +139,15 @@ <target>Informații</target> <source>Fatal Error</source> <target>Eroare Fatală</target> +<source>Scanning:</source> +<target>Scanez:</target> +<source>Encoding extended time information: %x</source> +<target>Codarea informațiilor timpului extins: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Filă de configurare FreeFileSync nevalidă!</target> <source>File does not exist:</source> @@ -234,20 +245,18 @@ Linia de comandă este executată de fiecare dată cînd: <target>Monitorizare activă...</target> <source>Waiting for missing directories...</source> <target>Aștept ca dosarele lipsă să devină disponibile...</target> -<source>Could not initialize directory monitoring:</source> -<target>Nu pot inițializa monitorizarea dosarelor:</target> <source>A directory input field is empty.</source> <target>Cel puțin unul din dosarele de comparat este nespecificat.</target> -<source>Error when monitoring directories.</source> -<target>Eroare la monitorizarea dosarelor.</target> <source>Drag && drop</source> <target>Trage un dosar peste compartimentul de mai jos sau folosește butonul Explorează</target> +<source>Could not initialize directory monitoring:</source> +<target>Nu pot inițializa monitorizarea dosarelor:</target> +<source>Error when monitoring directories.</source> +<target>Eroare la monitorizarea dosarelor.</target> <source>Conversion error:</source> <target>Eroare de convertire:</target> <source>Error moving file:</source> <target>Eroare la mutarea filei:</target> -<source>Operation aborted!</source> -<target>Operație abandonată!</target> <source>Target file already existing!</source> <target>Fila țintă există deja!</target> <source>Error moving directory:</source> @@ -278,6 +287,8 @@ Linia de comandă este executată de fiecare dată cînd: <target>Eroare la scrierea filei:</target> <source>Error reading file:</source> <target>Eroare la citirea filei:</target> +<source>Operation aborted!</source> +<target>Operație abandonată!</target> <source>Endless loop when traversing directory:</source> <target>Buclă infinită la parcurgerea dosarului:</target> <source>Error traversing directory:</source> @@ -418,24 +429,22 @@ Linia de comandă este executată de fiecare dată cînd: <target>Poți crea o filă ce include un set de comenzi [batch file], în scopul folosirii ei la sincronizarea inteligentă. Pentru a porni în modul set, dublu-clichează fila sau execut-o folosind linia de comandă: FreeFileSync.exe <filaset>. Această operație poate fi programată în planificatorul de sarcini al sistemului de operare [task scheduler].</target> <source>Help</source> <target>Ajutor</target> -<source>Configuration overview:</source> -<target>Panorama Configurației:</target> <source>Filter files</source> <target>Filtru de File</target> -<source>Status feedback</source> -<target>Afișarea Stării</target> -<source>Silent mode</source> -<target>Mod silențios</target> -<source>Start minimized and write status information to a logfile</source> -<target>Pornește minimizat și scrie informațiile de stare într-un jurnal</target> <source>Error handling</source> <target>Gestionarea Erorilor</target> <source>Overview</source> <target>Panoramă</target> -<source>Select logfile directory:</source> -<target>Selectează un dosar pentru fila .log:</target> +<source>Status feedback</source> +<target>Afișarea Stării</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Numărul maxim de file jurnal:</target> +<source>Select logfile directory:</source> +<target>Selectează un dosar pentru fila .log:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Salvează</target> <source>&Load</source> @@ -700,10 +709,6 @@ Transferă permisiunile filelor și dosarelor <target>Perechi de Dosare</target> <source>Select view</source> <target>Selectează Vederea</target> -<source>Folder Comparison and Synchronization</source> -<target>Comparare și Sincronizare de Dosare</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Reciclatorul nu este încă suportat pentru acest sistem de operare!</target> <source>Set direction:</source> <target>Setează Acțiunea ca în Icoana Alăturată:</target> <source>Exclude temporarily</source> @@ -738,6 +743,8 @@ Transferă permisiunile filelor și dosarelor <target>Vrei să salvezi modificările configurației curente?</target> <source>Configuration loaded!</source> <target>Configurație încărcată !</target> +<source>Folder Comparison and Synchronization</source> +<target>Comparare și Sincronizare de Dosare</target> <source>Hide files that exist on left side only</source> <target>Ascunde filele care există doar în stînga</target> <source>Show files that exist on left side only</source> @@ -952,10 +959,6 @@ Transferă permisiunile filelor și dosarelor <target>Baza de date existentă va fi făcută compatibilă cu versiunea softului și apoi va fi setat sensul implicit de sincronizare: Filele vechi vor fi suprascrise de cele noi.</target> <source>The file does not contain a valid configuration:</source> <target>Fila nu conține o configurație validă:</target> -<source>Scanning:</source> -<target>Scanez:</target> -<source>Encoding extended time information: %x</source> -<target>Codarea informațiilor timpului extins: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Poți ignora această eroare dacă vrei ca dosarul să fie considerat gol.</target> <source>Directory does not exist:</source> @@ -1032,8 +1035,6 @@ Transferă permisiunile filelor și dosarelor <target>Actualizez atributele lui %x</target> <source>Source directory does not exist anymore:</source> <target>Dosarul sursă nu mai există:</target> -<source>Generating database...</source> -<target>Generez baza de date...</target> <source>Nothing to synchronize according to configuration!</source> <target>Nu este nimic de sincronizat conform configurației!</target> <source>Target directory name must not be empty!</source> @@ -1054,10 +1055,14 @@ Transferă permisiunile filelor și dosarelor <target>Spațiu liber necesar:</target> <source>Free disk space available:</source> <target>Spațiu liber disponibil:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Va fi modificat un dosar care face parte din mai multe perechi de dosare! Reverifică setările de sincronizare!</target> <source>Processing folder pair:</source> <target>Procesez perechea de dosare:</target> +<source>Generating database...</source> +<target>Generez baza de date...</target> <source>Error copying locked file %x!</source> <target>Eroare la copierea filei zăvorîte %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/russian.lng b/BUILD/Languages/russian.lng index bd2a5e26..1fcbfa1b 100644 --- a/BUILD/Languages/russian.lng +++ b/BUILD/Languages/russian.lng @@ -7,6 +7,8 @@ <plural definition>n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<11 || n%100>14) ? 1 : 2</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Показать в Проводнике</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Автоматическая синхронизация</target> <source>Browse</source> <target>Обзор</target> -<source>Invalid commandline: "%x"</source> -<target>Неверная командная строка: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Неверная командная строка: %x</target> <source>Error resolving symbolic link:</source> <target>Ошибка при решении символической ссылки:</target> <source>Show popup</source> @@ -110,10 +112,10 @@ <target>Первоначальная синхронизация:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Файла базы данных FreeFileSync еще не существует:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Записей, относящихся к следующим файлам, в базе данных FreeFileSync еще не существует:</target> <source>Error reading from synchronization database:</source> <target>Ошибка при чтении из базы данных синхронизации:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Исключение произошло!</target> <source>Error deleting file:</source> @@ -137,6 +139,15 @@ <target>Информация</target> <source>Fatal Error</source> <target>Критическая ошибка</target> +<source>Scanning:</source> +<target>Сканирую:</target> +<source>Encoding extended time information: %x</source> +<target>Кодирование расширенной информации о времени: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Неверный файл конфигурации FreeFileSync!</target> <source>File does not exist:</source> @@ -234,20 +245,18 @@ The command line is executed each time: <target>Мониторинг включен...</target> <source>Waiting for missing directories...</source> <target>Ожидание пропущенных папок...</target> -<source>Could not initialize directory monitoring:</source> -<target>Не удалось инициализировать папку для мониторинга:</target> <source>A directory input field is empty.</source> <target>Поле ввода пути папки пустое.</target> -<source>Error when monitoring directories.</source> -<target>Ошибка при мониторинге папок.</target> <source>Drag && drop</source> <target>Drag && drop</target> +<source>Could not initialize directory monitoring:</source> +<target>Не удалось инициализировать папку для мониторинга:</target> +<source>Error when monitoring directories.</source> +<target>Ошибка при мониторинге папок.</target> <source>Conversion error:</source> <target>Ошибка преобразования:</target> <source>Error moving file:</source> <target>Ошибка перемещения файла:</target> -<source>Operation aborted!</source> -<target>Операция отменена!</target> <source>Target file already existing!</source> <target>Конечный файл уже существует!</target> <source>Error moving directory:</source> @@ -278,6 +287,8 @@ The command line is executed each time: <target>Ошибка при записи файла:</target> <source>Error reading file:</source> <target>Ошибка при чтении файла:</target> +<source>Operation aborted!</source> +<target>Операция отменена!</target> <source>Endless loop when traversing directory:</source> <target>Зацикливание при встрече пересекающихся путей:</target> <source>Error traversing directory:</source> @@ -421,24 +432,22 @@ The command line is executed each time: <target>Создайте файл пакетного задания для автоматической синхронизации. Для запуска в пакетном режиме просто дважды щелкните по файлу или выполните командную строку: FreeFileSync.exe <batchfile>. Также это может быть запланировано в планировщике задач операционной системы.</target> <source>Help</source> <target>Помощь</target> -<source>Configuration overview:</source> -<target>Настройки синхронизации:</target> <source>Filter files</source> <target>Фильтр файлов</target> -<source>Status feedback</source> -<target>Статус обратной связи</target> -<source>Silent mode</source> -<target>Скрытый режим</target> -<source>Start minimized and write status information to a logfile</source> -<target>Запускать свернутым и писать информацию о состоянии в лог-файл</target> <source>Error handling</source> <target>Обработка ошибок</target> <source>Overview</source> <target>Главная</target> -<source>Select logfile directory:</source> -<target>Выберите папку для лог-файлов:</target> +<source>Status feedback</source> +<target>Статус обратной связи</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Максимальное количество лог-файлов:</target> +<source>Select logfile directory:</source> +<target>Выберите папку для лог-файлов:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Сохранить</target> <source>&Load</source> @@ -699,10 +708,6 @@ Transfer file and directory permissions <target>Пары папок для синхронизации</target> <source>Select view</source> <target>Список файлов</target> -<source>Folder Comparison and Synchronization</source> -<target>Сравнение и синхронизация</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Корзина пока не поддерживается для этой системы!</target> <source>Set direction:</source> <target>Выберите направление:</target> <source>Exclude temporarily</source> @@ -737,6 +742,8 @@ Transfer file and directory permissions <target>Сохранить изменения в текущих настройках синхронизации?</target> <source>Configuration loaded!</source> <target>Настройки синхронизации загружены!</target> +<source>Folder Comparison and Synchronization</source> +<target>Сравнение и синхронизация</target> <source>Hide files that exist on left side only</source> <target>Скрыть файлы, существующие только слева</target> <source>Show files that exist on left side only</source> @@ -957,10 +964,6 @@ Transfer file and directory permissions </target> <source>The file does not contain a valid configuration:</source> <target>Файл не содержит действительных настроек синхронизации:</target> -<source>Scanning:</source> -<target>Сканирую:</target> -<source>Encoding extended time information: %x</source> -<target>Кодирование расширенной информации о времени: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Вы можете проигнорировать ошибку, приняв папку за пустую.</target> <source>Directory does not exist:</source> @@ -1037,8 +1040,6 @@ Transfer file and directory permissions <target>Обновление атрибутов %x</target> <source>Source directory does not exist anymore:</source> <target>Исходная папка больше не существует:</target> -<source>Generating database...</source> -<target>Создание базы данных...</target> <source>Nothing to synchronize according to configuration!</source> <target>Ничего нет для синхронизации в соответствии с настройками синхронизации!</target> <source>Target directory name must not be empty!</source> @@ -1059,10 +1060,14 @@ Transfer file and directory permissions <target>Требуемое свободное место на диске:</target> <source>Free disk space available:</source> <target>Доступно свободного места на диске:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Папка, входящая в несколько пар папок, будет изменена. Пожалуйста, проверьте настройки синхронизации!</target> <source>Processing folder pair:</source> <target>Обработка пары папок:</target> +<source>Generating database...</source> +<target>Создание базы данных...</target> <source>Error copying locked file %x!</source> <target>Ошибка при копировании заблокированного файла %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/slovenian.lng b/BUILD/Languages/slovenian.lng index 63b482b1..4879ecef 100644 --- a/BUILD/Languages/slovenian.lng +++ b/BUILD/Languages/slovenian.lng @@ -7,6 +7,8 @@ <plural definition>n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Prikaži v Raziskovalcu</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Avtomatizirana sinhronizacija</target> <source>Browse</source> <target>Brskaj</target> -<source>Invalid commandline: "%x"</source> -<target>Napačen ukaz v vrstici: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Napačen ukaz v vrstici: %x</target> <source>Error resolving symbolic link:</source> <target>Napaka pri razreševanju simbolične povezave:</target> <source>Show popup</source> @@ -111,10 +113,10 @@ <target>Začetna sinhronizacija:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Ena od FreeFileSync datotek podatkovne baze še en obstaja:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Eden od FreeFileSync vnosov v podatkovni bazi znotraj naslednje datoteke še ne obstaja:</target> <source>Error reading from synchronization database:</source> <target>Napaka pri branju iz sinhronizacijske podatkovne baze:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Zgodila se je napaka!</target> <source>Error deleting file:</source> @@ -139,6 +141,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Usodna napaka</target> +<source>Scanning:</source> +<target>Pregledujem:</target> +<source>Encoding extended time information: %x</source> +<target>Podrobne informacije o času enkodiranja: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Neveljavna FreeFileSync nastavitvena datoteka!</target> <source>File does not exist:</source> @@ -239,20 +250,18 @@ Ukazna vrstica se izvrši vsakič ko: <target>Nadziranje aktivno...</target> <source>Waiting for missing directories...</source> <target>Čakam na manjkajoče imenike...</target> -<source>Could not initialize directory monitoring:</source> -<target>Ne morem začeti nadzorovanja imenikov:</target> <source>A directory input field is empty.</source> <target>Vpisno polje za imenik je prazno.</target> -<source>Error when monitoring directories.</source> -<target>Napaka pri nadzorovanju imenikov.</target> <source>Drag && drop</source> <target>Povleci && spusti</target> +<source>Could not initialize directory monitoring:</source> +<target>Ne morem začeti nadzorovanja imenikov:</target> +<source>Error when monitoring directories.</source> +<target>Napaka pri nadzorovanju imenikov.</target> <source>Conversion error:</source> <target>Napaka pri pretvorbi:</target> <source>Error moving file:</source> <target>Napaka pri premikanju datoteke:</target> -<source>Operation aborted!</source> -<target>Operacija prekinjena!</target> <source>Target file already existing!</source> <target>Ciljna datoteka že obstaja!</target> <source>Error moving directory:</source> @@ -283,6 +292,8 @@ Ukazna vrstica se izvrši vsakič ko: <target>Napaka pri pisanju datoteke:</target> <source>Error reading file:</source> <target>Napaka pri branju datoteke:</target> +<source>Operation aborted!</source> +<target>Operacija prekinjena!</target> <source>Endless loop when traversing directory:</source> <target>Neskončna zanka pri prehodu imenika:</target> <source>Error traversing directory:</source> @@ -423,24 +434,22 @@ Ukazna vrstica se izvrši vsakič ko: <target>Ustvari batch datoteko za avtomatizirano sinhronizacijo. Da začnete v batch načinu preprosto dvokliknite datoteko ali jo izvršite preko ukazne vrstice: FreeFileSync.exe <batchfile>. To se lahko nastavi v razpored opravil vašega operacijskega sistema.</target> <source>Help</source> <target>Pomoč</target> -<source>Configuration overview:</source> -<target>Pregled konfiguracije:</target> <source>Filter files</source> <target>Filtriraj datoteke</target> -<source>Status feedback</source> -<target>Povratne informacije statusa</target> -<source>Silent mode</source> -<target>Tihi način</target> -<source>Start minimized and write status information to a logfile</source> -<target>Zaženi pomanjšano in piši statusne informacije v datoteko za beleženje</target> <source>Error handling</source> <target>Napaka pri obravnavanju</target> <source>Overview</source> <target>Pregled</target> -<source>Select logfile directory:</source> -<target>Izberite imenik za datoteko beleženja:</target> +<source>Status feedback</source> +<target>Povratne informacije statusa</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Maksimalno število datotek beleženja:</target> +<source>Select logfile directory:</source> +<target>Izberite imenik za datoteko beleženja:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Shrani</target> <source>&Load</source> @@ -705,10 +714,6 @@ Prenesi dovoljenja datotek in imenikov <target>Pari map</target> <source>Select view</source> <target>Izberite pogled</target> -<source>Folder Comparison and Synchronization</source> -<target>Primerjava in sinhronizacija imenika</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Koš še ni podprt za ta sistem!</target> <source>Set direction:</source> <target>Nastavi smer:</target> <source>Exclude temporarily</source> @@ -743,6 +748,8 @@ Prenesi dovoljenja datotek in imenikov <target>Shranim spremembe trenutne konfiguracije?</target> <source>Configuration loaded!</source> <target>Konfiguracija naložena!</target> +<source>Folder Comparison and Synchronization</source> +<target>Primerjava in sinhronizacija imenika</target> <source>Hide files that exist on left side only</source> <target>Skrij datoteke, ki obstajajo samo na levi strani</target> <source>Show files that exist on left side only</source> @@ -963,10 +970,6 @@ Prenesi dovoljenja datotek in imenikov <target>Nastavljanje privzetih smeri sinhronizacije: Stare datoteke bodo prepisane z novimi datotekami.</target> <source>The file does not contain a valid configuration:</source> <target>Datoteka ne vsebuje veljavne konfiguracije:</target> -<source>Scanning:</source> -<target>Pregledujem:</target> -<source>Encoding extended time information: %x</source> -<target>Podrobne informacije o času enkodiranja: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>To napako z obravnavanjem imenika kot praznega lahko ignorirate.</target> <source>Directory does not exist:</source> @@ -1043,8 +1046,6 @@ Prenesi dovoljenja datotek in imenikov <target>Posodabljam atribute od %x</target> <source>Source directory does not exist anymore:</source> <target>Izvorni imenik ne obstaja več:</target> -<source>Generating database...</source> -<target>Ustvarjam podatkovno bazo...</target> <source>Nothing to synchronize according to configuration!</source> <target>Po trenutni konfiguraciji ni nič za sinhronizirati!</target> <source>Target directory name must not be empty!</source> @@ -1065,10 +1066,14 @@ Prenesi dovoljenja datotek in imenikov <target>Potreben prostor na disku:</target> <source>Free disk space available:</source> <target>Prosti disk, ki je na voljo:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Imenik bo spremenjen, kar je del večkratnih parov map! Prosimo preglejte nastavitve sinhronizacije!</target> <source>Processing folder pair:</source> <target>Obdelujem par map:</target> +<source>Generating database...</source> +<target>Ustvarjam podatkovno bazo...</target> <source>Error copying locked file %x!</source> <target>Napaka pri kopiranju zaklenjene datoteke %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/spanish.lng b/BUILD/Languages/spanish.lng index e5b0be8b..cf6e7f1c 100644 --- a/BUILD/Languages/spanish.lng +++ b/BUILD/Languages/spanish.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Mostrar en Explorer</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Sincronización Automática</target> <source>Browse</source> <target>Examinar</target> -<source>Invalid commandline: "%x"</source> -<target>Línea de comandos inválida: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Línea de comandos inválida: %x</target> <source>Error resolving symbolic link:</source> <target>Error al resolver enlace simbólico:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Sincronización inicial:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Uno de los archivos de la base de datos de FreeFileSync aún no existe:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Una de las entradas de la base de datos de FreeFileSync con el siguiente archivo aún no existe:</target> <source>Error reading from synchronization database:</source> <target>Error al leer de la base de datos de sincronización:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>¡Ha ocurrido una excepción!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Error fatal</target> +<source>Scanning:</source> +<target>Escanear:</target> +<source>Encoding extended time information: %x</source> +<target>Información temporal extendida de la codificación: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>¡Archivo de configuración de FreeFileSync inválido!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ La línea de comandos se ejecuta cada vez: <target>Visualización activa...</target> <source>Waiting for missing directories...</source> <target>Esperando directorios faltantes...</target> -<source>Could not initialize directory monitoring:</source> -<target>No se ha podido inicializar la visualización de directorios:</target> <source>A directory input field is empty.</source> <target>Un campo de directorio está vacío.</target> -<source>Error when monitoring directories.</source> -<target>Error al visualizar los directorios.</target> <source>Drag && drop</source> <target>Arrastrar y soltar</target> +<source>Could not initialize directory monitoring:</source> +<target>No se ha podido inicializar la visualización de directorios:</target> +<source>Error when monitoring directories.</source> +<target>Error al visualizar los directorios.</target> <source>Conversion error:</source> <target>Error de conversión:</target> <source>Error moving file:</source> <target>Error al mover archivo:</target> -<source>Operation aborted!</source> -<target>¡Operación abortada!</target> <source>Target file already existing!</source> <target>¡El archivo de destino ya existe!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ La línea de comandos se ejecuta cada vez: <target>Error al escribir archivo:</target> <source>Error reading file:</source> <target>Error al leer archivo:</target> +<source>Operation aborted!</source> +<target>¡Operación abortada!</target> <source>Endless loop when traversing directory:</source> <target>Bucle infinito al buscar en el directorio:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ La línea de comandos se ejecuta cada vez: <target>Crear un archivo batch para la sincronización automática. Para iniciar el modo batch, simplemente hacer doble clic sobre el archivo o ejectuar mediante la línea de comandos: FreeFileSync.exe <archivo_batch>. También se puede programar en el planificador del tareas del sistema operativo.</target> <source>Help</source> <target>Ayuda</target> -<source>Configuration overview:</source> -<target>Visión global de la configuración:</target> <source>Filter files</source> <target>Filtrar archivos</target> -<source>Status feedback</source> -<target>Status feedback</target> -<source>Silent mode</source> -<target>Modo silencioso</target> -<source>Start minimized and write status information to a logfile</source> -<target>Iniciar minimizado y escribir información de estado en un registro</target> <source>Error handling</source> <target>Gestión de errores</target> <source>Overview</source> <target>Visión global</target> -<source>Select logfile directory:</source> -<target>Seleccione directorio para el archivo de registro:</target> +<source>Status feedback</source> +<target>Status feedback</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Número máximo de archivos de registro:</target> +<source>Select logfile directory:</source> +<target>Seleccione directorio para el archivo de registro:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Guardar</target> <source>&Load</source> @@ -695,10 +704,6 @@ Transferir permisos de archivo y directorio <target>Pares de carpetas</target> <source>Select view</source> <target>Sellecione vista</target> -<source>Folder Comparison and Synchronization</source> -<target>Comparación y Sincronización de Carpetas</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>¡La papelera de reciclaje no esta soportada en este sistema!</target> <source>Set direction:</source> <target>Indicar dirección:</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Transferir permisos de archivo y directorio <target>¿Guardar los cambios de la configuración actual?</target> <source>Configuration loaded!</source> <target>¡Configuración cargada!</target> +<source>Folder Comparison and Synchronization</source> +<target>Comparación y Sincronización de Carpetas</target> <source>Hide files that exist on left side only</source> <target>Ocultar archivos que existen sólo en el lado izquierdo</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Transferir permisos de archivo y directorio <target>Fijando direcciones de sincronización por defecto: Los archivos viejos serán sobreescritos por los archivos nuevos.</target> <source>The file does not contain a valid configuration:</source> <target>El archivo no contiene una configuración válida:</target> -<source>Scanning:</source> -<target>Escanear:</target> -<source>Encoding extended time information: %x</source> -<target>Información temporal extendida de la codificación: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Puede ignorar este error al considerar el directorio como vacío.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Transferir permisos de archivo y directorio <target>Actualizar atributos de %x</target> <source>Source directory does not exist anymore:</source> <target>El directorio origen ya no existe:</target> -<source>Generating database...</source> -<target>Generando base de datos...</target> <source>Nothing to synchronize according to configuration!</source> <target>¡Nada que sincronizar de acuerdo con la configuración!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Transferir permisos de archivo y directorio <target>Espacio de disco necesario:</target> <source>Free disk space available:</source> <target>Espacio de disco disponible:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Un directorio será modificado, el cual es parte de mútiples pares de carpetas. ¡Por favor, revise la configuración de la sincronización!</target> <source>Processing folder pair:</source> <target>Procesar un par de carpetas:</target> +<source>Generating database...</source> +<target>Generando base de datos...</target> <source>Error copying locked file %x!</source> <target>¡Error al copiar archivo bloqueado %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/swedish.lng b/BUILD/Languages/swedish.lng index 12a4784f..7ace3e68 100644 --- a/BUILD/Languages/swedish.lng +++ b/BUILD/Languages/swedish.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Visa i Utforskaren</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Automatiserad synkronisering</target> <source>Browse</source> <target>Bläddra</target> -<source>Invalid commandline: "%x"</source> -<target>Ogiltig kommandorad: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Ogiltig kommandorad: %x</target> <source>Error resolving symbolic link:</source> <target>Kan inte tyda symbolisk länk:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Initial synkronisering:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>En av FreeFileSyncs databasfiler saknas:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Ett av FreeFileSyncs databasobjekt saknas i följande fil:</target> <source>Error reading from synchronization database:</source> <target>Kan inte läsa från databasen:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Ett undantag inträffade!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Info</target> <source>Fatal Error</source> <target>Allvarligt fel</target> +<source>Scanning:</source> +<target>Skannar:</target> +<source>Encoding extended time information: %x</source> +<target>Kodar utökad tidsinformation: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Felaktig FreeFileSync konfigurationsfil!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ Kommandot verkställes när: <target>Övervakning aktiverad...</target> <source>Waiting for missing directories...</source> <target>Väntar på saknade destinationer...</target> -<source>Could not initialize directory monitoring:</source> -<target>Kan inte initiera katalogskanner:</target> <source>A directory input field is empty.</source> <target>Ett katalogfält är tomt</target> -<source>Error when monitoring directories.</source> -<target>Fel vid övervakning av kataloger.</target> <source>Drag && drop</source> <target>Dra && släpp</target> +<source>Could not initialize directory monitoring:</source> +<target>Kan inte initiera katalogskanner:</target> +<source>Error when monitoring directories.</source> +<target>Fel vid övervakning av kataloger.</target> <source>Conversion error:</source> <target>Konversionsfel:</target> <source>Error moving file:</source> <target>Kan inte flytta fil:</target> -<source>Operation aborted!</source> -<target>Processen avbruten!</target> <source>Target file already existing!</source> <target>Filen finns redan!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ Kommandot verkställes när: <target>Kan inte skriva fil:</target> <source>Error reading file:</source> <target>Kan inte läsa fil:</target> +<source>Operation aborted!</source> +<target>Processen avbruten!</target> <source>Endless loop when traversing directory:</source> <target>Oändlig loop vid accessförsök på katalog:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ Kommandot verkställes när: <target>Skapa en batch-fil för automatiserad synkronisering. För att starta i batch-läge, Dubbelklicka på filen eller starta den med kommandoraden: FreeFileSync.exe <batch-fil>. Batch-filen kan också schemaläggas i Windows Schemaläggare.</target> <source>Help</source> <target>Hjälp</target> -<source>Configuration overview:</source> -<target>Översikt:</target> <source>Filter files</source> <target>Undantag</target> -<source>Status feedback</source> -<target>Status</target> -<source>Silent mode</source> -<target>Tyst läge</target> -<source>Start minimized and write status information to a logfile</source> -<target>Starta minimerad och skriv statusinformation till en loggfil</target> <source>Error handling</source> <target>Felhantering</target> <source>Overview</source> <target>Översikt</target> -<source>Select logfile directory:</source> -<target>Välj loggfilskatalog:</target> +<source>Status feedback</source> +<target>Status</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Max antal loggfiler:</target> +<source>Select logfile directory:</source> +<target>Välj loggfilskatalog:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Spara</target> <source>&Load</source> @@ -695,10 +704,6 @@ Transfer file and directory permissions <target>Katalogpar</target> <source>Select view</source> <target>Välj vy</target> -<source>Folder Comparison and Synchronization</source> -<target>Katalogjämförelse och synkronisering</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Stödet för papperskorg finns ännu inte i detta system</target> <source>Set direction:</source> <target>Ange riktning:</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Transfer file and directory permissions <target>Vill du spara ändringarna i aktuella inställningar?</target> <source>Configuration loaded!</source> <target>Inställningar inlästa!</target> +<source>Folder Comparison and Synchronization</source> +<target>Katalogjämförelse och synkronisering</target> <source>Hide files that exist on left side only</source> <target>Visa inte filer som endast finns till vänster</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Transfer file and directory permissions <target>Standardsynkronisering: Gamla filer kommer att skrivas över av nyare versioner.</target> <source>The file does not contain a valid configuration:</source> <target>Filen är ingen giltig konfigureringsfil:</target> -<source>Scanning:</source> -<target>Skannar:</target> -<source>Encoding extended time information: %x</source> -<target>Kodar utökad tidsinformation: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Du kan bortse från det här felet och utgå ifrån att katalogen är tom</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Transfer file and directory permissions <target>Uppdaterar attribut för %x</target> <source>Source directory does not exist anymore:</source> <target>Källkatalogen finns inte längre:</target> -<source>Generating database...</source> -<target>Skapar databas...</target> <source>Nothing to synchronize according to configuration!</source> <target>Inget att synkronisera enligt aktuella inställningar!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Transfer file and directory permissions <target>Krav på ledigt diskutrymme:</target> <source>Free disk space available:</source> <target>Ledigt diskutrymme:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>En katalog som är en del av flera katalogpar kommer att ändras! Kontrollera synkroniseringsinställningarna</target> <source>Processing folder pair:</source> <target>Processar katalogpar:</target> +<source>Generating database...</source> +<target>Skapar databas...</target> <source>Error copying locked file %x!</source> <target>Kan inte kopiera låst fil %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/turkish.lng b/BUILD/Languages/turkish.lng index 4e0e6a33..6443596b 100644 --- a/BUILD/Languages/turkish.lng +++ b/BUILD/Languages/turkish.lng @@ -7,6 +7,8 @@ <plural definition>n == 1 ? 0 : 1</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Tarayıcıda Göster</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Kendiliğinden Eşleştirme</target> <source>Browse</source> <target>Gözat</target> -<source>Invalid commandline: "%x"</source> -<target>Geçersiz komut satırı: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Geçersiz komut satırı: %x</target> <source>Error resolving symbolic link:</source> <target>Sembolik bağlantı çözümlenirken hata:</target> <source>Show popup</source> @@ -109,10 +111,10 @@ <target>Başlangıç eşleştirmesi:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>FreeFileSync veritabanı dosyalarından biri henüz yok:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Şu dosyadaki FreeFileSync veritabanı kayıtlarından biri henüz yok:</target> <source>Error reading from synchronization database:</source> <target>Eşleştirme veri tabanı okunurken hata:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Olağan dışı bir durum oluştu!</target> <source>Error deleting file:</source> @@ -135,6 +137,15 @@ <target>Bilgi</target> <source>Fatal Error</source> <target>Ölümcül Hata</target> +<source>Scanning:</source> +<target>Taranıyor:</target> +<source>Encoding extended time information: %x</source> +<target>Uzatılmış zaman bilgisi kodlanıyor: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Geçersiz FreeFileSync yapılandırma dosyası!</target> <source>File does not exist:</source> @@ -229,20 +240,18 @@ Her seferinde çalıştırılacak komut satırı: <target>İzleme etkin...</target> <source>Waiting for missing directories...</source> <target>Kayıp klasörler için bekleniyor...</target> -<source>Could not initialize directory monitoring:</source> -<target>Klasör izlemesi başlatılamadı:</target> <source>A directory input field is empty.</source> <target>Bir klasör giriş alanı boş</target> -<source>Error when monitoring directories.</source> -<target>Klasörler izlenirken hata.</target> <source>Drag && drop</source> <target>Klasör ya da dosyaları buraya sürükleyip bırakabilirsiniz</target> +<source>Could not initialize directory monitoring:</source> +<target>Klasör izlemesi başlatılamadı:</target> +<source>Error when monitoring directories.</source> +<target>Klasörler izlenirken hata.</target> <source>Conversion error:</source> <target>Dönüştürme hatası:</target> <source>Error moving file:</source> <target>Dosya taşınırken hata:</target> -<source>Operation aborted!</source> -<target>İşlemden vazgeçildi!</target> <source>Target file already existing!</source> <target>Hedef dosya zaten var!</target> <source>Error moving directory:</source> @@ -273,6 +282,8 @@ Her seferinde çalıştırılacak komut satırı: <target>Dosya yazılırken hata:</target> <source>Error reading file:</source> <target>Dosya okunurken hata:</target> +<source>Operation aborted!</source> +<target>İşlemden vazgeçildi!</target> <source>Endless loop when traversing directory:</source> <target>Klasörlerde dolaşırken sonsuz döngü:</target> <source>Error traversing directory:</source> @@ -413,24 +424,22 @@ Her seferinde çalıştırılacak komut satırı: <target>Kendiliğinden eşleştirme için bir komut dosyası oluşturun. Komut dosyasını işlemek için dosyanın üzerinde çift tıklayabilir ya da FreeFileSync.exe <komutdosyası> şeklinde bir komut satırı yazabilirsiniz. Bu işi işletim sisteminizin görev yöneticisi ile istediğiniz zaman çalışacak şekilde ayarlayabilirsiniz.</target> <source>Help</source> <target>Yardım</target> -<source>Configuration overview:</source> -<target>Yapılandırma özeti:</target> <source>Filter files</source> <target>Dosya süzgeci</target> -<source>Status feedback</source> -<target>Durum geri bildirimi</target> -<source>Silent mode</source> -<target>Sessiz kip</target> -<source>Start minimized and write status information to a logfile</source> -<target>Simge durumuna küçültülmüş olarak başlat ve durum bilgisini günlük dosyasına yaz</target> <source>Error handling</source> <target>Hata olduğunda:</target> <source>Overview</source> <target>Özet</target> -<source>Select logfile directory:</source> -<target>Kayıt dosyası klasörünü seçin:</target> +<source>Status feedback</source> +<target>Durum geri bildirimi</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>En fazla günlük dosyası sayısı:</target> +<source>Select logfile directory:</source> +<target>Kayıt dosyası klasörünü seçin:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Kaydet</target> <source>&Load</source> @@ -695,10 +704,6 @@ Dosya ve klasör izinlerini de aktar <target>Klasör çiftleri</target> <source>Select view</source> <target>Görünümü seçin</target> -<source>Folder Comparison and Synchronization</source> -<target>Klasör Karşılaştırma ve Eşleştirme</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Geri Dönüşüm Kutusu bu sistemde desteklenmiyor!</target> <source>Set direction:</source> <target>Yönü seçin:</target> <source>Exclude temporarily</source> @@ -733,6 +738,8 @@ Dosya ve klasör izinlerini de aktar <target>Değişiklikleri şu anki yapılandırmaya kaydetmek ister misiniz?</target> <source>Configuration loaded!</source> <target>Yapılandırma yüklendi!</target> +<source>Folder Comparison and Synchronization</source> +<target>Klasör Karşılaştırma ve Eşleştirme</target> <source>Hide files that exist on left side only</source> <target>Yalnız sol yanda bulunan dosyaları gizle</target> <source>Show files that exist on left side only</source> @@ -941,10 +948,6 @@ Dosya ve klasör izinlerini de aktar <target>Varsayılan eşleştirme yönleri ayarlanıyor: Yeni dosyalar eski dosyaların üzerine yazılacak.</target> <source>The file does not contain a valid configuration:</source> <target>Dosya geçerli bir yapılandırma içermiyor:</target> -<source>Scanning:</source> -<target>Taranıyor:</target> -<source>Encoding extended time information: %x</source> -<target>Uzatılmış zaman bilgisi kodlanıyor: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Bu hatayı, klasörü boş sayacak şekilde yok sayabilirsiniz.</target> <source>Directory does not exist:</source> @@ -1021,8 +1024,6 @@ Dosya ve klasör izinlerini de aktar <target>%x öznitelikleri güncelleniyor</target> <source>Source directory does not exist anymore:</source> <target>Kaynak klasör artık yok:</target> -<source>Generating database...</source> -<target>Veri tabanı oluşturuluyor...</target> <source>Nothing to synchronize according to configuration!</source> <target>Yapılandırmaya göre eşleştirilecek bir şey yok!</target> <source>Target directory name must not be empty!</source> @@ -1043,10 +1044,14 @@ Dosya ve klasör izinlerini de aktar <target>Gereken boş disk alanı:</target> <source>Free disk space available:</source> <target>Kullanılabilir disk alanı:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Çoklu klasör çiftlerinin bir parçası olduğan bir klasör düzenlenecek! Lütfen eşleştirme ayarlarınızı gözden geçirin!</target> <source>Processing folder pair:</source> <target>İşlenen klasör çifti:</target> +<source>Generating database...</source> +<target>Veri tabanı oluşturuluyor...</target> <source>Error copying locked file %x!</source> <target>Kilitli dosya %x kopyalanırken hata!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Languages/ukrainian.lng b/BUILD/Languages/ukrainian.lng index ee2b3cc6..23b8c06d 100644 --- a/BUILD/Languages/ukrainian.lng +++ b/BUILD/Languages/ukrainian.lng @@ -7,6 +7,8 @@ <plural definition>n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2</plural definition> </header> +<source>Searching for directory %x...</source> +<target></target> <source>Show in Explorer</source> <target>Показати у Провіднику</target> <source>Open with default application</source> @@ -17,8 +19,8 @@ <target>RealtimeSync - Автоматична синхронізація</target> <source>Browse</source> <target>Переглянути</target> -<source>Invalid commandline: "%x"</source> -<target>Неправильний командний рядок: "%x"</target> +<source>Invalid commandline: %x</source> +<target>Неправильний командний рядок: %x</target> <source>Error resolving symbolic link:</source> <target>Помилка при вирішені символічного посилання:</target> <source>Show popup</source> @@ -110,10 +112,10 @@ <target>Вступна синхронізація:</target> <source>One of the FreeFileSync database files is not yet existing:</source> <target>Файла бази даних FreeFileSync ще не існує:</target> -<source>One of the FreeFileSync database entries within the following file is not yet existing:</source> -<target>Записів, які стосуються таких файлів, в базі даних FreeFileSync ще не існує:</target> <source>Error reading from synchronization database:</source> <target>Помилка при читанні з бази даних синхронізації:</target> +<source>Database files do not share a common synchronization session:</source> +<target></target> <source>An exception occurred!</source> <target>Відбулось виключення!</target> <source>Error deleting file:</source> @@ -137,6 +139,15 @@ <target>Інформація</target> <source>Fatal Error</source> <target>Критична помилка</target> +<source>Scanning:</source> +<target>Сканую:</target> +<source>Encoding extended time information: %x</source> +<target>Кодування розширеної інформації про час: %x</target> +<source> +<pluralform>[1 Thread]</pluralform> +<pluralform>[%x Threads]</pluralform> +</source> +<target></target> <source>Invalid FreeFileSync config file!</source> <target>Неправильний файл конфігурації FreeFileSync!</target> <source>File does not exist:</source> @@ -234,20 +245,18 @@ The command line is executed each time: <target>Моніторинг включений...</target> <source>Waiting for missing directories...</source> <target>Очікування пропущених каталогів...</target> -<source>Could not initialize directory monitoring:</source> -<target>Не вдалося ініціювати каталог для моніторингу:</target> <source>A directory input field is empty.</source> <target>Поле введення шляху каталога порожнє.</target> -<source>Error when monitoring directories.</source> -<target>Помилка моніторингу каталогу.</target> <source>Drag && drop</source> <target>Drag && drop</target> +<source>Could not initialize directory monitoring:</source> +<target>Не вдалося ініціювати каталог для моніторингу:</target> +<source>Error when monitoring directories.</source> +<target>Помилка моніторингу каталогу.</target> <source>Conversion error:</source> <target>Помилка перетворення:</target> <source>Error moving file:</source> <target>Помилка переміщення файла:</target> -<source>Operation aborted!</source> -<target>Операція відмінена!</target> <source>Target file already existing!</source> <target>Кінцевий файл уже існує!</target> <source>Error moving directory:</source> @@ -278,6 +287,8 @@ The command line is executed each time: <target>Помилка при записі файла:</target> <source>Error reading file:</source> <target>Помилка при читанні файла:</target> +<source>Operation aborted!</source> +<target>Операція відмінена!</target> <source>Endless loop when traversing directory:</source> <target>Зациклюванння при обході каталогу:</target> <source>Error traversing directory:</source> @@ -421,24 +432,22 @@ The command line is executed each time: <target>Створити пакетний файл для автоматичної синхронізації. Щоб запустити пакетний режим просто двічі клацніть файл або виконайте командний рядок: FreeFileSync.exe <batchfile>. Це можна також запланувати в менеджері ОС</target> <source>Help</source> <target>Допомога</target> -<source>Configuration overview:</source> -<target>Огляд конфігурації:</target> <source>Filter files</source> <target>Фільтр файлів</target> -<source>Status feedback</source> -<target>Статус оберненого зв’язку</target> -<source>Silent mode</source> -<target>Тихий режим роботи</target> -<source>Start minimized and write status information to a logfile</source> -<target>Запускати згорнутим і записувати інформацію про стан в лог-файл</target> <source>Error handling</source> <target>Обробка помилок</target> <source>Overview</source> <target>Головна</target> -<source>Select logfile directory:</source> -<target>Виберіть каталог для лог-файлів:</target> +<source>Status feedback</source> +<target>Статус оберненого зв’язку</target> +<source>Run minimized</source> +<target></target> <source>Maximum number of logfiles:</source> <target>Максимальна кількість лог-файлів:</target> +<source>Select logfile directory:</source> +<target>Виберіть каталог для лог-файлів:</target> +<source>Batch settings</source> +<target></target> <source>&Save</source> <target>&Зберегти</target> <source>&Load</source> @@ -699,10 +708,6 @@ Transfer file and directory permissions <target>Пари папок</target> <source>Select view</source> <target>Список файлів</target> -<source>Folder Comparison and Synchronization</source> -<target>Порівнювання і синхронізація папок</target> -<source>Recycle Bin not yet supported for this system!</source> -<target>Корзина не підтримується у цій системі!</target> <source>Set direction:</source> <target>Виберіть напрям:</target> <source>Exclude temporarily</source> @@ -737,6 +742,8 @@ Transfer file and directory permissions <target>Зберегти зміни в активних налаштуваннях?</target> <source>Configuration loaded!</source> <target>Налаштування синхронізації загружено!</target> +<source>Folder Comparison and Synchronization</source> +<target>Порівнювання і синхронізація папок</target> <source>Hide files that exist on left side only</source> <target>Приховати файли, які є тільки ліворуч</target> <source>Show files that exist on left side only</source> @@ -954,10 +961,6 @@ Transfer file and directory permissions </target> <source>The file does not contain a valid configuration:</source> <target>Файл не містить правильних налаштувань синхронізації:</target> -<source>Scanning:</source> -<target>Сканую:</target> -<source>Encoding extended time information: %x</source> -<target>Кодування розширеної інформації про час: %x</target> <source>You can ignore this error to consider the directory as empty.</source> <target>Ви можете проігнорувати помилку, вважаючи каталог порожнім.</target> <source>Directory does not exist:</source> @@ -1034,8 +1037,6 @@ Transfer file and directory permissions <target>Оновлення атрибутів %x</target> <source>Source directory does not exist anymore:</source> <target>Каталог-джерело вже не існує:</target> -<source>Generating database...</source> -<target>Створення бази даних...</target> <source>Nothing to synchronize according to configuration!</source> <target>Нічого синхронізувати згідно з налаштуваннями синхронізації!</target> <source>Target directory name must not be empty!</source> @@ -1056,10 +1057,14 @@ Transfer file and directory permissions <target>Потрібне вільне місце на диску:</target> <source>Free disk space available:</source> <target>Доступно вільного місця на диску:</target> +<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source> +<target></target> <source>A directory will be modified which is part of multiple folder pairs! Please review synchronization settings!</source> <target>Каталог, який є частиною декількох пар папок, буде змінений. Будь ласка, перевірте налаштування синхронізації!</target> <source>Processing folder pair:</source> <target>Обробка пари папок:</target> +<source>Generating database...</source> +<target>Створення бази даних...</target> <source>Error copying locked file %x!</source> <target>Помилка при копіюванні заблокованого файла %x!</target> <source>Data verification error: Source and target file have different content!</source> diff --git a/BUILD/Resources.dat b/BUILD/Resources.dat Binary files differindex a88b0703..357e14f7 100644 --- a/BUILD/Resources.dat +++ b/BUILD/Resources.dat diff --git a/FreeFileSync - wxWidgets v2.9.2 Beta.vcxproj b/FreeFileSync - wxWidgets v2.9.2 Beta.vcxproj index 5a878c54..541209c0 100644 --- a/FreeFileSync - wxWidgets v2.9.2 Beta.vcxproj +++ b/FreeFileSync - wxWidgets v2.9.2 Beta.vcxproj @@ -256,7 +256,6 @@ <ClCompile Include="library\statistics.cpp" /> <ClCompile Include="library\status_handler.cpp" /> <ClCompile Include="shared\app_main.cpp" /> - <ClCompile Include="shared\check_exist.cpp" /> <ClCompile Include="shared\custom_button.cpp" /> <ClCompile Include="shared\custom_combo_box.cpp" /> <ClCompile Include="shared\custom_tooltip.cpp" /> diff --git a/FreeFileSync.cbp b/FreeFileSync.cbp index 53917e8d..861db2be 100644 --- a/FreeFileSync.cbp +++ b/FreeFileSync.cbp @@ -96,6 +96,7 @@ <Add option="-pipe" /> <Add option="-mthreads" /> <Add option="-std=gnu++0x" /> + <Add option='-include "shared/warn_static.h"' /> <Add option="-D__GNUWIN32__" /> <Add option="-D__WXMSW__" /> <Add option="-DFFS_WIN" /> @@ -123,6 +124,7 @@ <Add library="libws2_32.a" /> <Add library="libwinspool.a" /> <Add library="libwinmm.a" /> + <Add library="libmpr.a" /> <Add directory="C:\Program Files\C++\Boost\stage\lib" /> </Linker> <Unit filename="WxWizFrame.fbp"> @@ -160,6 +162,7 @@ <Option target="Debug-DLL" /> </Unit> <Unit filename="library\detect_renaming.h" /> + <Unit filename="library\dir_exist_async.h" /> <Unit filename="library\dir_lock.cpp" /> <Unit filename="library\dir_lock.h" /> <Unit filename="library\error_log.cpp"> @@ -203,6 +206,8 @@ </Unit> <Unit filename="library\lock_holder.h" /> <Unit filename="library\norm_filter.h" /> + <Unit filename="library\parallel_scan.cpp" /> + <Unit filename="library\parallel_scan.h" /> <Unit filename="library\process_xml.cpp" /> <Unit filename="library\process_xml.h"> <Option target="<{~None~}>" /> @@ -239,7 +244,6 @@ </Unit> <Unit filename="shared\boost_thread_wrap.h" /> <Unit filename="shared\build_info.h" /> - <Unit filename="shared\check_exist.cpp" /> <Unit filename="shared\check_exist.h" /> <Unit filename="shared\custom_button.cpp"> <Option target="Release" /> @@ -277,6 +281,7 @@ <Unit filename="shared\file_io.h" /> <Unit filename="shared\file_traverser.cpp" /> <Unit filename="shared\file_traverser.h" /> + <Unit filename="shared\file_update_handle.h" /> <Unit filename="shared\global_func.h" /> <Unit filename="shared\guid.cpp" /> <Unit filename="shared\guid.h" /> @@ -349,7 +354,6 @@ <Unit filename="shared\zbase.h" /> <Unit filename="shared\zstring.cpp" /> <Unit filename="shared\zstring.h" /> - <Unit filename="shared\zxstring.h" /> <Unit filename="structures.cpp" /> <Unit filename="structures.h"> <Option target="<{~None~}>" /> diff --git a/FreeFileSync.vcxproj b/FreeFileSync.vcxproj index f2025456..8b1de3a2 100644 --- a/FreeFileSync.vcxproj +++ b/FreeFileSync.vcxproj @@ -94,6 +94,7 @@ <IntDir>OBJ\$(ProjectName)_$(Configuration)_$(PlatformName)_VCPP\</IntDir> <GenerateManifest>false</GenerateManifest> <TargetName>$(ProjectName)_$(PlatformName)</TargetName> + <EmbedManifest>true</EmbedManifest> </PropertyGroup> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ClCompile> @@ -107,7 +108,7 @@ <MultiProcessorCompilation>false</MultiProcessorCompilation> <DisableLanguageExtensions>false</DisableLanguageExtensions> <PrecompiledHeaderOutputFile>$(IntDir)pch.obj</PrecompiledHeaderOutputFile> - <ForcedIncludeFiles>$(ProjectDir)shared/pch.h</ForcedIncludeFiles> + <ForcedIncludeFiles>$(SolutionDir)shared/warn_static.h;$(ProjectDir)shared/pch.h</ForcedIncludeFiles> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> </ClCompile> @@ -142,7 +143,7 @@ <MultiProcessorCompilation>false</MultiProcessorCompilation> <DisableLanguageExtensions>false</DisableLanguageExtensions> <PrecompiledHeaderOutputFile>$(IntDir)pch.obj</PrecompiledHeaderOutputFile> - <ForcedIncludeFiles>$(ProjectDir)shared/pch.h</ForcedIncludeFiles> + <ForcedIncludeFiles>$(SolutionDir)shared/warn_static.h;$(ProjectDir)shared/pch.h</ForcedIncludeFiles> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> @@ -180,6 +181,7 @@ <MultiProcessorCompilation>true</MultiProcessorCompilation> <DisableLanguageExtensions>false</DisableLanguageExtensions> <InlineFunctionExpansion>Default</InlineFunctionExpansion> + <ForcedIncludeFiles>$(SolutionDir)shared/warn_static.h;</ForcedIncludeFiles> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -215,6 +217,7 @@ <MultiProcessorCompilation>true</MultiProcessorCompilation> <DisableLanguageExtensions>false</DisableLanguageExtensions> <SuppressStartupBanner>true</SuppressStartupBanner> + <ForcedIncludeFiles>$(SolutionDir)shared/warn_static.h;</ForcedIncludeFiles> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -251,12 +254,12 @@ <ClCompile Include="library\error_log.cpp" /> <ClCompile Include="library\hard_filter.cpp" /> <ClCompile Include="library\icon_buffer.cpp" /> + <ClCompile Include="library\parallel_scan.cpp" /> <ClCompile Include="library\process_xml.cpp" /> <ClCompile Include="library\resources.cpp" /> <ClCompile Include="library\statistics.cpp" /> <ClCompile Include="library\status_handler.cpp" /> <ClCompile Include="shared\app_main.cpp" /> - <ClCompile Include="shared\check_exist.cpp" /> <ClCompile Include="shared\custom_button.cpp" /> <ClCompile Include="shared\custom_combo_box.cpp" /> <ClCompile Include="shared\custom_tooltip.cpp" /> @@ -20,13 +20,21 @@ endif #support for GTKMM -FFS_CPPFLAGS+=`pkg-config --cflags gtkmm-2.4` -LINKFLAGS+=`pkg-config --libs gtkmm-2.4` +FFS_CPPFLAGS += `pkg-config --cflags gtkmm-2.4` +LINKFLAGS += `pkg-config --libs gtkmm-2.4` #support for SELinux (optional) SELINUX_EXISTING=$(shell pkg-config --exists libselinux && echo YES) ifeq ($(SELINUX_EXISTING),YES) -FFS_CPPFLAGS += -DHAVE_SELINUX +FFS_CPPFLAGS += `pkg-config --cflags libselinux ` -DHAVE_SELINUX +LINKFLAGS += `pkg-config --libs libselinux` +endif + +#support for Ubuntu Unity (optional) +UNITY_EXISTING=$(shell pkg-config --exists unity && echo YES) +ifeq ($(UNITY_EXISTING),YES) +FFS_CPPFLAGS += `pkg-config --cflags unity` -DHAVE_UBUNTU_UNITY +LINKFLAGS += `pkg-config --libs unity` endif FILE_LIST= #internal list of all *.cpp files needed for compilation @@ -50,6 +58,7 @@ FILE_LIST+=ui/search.cpp FILE_LIST+=ui/switch_to_gui.cpp FILE_LIST+=ui/msg_popup.cpp FILE_LIST+=ui/progress_indicator.cpp +FILE_LIST+=library/parallel_scan.cpp FILE_LIST+=library/custom_grid.cpp FILE_LIST+=library/error_log.cpp FILE_LIST+=library/status_handler.cpp @@ -65,10 +74,10 @@ FILE_LIST+=library/dir_lock.cpp FILE_LIST+=shared/i18n.cpp FILE_LIST+=shared/localization.cpp FILE_LIST+=shared/file_io.cpp +FILE_LIST+=shared/taskbar.cpp FILE_LIST+=shared/dir_name.cpp FILE_LIST+=shared/guid.cpp FILE_LIST+=shared/xml_base.cpp -FILE_LIST+=shared/check_exist.cpp FILE_LIST+=shared/last_error.cpp FILE_LIST+=shared/custom_tooltip.cpp FILE_LIST+=shared/file_handling.cpp diff --git a/RealtimeSync/RealtimeSync.cbp b/RealtimeSync/RealtimeSync.cbp index 867b898c..8464abfc 100644 --- a/RealtimeSync/RealtimeSync.cbp +++ b/RealtimeSync/RealtimeSync.cbp @@ -92,6 +92,7 @@ <Add library="libcomdlg32.a" /> <Add library="libws2_32.a" /> <Add library="libwinspool.a" /> + <Add library="libmpr.a" /> <Add directory="C:\Program Files\C++\Boost\stage\lib" /> </Linker> <Unit filename="WxWizDialog.fbp" /> @@ -122,7 +123,6 @@ <Unit filename="..\Shared\zstring.cpp" /> <Unit filename="..\Shared\zstring.h" /> <Unit filename="..\library\process_xml.cpp" /> - <Unit filename="..\shared\check_exist.cpp" /> <Unit filename="..\shared\dir_watcher.cpp" /> <Unit filename="..\shared\dir_watcher.h" /> <Unit filename="..\shared\dll_loader.cpp" /> @@ -154,6 +154,7 @@ <Unit filename="..\shared\privilege.cpp" /> <Unit filename="..\shared\resolve_path.cpp" /> <Unit filename="..\shared\resolve_path.h" /> + <Unit filename="..\shared\shell_execute.h" /> <Unit filename="..\shared\standard_paths.cpp" /> <Unit filename="..\shared\standard_paths.h" /> <Unit filename="..\shared\util.cpp" /> diff --git a/RealtimeSync/RealtimeSync.vcxproj b/RealtimeSync/RealtimeSync.vcxproj index cdeb18b4..5936b74c 100644 --- a/RealtimeSync/RealtimeSync.vcxproj +++ b/RealtimeSync/RealtimeSync.vcxproj @@ -106,7 +106,7 @@ <MultiProcessorCompilation>false</MultiProcessorCompilation> <DisableLanguageExtensions>false</DisableLanguageExtensions> <PrecompiledHeaderOutputFile>$(IntDir)pch.obj</PrecompiledHeaderOutputFile> - <ForcedIncludeFiles>$(ProjectDir)/../shared/pch.h</ForcedIncludeFiles> + <ForcedIncludeFiles>$(SolutionDir)shared/warn_static.h;$(ProjectDir)/../shared/pch.h</ForcedIncludeFiles> <DebugInformationFormat>EditAndContinue</DebugInformationFormat> </ClCompile> <Link> @@ -136,7 +136,7 @@ <MultiProcessorCompilation>false</MultiProcessorCompilation> <DisableLanguageExtensions>false</DisableLanguageExtensions> <PrecompiledHeaderOutputFile>$(IntDir)pch.obj</PrecompiledHeaderOutputFile> - <ForcedIncludeFiles>$(ProjectDir)/../shared/pch.h</ForcedIncludeFiles> + <ForcedIncludeFiles>$(SolutionDir)shared/warn_static.h;$(ProjectDir)/../shared/pch.h</ForcedIncludeFiles> <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> <SuppressStartupBanner>true</SuppressStartupBanner> </ClCompile> @@ -169,6 +169,7 @@ <RuntimeLibrary>MultiThreaded</RuntimeLibrary> <MultiProcessorCompilation>true</MultiProcessorCompilation> <DisableLanguageExtensions>false</DisableLanguageExtensions> + <ForcedIncludeFiles>$(SolutionDir)shared/warn_static.h;</ForcedIncludeFiles> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -204,6 +205,7 @@ <MultiProcessorCompilation>true</MultiProcessorCompilation> <DisableLanguageExtensions>false</DisableLanguageExtensions> <SuppressStartupBanner>true</SuppressStartupBanner> + <ForcedIncludeFiles>$(SolutionDir)shared/warn_static.h;</ForcedIncludeFiles> </ClCompile> <Link> <SubSystem>Windows</SubSystem> @@ -226,7 +228,6 @@ </ItemDefinitionGroup> <ItemGroup> <ClCompile Include="..\library\process_xml.cpp" /> - <ClCompile Include="..\shared\check_exist.cpp" /> <ClCompile Include="..\shared\custom_button.cpp" /> <ClCompile Include="..\shared\dir_watcher.cpp" /> <ClCompile Include="..\shared\dll_loader.cpp" /> diff --git a/RealtimeSync/application.cpp b/RealtimeSync/application.cpp index 196e0ecc..6b7e81e9 100644 --- a/RealtimeSync/application.cpp +++ b/RealtimeSync/application.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "application.h" #include "main_dlg.h" #include <wx/event.h> diff --git a/RealtimeSync/application.h b/RealtimeSync/application.h index a8a931e0..6b5481ad 100644 --- a/RealtimeSync/application.h +++ b/RealtimeSync/application.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef REALTIMESYNCAPP_H #define REALTIMESYNCAPP_H diff --git a/RealtimeSync/main_dlg.cpp b/RealtimeSync/main_dlg.cpp index 860ddcef..e8851428 100644 --- a/RealtimeSync/main_dlg.cpp +++ b/RealtimeSync/main_dlg.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "main_dlg.h" #include "resources.h" #include "../shared/custom_button.h" diff --git a/RealtimeSync/main_dlg.h b/RealtimeSync/main_dlg.h index 2ce04557..54d4d50c 100644 --- a/RealtimeSync/main_dlg.h +++ b/RealtimeSync/main_dlg.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef REALTIMESYNCMAIN_H #define REALTIMESYNCMAIN_H diff --git a/RealtimeSync/makefile b/RealtimeSync/makefile index 2e123af2..3bc19061 100644 --- a/RealtimeSync/makefile +++ b/RealtimeSync/makefile @@ -27,7 +27,6 @@ FILE_LIST+=xml_ffs.cpp FILE_LIST+=../library/process_xml.cpp FILE_LIST+=../structures.cpp FILE_LIST+=../shared/util.cpp -FILE_LIST+=../shared/check_exist.cpp FILE_LIST+=../shared/i18n.cpp FILE_LIST+=../shared/localization.cpp FILE_LIST+=../shared/dir_watcher.cpp diff --git a/RealtimeSync/resources.cpp b/RealtimeSync/resources.cpp index 3f7f7988..e9231623 100644 --- a/RealtimeSync/resources.cpp +++ b/RealtimeSync/resources.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "resources.h" #include <wx/wfstream.h> #include <wx/zipstrm.h> diff --git a/RealtimeSync/resources.h b/RealtimeSync/resources.h index 1237c811..fec82358 100644 --- a/RealtimeSync/resources.h +++ b/RealtimeSync/resources.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef RESOURCES_H_INCLUDED #define RESOURCES_H_INCLUDED diff --git a/RealtimeSync/tray_menu.cpp b/RealtimeSync/tray_menu.cpp index e6d5f0d4..98c66cb5 100644 --- a/RealtimeSync/tray_menu.cpp +++ b/RealtimeSync/tray_menu.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "tray_menu.h" #include <algorithm> #include <iterator> @@ -299,8 +299,8 @@ rts::MonitorResponse rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& if (config.commandline.empty()) { - std::wstring errorMsg = _("Invalid commandline: \"%x\""); - replace(errorMsg, L"%x", config.commandline); + std::wstring errorMsg = _("Invalid commandline: %x"); + replace(errorMsg, L"%x", L"\"" + config.commandline + L"\""); throw FileError(errorMsg); } diff --git a/RealtimeSync/tray_menu.h b/RealtimeSync/tray_menu.h index 01fbba17..80f2d26c 100644 --- a/RealtimeSync/tray_menu.h +++ b/RealtimeSync/tray_menu.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef TRAYMENU_H_INCLUDED #define TRAYMENU_H_INCLUDED diff --git a/RealtimeSync/watcher.cpp b/RealtimeSync/watcher.cpp index 059f420b..b17c63ba 100644 --- a/RealtimeSync/watcher.cpp +++ b/RealtimeSync/watcher.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "watcher.h" #include "../shared/file_handling.h" #include "../shared/i18n.h" @@ -120,13 +120,13 @@ rts::WaitResult rts::waitForChanges(const std::vector<Zstring>& dirNames, WaitCa if (!changedFiles.empty()) { -/* - std::for_each(changedFiles.begin(), changedFiles.end(), - [](const Zstring& fn) { wxMessageBox(toWx(fn));}); + /* + std::for_each(changedFiles.begin(), changedFiles.end(), + [](const Zstring& fn) { wxMessageBox(toWx(fn));}); - const wxString filename = toWx(changedFiles[0]); - ::wxSetEnv(wxT("RTS_CHANGE"), filename); -*/ + const wxString filename = toWx(changedFiles[0]); + ::wxSetEnv(wxT("RTS_CHANGE"), filename); + */ return CHANGE_DETECTED; //directory change detected } diff --git a/RealtimeSync/watcher.h b/RealtimeSync/watcher.h index 7208e7fb..04a0d860 100644 --- a/RealtimeSync/watcher.h +++ b/RealtimeSync/watcher.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef WATCHER_H_INCLUDED #define WATCHER_H_INCLUDED diff --git a/RealtimeSync/xml_ffs.cpp b/RealtimeSync/xml_ffs.cpp index 3424dea2..ed0a4331 100644 --- a/RealtimeSync/xml_ffs.cpp +++ b/RealtimeSync/xml_ffs.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "xml_ffs.h" #include "../shared/standard_paths.h" #include "../shared/global_func.h" diff --git a/RealtimeSync/xml_ffs.h b/RealtimeSync/xml_ffs.h index fb48712a..f359c039 100644 --- a/RealtimeSync/xml_ffs.h +++ b/RealtimeSync/xml_ffs.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef XMLFREEFILESYNC_H_INCLUDED #define XMLFREEFILESYNC_H_INCLUDED diff --git a/RealtimeSync/xml_proc.cpp b/RealtimeSync/xml_proc.cpp index e3d7f270..b9839f9c 100644 --- a/RealtimeSync/xml_proc.cpp +++ b/RealtimeSync/xml_proc.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "xml_proc.h" #include <wx/filefn.h> #include "../shared/i18n.h" diff --git a/RealtimeSync/xml_proc.h b/RealtimeSync/xml_proc.h index 3e2549b0..37d2e029 100644 --- a/RealtimeSync/xml_proc.h +++ b/RealtimeSync/xml_proc.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef XMLPROCESSING_H_INCLUDED #define XMLPROCESSING_H_INCLUDED diff --git a/algorithm.cpp b/algorithm.cpp index cfc2997a..a059e693 100644 --- a/algorithm.cpp +++ b/algorithm.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "algorithm.h" #include <iterator> #include <stdexcept> @@ -1265,8 +1265,8 @@ private: void zen::addHardFiltering(BaseDirMapping& baseMap, const Zstring& excludeFilter) { - ApplyHardFilter<STRATEGY_AND>(*HardFilter::FilterRef( - new NameFilter(FilterConfig().includeFilter, excludeFilter))).execute(baseMap); + ApplyHardFilter<STRATEGY_AND>(*HardFilter::FilterRef( + new NameFilter(FilterConfig().includeFilter, excludeFilter))).execute(baseMap); } diff --git a/algorithm.h b/algorithm.h index ad5d2e30..16bb11eb 100644 --- a/algorithm.h +++ b/algorithm.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef ALGORITHM_H_INCLUDED #define ALGORITHM_H_INCLUDED diff --git a/comparison.cpp b/comparison.cpp index 0f2b1dab..74742898 100644 --- a/comparison.cpp +++ b/comparison.cpp @@ -3,29 +3,19 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "comparison.h" -#include <map> +#include "library/parallel_scan.h" +#include "shared/resolve_path.h" +#include "shared/i18n.h" #include <stdexcept> -#include <memory> -#include "shared/global_func.h" +#include "library/dir_exist_async.h" +#include "shared/string_conv.h" #include "shared/loki/ScopeGuard.h" -#include "shared/i18n.h" -#include <wx/msgdlg.h> -#include <wx/log.h> -#include <boost/bind.hpp> +#include "library/binary.h" #include "algorithm.h" #include "shared/util.h" -#include "shared/string_conv.h" -#include "shared/file_handling.h" -#include "shared/resolve_path.h" -#include "shared/last_error.h" -#include "shared/file_traverser.h" -#include "file_hierarchy.h" -#include "library/binary.h" #include "library/cmp_filetime.h" -#include "library/lock_holder.h" -#include "library/db_file.h" #ifdef FFS_WIN #include "shared/perf.h" @@ -34,7 +24,7 @@ using namespace zen; -std::vector<zen::FolderPairCfg> zen::extractCompareCfg(const MainConfiguration& mainCfg) +std::vector<FolderPairCfg> zen::extractCompareCfg(const MainConfiguration& mainCfg) { //merge first and additional pairs std::vector<FolderPairEnh> allPairs; @@ -46,374 +36,41 @@ std::vector<zen::FolderPairCfg> zen::extractCompareCfg(const MainConfiguration& std::vector<FolderPairCfg> output; std::transform(allPairs.begin(), allPairs.end(), std::back_inserter(output), [&](const FolderPairEnh& enhPair) -> FolderPairCfg - { - return FolderPairCfg(getFormattedDirectoryName(enhPair.leftDirectory), //ensure they end with FILE_NAME_SEPARATOR and replace macros - getFormattedDirectoryName(enhPair.rightDirectory), - normalizeFilters(mainCfg.globalFilter, enhPair.localFilter), - mainCfg.handleSymlinks, - enhPair.altSyncConfig.get() ? enhPair.altSyncConfig->syncConfiguration : mainCfg.syncConfiguration); - }); - return output; -} - - -class DirCallback; - -struct TraverserConfig -{ -public: - TraverserConfig(SymLinkHandling handleSymlinks, - const HardFilter::FilterRef& filter, - std::set<Zstring>& failedReads, - ProcessCallback& handler) : - handleSymlinks_(handleSymlinks), - textScanning(toZ(_("Scanning:")) + Zstr(" \n")), - filterInstance(filter), - statusHandler(handler), - failedReads_(failedReads) {} - - typedef std::shared_ptr<DirCallback> CallbackPointer; - std::vector<CallbackPointer> callBackBox; //collection of callback pointers to handle ownership - - const SymLinkHandling handleSymlinks_; - const Zstring textScanning; - const HardFilter::FilterRef filterInstance; //always bound! - ProcessCallback& statusHandler; - - std::set<Zstring>& failedReads_; //relative postfixed names of directories that could not be read (empty for root) -}; - - -class DirCallback : public zen::TraverseCallback -{ -public: - DirCallback(TraverserConfig& config, - const Zstring& relNameParentPf, //postfixed with FILE_NAME_SEPARATOR! - DirContainer& output) : - cfg(config), - relNameParentPf_(relNameParentPf), - output_(output) {} - - virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details); - virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details); - virtual ReturnValDir onDir (const Zchar* shortName, const Zstring& fullName); - virtual HandleError onError (const std::wstring& errorText); - -private: - TraverserConfig& cfg; - const Zstring relNameParentPf_; - DirContainer& output_; -}; - - -void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) -{ - const Zstring fileNameShort = shortName; - - //do not list the database file(s) sync.ffs_db, sync.x64.ffs_db, etc. or lock files - if (endsWith(fileNameShort, SYNC_DB_FILE_ENDING) || - endsWith(fileNameShort, LOCK_FILE_ENDING)) - return; - - //assemble status message (performance optimized) = textScanning + wxT("\"") + fullName + wxT("\"") - Zstring statusText = cfg.textScanning; - statusText.reserve(statusText.length() + fullName.length() + 2); - statusText += Zchar('\"'); - statusText += fullName; - statusText += Zchar('\"'); - - //update UI/commandline status information - cfg.statusHandler.reportInfo(utf8CvrtTo<wxString>(statusText)); - - //------------------------------------------------------------------------------------ - //apply filter before processing (use relative name!) - if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort)) - return; - - //warning: for windows retrieveFileID is slow as hell! approximately 3 * 10^-4 s per file! - //therefore only large files (that take advantage of detection of renaming when synchronizing) should be evaluated! - //testcase: scanning only files larger than 1 MB results in performance loss of 6% - - //#warning this call is NOT acceptable for Linux! - // //Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!) - // const util::FileID fileIdentifier = details.fileSize >= cfg.detectRenameThreshold_ ? - // util::retrieveFileID(fullName) : - // util::FileID(); - - output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTimeRaw, details.fileSize)); - - //add 1 element to the progress indicator - cfg.statusHandler.updateProcessedData(1, 0); //NO performance issue at all - cfg.statusHandler.requestUiRefresh(); //may throw -} - - -void DirCallback::onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) -{ - if (cfg.handleSymlinks_ == SYMLINK_IGNORE) - return; - - //assemble status message (performance optimized) = textScanning + wxT("\"") + fullName + wxT("\"") - Zstring statusText = cfg.textScanning; - statusText.reserve(statusText.length() + fullName.length() + 2); - statusText += Zchar('\"'); - statusText += fullName; - statusText += Zchar('\"'); - - //update UI/commandline status information - cfg.statusHandler.reportInfo(utf8CvrtTo<wxString>(statusText)); - - //------------------------------------------------------------------------------------ - const Zstring& relName = relNameParentPf_ + shortName; - - //apply filter before processing (use relative name!) - if (!cfg.filterInstance->passFileFilter(relName)) //always use file filter: Link type may not be "stable" on Linux! - return; - - output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTimeRaw, details.targetPath, details.dirLink ? LinkDescriptor::TYPE_DIR : LinkDescriptor::TYPE_FILE)); - - //add 1 element to the progress indicator - cfg.statusHandler.updateProcessedData(1, 0); //NO performance issue at all - cfg.statusHandler.requestUiRefresh(); //may throw -} - - -TraverseCallback::ReturnValDir DirCallback::onDir(const Zchar* shortName, const Zstring& fullName) -{ - //assemble status message (performance optimized) = textScanning + wxT("\"") + fullName + wxT("\"") - Zstring statusText = cfg.textScanning; - statusText.reserve(statusText.length() + fullName.length() + 2); - statusText += Zchar('\"'); - statusText += fullName; - statusText += Zchar('\"'); - - //update UI/commandline status information - cfg.statusHandler.reportInfo(utf8CvrtTo<wxString>(statusText)); - - //------------------------------------------------------------------------------------ - const Zstring& relName = relNameParentPf_ + shortName; - - //apply filter before processing (use relative name!) - bool subObjMightMatch = true; - if (!cfg.filterInstance->passDirFilter(relName, &subObjMightMatch)) - { - if (!subObjMightMatch) - return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //do NOT traverse subdirs - } - else - { - cfg.statusHandler.updateProcessedData(1, 0); //NO performance issue at all - cfg.statusHandler.requestUiRefresh(); //may throw - } - - DirContainer& subDir = output_.addSubDir(shortName); - - TraverserConfig::CallbackPointer subDirCallback = std::make_shared<DirCallback>(cfg, relName + FILE_NAME_SEPARATOR, subDir); - cfg.callBackBox.push_back(subDirCallback); //handle lifetime - //attention: ensure directory filtering is applied later to exclude actually filtered directories - return ReturnValDir(Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_CONTINUE>(), *subDirCallback.get()); -} - - -DirCallback::HandleError DirCallback::onError(const std::wstring& errorText) -{ - switch (cfg.statusHandler.reportError(errorText)) - { - case ProcessCallback::IGNORE_ERROR: - cfg.failedReads_.insert(relNameParentPf_); - return TRAV_ERROR_IGNORE; - - case ProcessCallback::RETRY: - return TRAV_ERROR_RETRY; - } - - assert(false); - return TRAV_ERROR_IGNORE; -} - - -//------------------------------------------------------------------------------------------ -struct DirBufferKey -{ - DirBufferKey(const Zstring& dirname, - const HardFilter::FilterRef& filterIn, //filter interface: always bound by design! - SymLinkHandling handleSymlinks) : - directoryName(dirname), - filter(filterIn), - handleSymlinks_(handleSymlinks) {} - - const Zstring directoryName; - const HardFilter::FilterRef filter; - const SymLinkHandling handleSymlinks_; - - bool operator<(const DirBufferKey& other) const - { - if (handleSymlinks_ != other.handleSymlinks_) - return handleSymlinks_ < other.handleSymlinks_; - - if (!EqualFilename()(directoryName, other.directoryName)) - return LessFilename()(directoryName, other.directoryName); - - return *filter < *other.filter; - } -}; - - -struct DirBufferValue -{ - DirContainer dirCont; - std::set<Zstring> failedReads; //relative postfixed names of directories that could not be read (empty for root), e.g. access denied, or temporal network drop -}; - - -//------------------------------------------------------------------------------------------ -class CompareProcess::DirectoryBuffer //buffer multiple scans of the same directories -{ -public: - DirectoryBuffer(ProcessCallback& procCallback) : - procCallback_(procCallback) {} - - const DirBufferValue& getDirectoryDescription(const Zstring& directoryPostfixed, const HardFilter::FilterRef& filter, SymLinkHandling handleSymlinks) { - const DirBufferKey searchKey(directoryPostfixed, filter, handleSymlinks); - - auto iter = buffer.find(searchKey); - if (iter != buffer.end()) - return iter->second; //entry found in buffer; return - else - return insertIntoBuffer(searchKey); //entry not found; create new one - } - -private: - typedef std::map<DirBufferKey, DirBufferValue> BufferType; - - DirBufferValue& insertIntoBuffer(const DirBufferKey& newKey); - - BufferType buffer; - - ProcessCallback& procCallback_; -}; -//------------------------------------------------------------------------------------------ - - -#ifdef FFS_WIN -class DstHackCallbackImpl : public DstHackCallback -{ -public: - DstHackCallbackImpl(ProcessCallback& procCallback) : - textApplyingDstHack(toZ(_("Encoding extended time information: %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""))), - procCallback_(procCallback) {} - -private: - virtual void requestUiRefresh(const Zstring& filename) //applying DST hack imposes significant one-time performance drawback => callback to inform user - { - Zstring statusText = textApplyingDstHack; - statusText.Replace(Zstr("%x"), filename); - procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText)); - } - - const Zstring textApplyingDstHack; - ProcessCallback& procCallback_; -}; -#endif - - -DirBufferValue& CompareProcess::DirectoryBuffer::insertIntoBuffer(const DirBufferKey& newKey) -{ - DirBufferValue& bufferVal = buffer[newKey]; //default construct value - - if (!newKey.directoryName.empty() && - zen::dirExists(newKey.directoryName)) //folder existence already checked in startCompareProcess(): do not treat as error when arriving here! - { - TraverserConfig travCfg(newKey.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy - newKey.filter, - bufferVal.failedReads, - procCallback_); - - DirCallback traverser(travCfg, - Zstring(), - bufferVal.dirCont); - - bool followSymlinks = false; - switch (newKey.handleSymlinks_) - { - case SYMLINK_IGNORE: - followSymlinks = false; //=> symlinks will be reported via onSymlink() where they are excluded - break; - case SYMLINK_USE_DIRECTLY: - followSymlinks = false; - break; - case SYMLINK_FOLLOW_LINK: - followSymlinks = true; - break; - } - - DstHackCallback* dstCallbackPtr = NULL; -#ifdef FFS_WIN - DstHackCallbackImpl dstCallback(procCallback_); - dstCallbackPtr = &dstCallback; -#endif - - //get all files and folders from directoryPostfixed (and subdirectories) - traverseFolder(newKey.directoryName, followSymlinks, traverser, dstCallbackPtr); //exceptions may be thrown! - } - return bufferVal; + return FolderPairCfg(getFormattedDirectoryName(enhPair.leftDirectory), //ensure they end with FILE_NAME_SEPARATOR and replace macros + getFormattedDirectoryName(enhPair.rightDirectory), + normalizeFilters(mainCfg.globalFilter, enhPair.localFilter), + mainCfg.handleSymlinks, + enhPair.altSyncConfig.get() ? enhPair.altSyncConfig->syncConfiguration : mainCfg.syncConfiguration); + }); + return output; } - //------------------------------------------------------------------------------------------ namespace { -void foldersAreValidForComparison(const std::vector<FolderPairCfg>& folderPairsForm, ProcessCallback& procCallback) +void checkForIncompleteInput(const std::vector<FolderPairCfg>& folderPairsForm, ProcessCallback& procCallback) { bool nonEmptyPairFound = false; //check if user entered at least one folder pair bool partiallyFilledPairFound = false; - const std::wstring additionalInfo = _("You can ignore this error to consider the directory as empty."); - - for (std::vector<FolderPairCfg>::const_iterator i = folderPairsForm.begin(); i != folderPairsForm.end(); ++i) + std::for_each(folderPairsForm.begin(), folderPairsForm.end(), + [&](const FolderPairCfg& fpCfg) { - if (!i->leftDirectoryFmt.empty() || !i->rightDirectoryFmt.empty()) //may be partially filled though + if (!fpCfg.leftDirectoryFmt.empty() || !fpCfg.rightDirectoryFmt.empty()) //may be partially filled though nonEmptyPairFound = true; - if ((i->leftDirectoryFmt.empty() && !i->rightDirectoryFmt.empty()) || - (!i->leftDirectoryFmt.empty() && i->rightDirectoryFmt.empty())) + if ((fpCfg.leftDirectoryFmt.empty() && !fpCfg.rightDirectoryFmt.empty()) || + (!fpCfg.leftDirectoryFmt.empty() && fpCfg.rightDirectoryFmt.empty())) partiallyFilledPairFound = true; - - //check if folders exist - if (!i->leftDirectoryFmt.empty()) - while (!zen::dirExists(i->leftDirectoryFmt)) - { - std::wstring errorMessage = _("Directory does not exist:") + "\n" + "\"" + i->leftDirectoryFmt + "\""; - ProcessCallback::Response rv = procCallback.reportError(errorMessage + "\n\n" + additionalInfo + " " + zen::getLastErrorFormatted()); - - if (rv == ProcessCallback::IGNORE_ERROR) - break; - else if (rv == ProcessCallback::RETRY) - ; //continue with loop - else - throw std::logic_error("Programming Error: Unknown return value! (2)"); - } - - if (!i->rightDirectoryFmt.empty()) - while (!zen::dirExists(i->rightDirectoryFmt)) - { - std::wstring errorMessage = _("Directory does not exist:") + "\n" + "\"" + i->rightDirectoryFmt + "\""; - ProcessCallback::Response rv = procCallback.reportError(errorMessage + "\n\n" + additionalInfo + " " + zen::getLastErrorFormatted()); - if (rv == ProcessCallback::IGNORE_ERROR) - break; - else if (rv == ProcessCallback::RETRY) - ; //continue with loop - else - throw std::logic_error("Programming Error: Unknown return value! (3)"); - } - } + }); //check for empty entries if (!nonEmptyPairFound || partiallyFilledPairFound) { while (true) { + const std::wstring additionalInfo = _("You can ignore this error to consider the directory as empty."); const ProcessCallback::Response rv = procCallback.reportError(_("A directory input field is empty.") + " \n\n" + + "(" + additionalInfo + ")"); if (rv == ProcessCallback::IGNORE_ERROR) @@ -427,6 +84,29 @@ void foldersAreValidForComparison(const std::vector<FolderPairCfg>& folderPairsF } +void checkDirectoryExistence(const std::set<Zstring, LessFilename>& dirnames, ProcessCallback& procCallback) +{ + std::for_each(dirnames.begin(), dirnames.end(), + [&](const Zstring& dirname) + { + if (!dirname.empty()) + while (!dirExistsUpdating(dirname, procCallback)) + { + const std::wstring additionalInfo = _("You can ignore this error to consider the directory as empty."); + std::wstring errorMessage = _("Directory does not exist:") + "\n" + "\"" + dirname + "\""; + ProcessCallback::Response rv = procCallback.reportError(errorMessage + "\n\n" + additionalInfo /* + " " + getLastErrorFormatted()*/); + + if (rv == ProcessCallback::IGNORE_ERROR) + break; + else if (rv == ProcessCallback::RETRY) + ; //continue with loop + else + throw std::logic_error("Programming Error: Unknown return value! (2)"); + } + }); +} + + namespace { struct EqualDependentDirectory : public std::binary_function<Zstring, Zstring, bool> @@ -453,17 +133,17 @@ wxString checkFolderDependency(const std::vector<FolderPairCfg>& folderPairsForm dependentDirs.push_back(std::make_pair(toWx(i->leftDirectoryFmt), toWx(i->rightDirectoryFmt))); } - wxString warnignMsg; + wxString warningMsg; if (!dependentDirs.empty()) { - warnignMsg = _("Directories are dependent! Be careful when setting up synchronization rules:"); + warningMsg = _("Directories are dependent! Be careful when setting up synchronization rules:"); for (auto i = dependentDirs.begin(); i != dependentDirs.end(); ++i) - warnignMsg += wxString(L"\n\n") + + warningMsg += wxString(L"\n\n") + "\"" + i->first + "\"\n" + "\"" + i->second + "\""; } - return warnignMsg; + return warningMsg; } @@ -562,10 +242,7 @@ CompareProcess::CompareProcess(size_t fileTimeTol, fileTimeTolerance(fileTimeTol), m_warnings(warnings), procCallback(handler), - txtComparingContentOfFiles(toZ(_("Comparing content of files %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false)) -{ - directoryBuffer.reset(new DirectoryBuffer(handler)); -} + txtComparingContentOfFiles(toZ(_("Comparing content of files %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""), false)) {} CompareProcess::~CompareProcess() {} //std::auto_ptr does not work with forward declarations (Or we need a non-inline ~CompareProcess())! @@ -575,10 +252,11 @@ void CompareProcess::startCompareProcess(const std::vector<FolderPairCfg>& direc const CompareVariant cmpVar, FolderComparison& output) { -#ifdef NDEBUG - wxLogNull noWxLogs; //hide wxWidgets log messages in release build -#endif - + /* + #ifdef NDEBUG + wxLogNull noWxLogs; //hide wxWidgets log messages in release build + #endif + */ //PERF_START; @@ -587,8 +265,18 @@ void CompareProcess::startCompareProcess(const std::vector<FolderPairCfg>& direc //-------------------some basic checks:------------------------------------------ - //ensure that folders are valid - foldersAreValidForComparison(directoryPairs, procCallback); + checkForIncompleteInput(directoryPairs, procCallback); + + + std::set<Zstring, LessFilename> dirnames; + std::for_each(directoryPairs.begin(), directoryPairs.end(), + [&](const FolderPairCfg& fpCfg) + { + dirnames.insert(fpCfg.leftDirectoryFmt); + dirnames.insert(fpCfg.rightDirectoryFmt); + }); + + checkDirectoryExistence(dirnames, procCallback); { //check if folders have dependencies @@ -602,6 +290,58 @@ void CompareProcess::startCompareProcess(const std::vector<FolderPairCfg>& direc try { + //------------------- fill directory buffer --------------------------------------------------- + std::set<DirectoryKey> keysToRead; + + std::for_each(directoryPairs.begin(), directoryPairs.end(), + [&](const FolderPairCfg& fpCfg) + { + keysToRead.insert(DirectoryKey(fpCfg.leftDirectoryFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + keysToRead.insert(DirectoryKey(fpCfg.rightDirectoryFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + }); + + class CbImpl : public FillBufferCallback + { + public: + CbImpl(ProcessCallback& pcb) : + procCallback_(pcb), + itemsReported(0) {} + + virtual void reportStatus(const std::wstring& statusMsg, int itemTotal) + { + procCallback_.updateProcessedData(itemTotal - itemsReported, 0); //processed data is communicated in subfunctions! + itemsReported = itemTotal; + + procCallback_.reportInfo(statusMsg); //may throw + //procCallback_.requestUiRefresh(); //already called by reportInfo() + } + + virtual HandleError reportError(const std::wstring& errorText) + { + switch (procCallback_.reportError(errorText)) + { + case ProcessCallback::IGNORE_ERROR: + return TRAV_ERROR_IGNORE; + + case ProcessCallback::RETRY: + return TRAV_ERROR_RETRY; + } + + assert(false); + return TRAV_ERROR_IGNORE; + } + + private: + ProcessCallback& procCallback_; + int itemsReported; + } cb(procCallback); + + fillBuffer(keysToRead, //in + directoryBuffer, //out + cb, + UI_UPDATE_INTERVAL / 4); //every ~25 ms + //------------------------------------------------------------------------------------------- + //prevent shutdown while (binary) comparison is in progress util::DisableStandby dummy2; (void)dummy2; @@ -662,7 +402,7 @@ void CompareProcess::startCompareProcess(const std::vector<FolderPairCfg>& direc //--------------------assemble conflict descriptions--------------------------- //check for very old dates or date2s in the future -wxString getConflictInvalidDate(const Zstring& fileNameFull, zen::Int64 utcTime) +wxString getConflictInvalidDate(const Zstring& fileNameFull, Int64 utcTime) { wxString msg = _("File %x has an invalid date!"); replace(msg, L"%x", wxString(L"\"") + fileNameFull + "\""); @@ -706,65 +446,65 @@ wxString getConflictSameDateDiffSize(const FileMapping& fileObj) //----------------------------------------------------------------------------- -void CompareProcess::categorizeSymlinkByTime(SymLinkMapping* linkObj) const +void CompareProcess::categorizeSymlinkByTime(SymLinkMapping& linkObj) const { const CmpFileTime timeCmp(fileTimeTolerance); //categorize symlinks that exist on both sides if ( //special handling: if symlinks have the same "content" they are seen as equal while other metadata is ignored #ifdef FFS_WIN //type of symbolic link is relevant for Windows only - linkObj->getLinkType<LEFT_SIDE>() == linkObj->getLinkType<RIGHT_SIDE>() && + linkObj.getLinkType<LEFT_SIDE>() == linkObj.getLinkType<RIGHT_SIDE>() && #endif - !linkObj->getTargetPath<LEFT_SIDE>().empty() && - linkObj->getTargetPath<LEFT_SIDE>() == linkObj->getTargetPath<RIGHT_SIDE>()) + !linkObj.getTargetPath<LEFT_SIDE>().empty() && + linkObj.getTargetPath<LEFT_SIDE>() == linkObj.getTargetPath<RIGHT_SIDE>()) { //symlinks have same "content" - if (linkObj->getShortName<LEFT_SIDE>() == linkObj->getShortName<RIGHT_SIDE>() && - timeCmp.getResult(linkObj->getLastWriteTime<LEFT_SIDE>(), - linkObj->getLastWriteTime<RIGHT_SIDE>()) == CmpFileTime::TIME_EQUAL) - linkObj->setCategory<SYMLINK_EQUAL>(); + if (linkObj.getShortName<LEFT_SIDE>() == linkObj.getShortName<RIGHT_SIDE>() && + timeCmp.getResult(linkObj.getLastWriteTime<LEFT_SIDE>(), + linkObj.getLastWriteTime<RIGHT_SIDE>()) == CmpFileTime::TIME_EQUAL) + linkObj.setCategory<SYMLINK_EQUAL>(); else - linkObj->setCategory<SYMLINK_DIFFERENT_METADATA>(); + linkObj.setCategory<SYMLINK_DIFFERENT_METADATA>(); return; } - switch (timeCmp.getResult(linkObj->getLastWriteTime<LEFT_SIDE>(), - linkObj->getLastWriteTime<RIGHT_SIDE>())) + switch (timeCmp.getResult(linkObj.getLastWriteTime<LEFT_SIDE>(), + linkObj.getLastWriteTime<RIGHT_SIDE>())) { case CmpFileTime::TIME_EQUAL: if ( #ifdef FFS_WIN //type of symbolic link is relevant for Windows only - linkObj->getLinkType<LEFT_SIDE>() == linkObj->getLinkType<RIGHT_SIDE>() && + linkObj.getLinkType<LEFT_SIDE>() == linkObj.getLinkType<RIGHT_SIDE>() && #endif - linkObj->getTargetPath<LEFT_SIDE>() == linkObj->getTargetPath<RIGHT_SIDE>()) //may both be empty if following link failed + linkObj.getTargetPath<LEFT_SIDE>() == linkObj.getTargetPath<RIGHT_SIDE>()) //may both be empty if following link failed { - if (linkObj->getShortName<LEFT_SIDE>() == linkObj->getShortName<RIGHT_SIDE>()) - linkObj->setCategory<SYMLINK_EQUAL>(); + if (linkObj.getShortName<LEFT_SIDE>() == linkObj.getShortName<RIGHT_SIDE>()) + linkObj.setCategory<SYMLINK_EQUAL>(); else - linkObj->setCategory<SYMLINK_DIFFERENT_METADATA>(); + linkObj.setCategory<SYMLINK_DIFFERENT_METADATA>(); } else { wxString conflictMsg = _("Conflict detected:") + "\n" + _("Symlinks %x have the same date but a different target!"); - replace(conflictMsg, L"%x", wxString(L"\"") + linkObj->getRelativeName<LEFT_SIDE>() + "\""); - linkObj->setCategoryConflict(conflictMsg); + replace(conflictMsg, L"%x", wxString(L"\"") + linkObj.getRelativeName<LEFT_SIDE>() + "\""); + linkObj.setCategoryConflict(conflictMsg); } break; case CmpFileTime::TIME_LEFT_NEWER: - linkObj->setCategory<SYMLINK_LEFT_NEWER>(); + linkObj.setCategory<SYMLINK_LEFT_NEWER>(); break; case CmpFileTime::TIME_RIGHT_NEWER: - linkObj->setCategory<SYMLINK_RIGHT_NEWER>(); + linkObj.setCategory<SYMLINK_RIGHT_NEWER>(); break; case CmpFileTime::TIME_LEFT_INVALID: - linkObj->setCategoryConflict(getConflictInvalidDate(linkObj->getFullName<LEFT_SIDE>(), linkObj->getLastWriteTime<LEFT_SIDE>())); + linkObj.setCategoryConflict(getConflictInvalidDate(linkObj.getFullName<LEFT_SIDE>(), linkObj.getLastWriteTime<LEFT_SIDE>())); break; case CmpFileTime::TIME_RIGHT_INVALID: - linkObj->setCategoryConflict(getConflictInvalidDate(linkObj->getFullName<RIGHT_SIDE>(), linkObj->getLastWriteTime<RIGHT_SIDE>())); + linkObj.setCategoryConflict(getConflictInvalidDate(linkObj.getFullName<RIGHT_SIDE>(), linkObj.getLastWriteTime<RIGHT_SIDE>())); break; } } @@ -788,7 +528,8 @@ void CompareProcess::compareByTimeSize(const std::vector<FolderPairCfg>& directo performComparison(*pair, output.back(), uncategorizedFiles, uncategorizedLinks); //finish symlink categorization - std::for_each(uncategorizedLinks.begin(), uncategorizedLinks.end(), boost::bind(&CompareProcess::categorizeSymlinkByTime, this, _1)); + std::for_each(uncategorizedLinks.begin(), uncategorizedLinks.end(), + [&](SymLinkMapping* linkMap) { this->categorizeSymlinkByTime(*linkMap); }); //categorize files that exist on both sides const CmpFileTime timeCmp(fileTimeTolerance); @@ -833,9 +574,9 @@ void CompareProcess::compareByTimeSize(const std::vector<FolderPairCfg>& directo } -zen::UInt64 getBytesToCompare(const std::vector<FileMapping*>& rowsToCompare) +UInt64 getBytesToCompare(const std::vector<FileMapping*>& rowsToCompare) { - zen::UInt64 dataTotal; + UInt64 dataTotal; for (std::vector<FileMapping*>::const_iterator j = rowsToCompare.begin(); j != rowsToCompare.end(); ++j) dataTotal += (*j)->getFileSize<LEFT_SIDE>(); //left and right filesizes should be the same @@ -844,27 +585,27 @@ zen::UInt64 getBytesToCompare(const std::vector<FileMapping*>& rowsToCompare) } -void CompareProcess::categorizeSymlinkByContent(SymLinkMapping* linkObj) const +void CompareProcess::categorizeSymlinkByContent(SymLinkMapping& linkObj) const { //categorize symlinks that exist on both sides const CmpFileTime timeCmp(fileTimeTolerance); if ( #ifdef FFS_WIN //type of symbolic link is relevant for Windows only - linkObj->getLinkType<LEFT_SIDE>() == linkObj->getLinkType<RIGHT_SIDE>() && + linkObj.getLinkType<LEFT_SIDE>() == linkObj.getLinkType<RIGHT_SIDE>() && #endif - linkObj->getTargetPath<LEFT_SIDE>() == linkObj->getTargetPath<RIGHT_SIDE>()) + linkObj.getTargetPath<LEFT_SIDE>() == linkObj.getTargetPath<RIGHT_SIDE>()) { //symlinks have same "content" - if (linkObj->getShortName<LEFT_SIDE>() == linkObj->getShortName<RIGHT_SIDE>() && - timeCmp.getResult(linkObj->getLastWriteTime<LEFT_SIDE>(), - linkObj->getLastWriteTime<RIGHT_SIDE>()) == CmpFileTime::TIME_EQUAL) - linkObj->setCategory<SYMLINK_EQUAL>(); + if (linkObj.getShortName<LEFT_SIDE>() == linkObj.getShortName<RIGHT_SIDE>() && + timeCmp.getResult(linkObj.getLastWriteTime<LEFT_SIDE>(), + linkObj.getLastWriteTime<RIGHT_SIDE>()) == CmpFileTime::TIME_EQUAL) + linkObj.setCategory<SYMLINK_EQUAL>(); else - linkObj->setCategory<SYMLINK_DIFFERENT_METADATA>(); + linkObj.setCategory<SYMLINK_DIFFERENT_METADATA>(); } else - linkObj->setCategory<SYMLINK_DIFFERENT>(); + linkObj.setCategory<SYMLINK_DIFFERENT>(); } @@ -889,7 +630,8 @@ void CompareProcess::compareByContent(const std::vector<FolderPairCfg>& director performComparison(*pair, output.back(), compareCandidates, uncategorizedLinks); //finish symlink categorization - std::for_each(uncategorizedLinks.begin(), uncategorizedLinks.end(), boost::bind(&CompareProcess::categorizeSymlinkByContent, this, _1)); + std::for_each(uncategorizedLinks.begin(), uncategorizedLinks.end(), + [&](SymLinkMapping* linkMap) { this->categorizeSymlinkByContent(*linkMap); }); } //finish categorization... @@ -910,10 +652,10 @@ void CompareProcess::compareByContent(const std::vector<FolderPairCfg>& director const size_t objectsTotal = filesToCompareBytewise.size() * 2; - const zen::UInt64 bytesTotal = getBytesToCompare(filesToCompareBytewise); + const UInt64 bytesTotal = getBytesToCompare(filesToCompareBytewise); procCallback.initNewProcess(static_cast<int>(objectsTotal), - to<zen::Int64>(bytesTotal), + to<Int64>(bytesTotal), ProcessCallback::PROCESS_COMPARING_CONTENT); const CmpFileTime timeCmp(fileTimeTolerance); @@ -1147,16 +889,16 @@ void CompareProcess::performComparison(const FolderPairCfg& fpCfg, //PERF_START; - //scan directories - const DirBufferValue& bufValueLeft = directoryBuffer->getDirectoryDescription( - output.getBaseDir<LEFT_SIDE>(), - fpCfg.filter.nameFilter, - fpCfg.handleSymlinks); + auto getDirValue = [&](const Zstring& dirnameFmt) -> const DirectoryValue& + { + auto iter = directoryBuffer.find(DirectoryKey(dirnameFmt, fpCfg.filter.nameFilter, fpCfg.handleSymlinks)); + if (iter == directoryBuffer.end()) + throw std::logic_error("Programming Error: directory buffer entry not found!"); + return iter->second; + }; - const DirBufferValue& bufValueRight = directoryBuffer->getDirectoryDescription( - output.getBaseDir<RIGHT_SIDE>(), - fpCfg.filter.nameFilter, - fpCfg.handleSymlinks); + const DirectoryValue& bufValueLeft = getDirValue(fpCfg.leftDirectoryFmt); + const DirectoryValue& bufValueRight = getDirValue(fpCfg.rightDirectoryFmt); procCallback.reportInfo(_("Generating file list...")); procCallback.forceUiRefresh(); //keep total number of scanned files up to date @@ -1181,10 +923,10 @@ void CompareProcess::performComparison(const FolderPairCfg& fpCfg, if (!failedReads.empty()) { Zstring filterFailedRead; - //exclude subfolders only + //exclude subfolders only std::for_each(failedReads.begin(), failedReads.end(), - [&](const Zstring& relDirPf) { filterFailedRead += relDirPf + Zstr("?*\n"); }); - //note: relDirPf is empty for base dir, otherwise postfixed! e.g. "subdir\" + [&](const Zstring& relDirPf) { filterFailedRead += relDirPf + Zstr("?*\n"); }); + //note: relDirPf is empty for base dir, otherwise postfixed! e.g. "subdir\" addHardFiltering(output, filterFailedRead); } diff --git a/comparison.h b/comparison.h index 444b3c3c..2d1eab75 100644 --- a/comparison.h +++ b/comparison.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef COMPARISON_H_INCLUDED #define COMPARISON_H_INCLUDED @@ -13,6 +13,7 @@ #include "structures.h" #include "shared/disable_standby.h" #include "library/norm_filter.h" +#include "library/parallel_scan.h" namespace zen @@ -51,24 +52,22 @@ public: void startCompareProcess(const std::vector<FolderPairCfg>& directoryPairs, const CompareVariant cmpVar, FolderComparison& output); -~CompareProcess(); + ~CompareProcess(); private: void compareByTimeSize(const std::vector<FolderPairCfg>& directoryPairsFormatted, FolderComparison& output); void compareByContent( const std::vector<FolderPairCfg>& directoryPairsFormatted, FolderComparison& output); //create comparison result table and fill category except for files existing on both sides: undefinedFiles and undefinedLinks are appended! - void categorizeSymlinkByTime(SymLinkMapping* linkObj) const; - void categorizeSymlinkByContent(SymLinkMapping* linkObj) const; + void categorizeSymlinkByTime(SymLinkMapping& linkObj) const; + void categorizeSymlinkByContent(SymLinkMapping& linkObj) const; void performComparison(const FolderPairCfg& fpCfg, BaseDirMapping& output, std::vector<FileMapping*>& undefinedFiles, std::vector<SymLinkMapping*>& undefinedLinks); - //buffer accesses to the same directories; useful when multiple folder pairs are used - class DirectoryBuffer; - std::unique_ptr<DirectoryBuffer> directoryBuffer; + std::map<DirectoryKey, DirectoryValue> directoryBuffer; const size_t fileTimeTolerance; //max allowed file time deviation diff --git a/file_hierarchy.cpp b/file_hierarchy.cpp index 9e7d4d4e..c1aa97ae 100644 --- a/file_hierarchy.cpp +++ b/file_hierarchy.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "file_hierarchy.h" #include "shared/build_info.h" diff --git a/file_hierarchy.h b/file_hierarchy.h index 1b4f244b..673acebc 100644 --- a/file_hierarchy.h +++ b/file_hierarchy.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FILEHIERARCHY_H_INCLUDED #define FILEHIERARCHY_H_INCLUDED @@ -51,9 +51,6 @@ struct FileDescriptor zen::Int64 lastWriteTimeRaw; //number of seconds since Jan. 1st 1970 UTC, same semantics like time_t (== signed long) zen::UInt64 fileSize; - - //#warning: what about memory consumption?? (assume large comparisons!!?) - //util::FileID fileIdentifier; //unique file identifier, optional: may be NULL! }; @@ -94,9 +91,9 @@ class FileSystemObject; //------------------------------------------------------------------ /* ERD: -DirContainer 1 -----> 0..n DirContainer -DirContainer 1 -----> 0..n FileDescriptor -DirContainer 1 -----> 0..n LinkDescriptor + DirContainer 1 -----> 0..n DirContainer + DirContainer 1 -----> 0..n FileDescriptor + DirContainer 1 -----> 0..n LinkDescriptor */ struct DirContainer @@ -145,7 +142,7 @@ SymLinkMapping FileMapping DirMapping BaseDirMapping class HierarchyObject { public: - typedef size_t ObjectID; + typedef long ObjectID; FileSystemObject* retrieveById(ObjectID id); //returns NULL if object is not found; logarithmic complexity const FileSystemObject* retrieveById(ObjectID id) const; // @@ -282,9 +279,9 @@ class FileSystemObject public: virtual void accept(FSObjectVisitor& visitor) const = 0; - const Zstring getParentRelativeName() const; //get name relative to base sync dir without FILE_NAME_SEPARATOR postfix - const Zstring getObjRelativeName() const; //same as getRelativeName() but also returns value if either side is empty - const Zstring& getObjShortName() const; //same as getShortName() but also returns value if either side is empty + const Zstring getParentRelativeName() const; //get name relative to base sync dir without FILE_NAME_SEPARATOR postfix + const Zstring& getObjShortName () const; //same as getShortName() but also returns value if either side is empty + const Zstring getObjRelativeName () const; //same as getRelativeName() but also returns value if either side is empty template <SelectedSide side> bool isEmpty() const; template <SelectedSide side> const Zstring& getShortName() const; template <SelectedSide side> const Zstring getRelativeName() const; //get name relative to base sync dir without FILE_NAME_SEPARATOR prefix @@ -572,6 +569,7 @@ HierarchyObject::ObjectID FileSystemObject::getId() const inline HierarchyObject::ObjectID FileSystemObject::getUniqueId() { + //warning: potential MT issue in the future! static HierarchyObject::ObjectID id = 0; return ++id; } @@ -665,16 +663,14 @@ SyncOperation FileSystemObject::testSyncOperation(bool selected, SyncDirection p } -template <> -inline +template <> inline bool FileSystemObject::isEmpty<LEFT_SIDE>() const { return shortNameLeft_.empty(); } -template <> -inline +template <> inline bool FileSystemObject::isEmpty<RIGHT_SIDE>() const { return shortNameRight_.empty(); @@ -688,16 +684,14 @@ bool FileSystemObject::isEmpty() const } -template <> -inline +template <> inline const Zstring& FileSystemObject::getShortName<LEFT_SIDE>() const { return shortNameLeft_; //empty if not existing } -template <> -inline +template <> inline const Zstring& FileSystemObject::getShortName<RIGHT_SIDE>() const { return shortNameRight_; //empty if not existing diff --git a/library/binary.cpp b/library/binary.cpp index 3a202711..1fd8c55f 100644 --- a/library/binary.cpp +++ b/library/binary.cpp @@ -3,12 +3,13 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "binary.h" #include "../shared/file_io.h" #include <vector> #include <wx/stopwatch.h> #include "../shared/int64.h" +#include <boost/thread/tss.hpp> inline void setMinSize(std::vector<char>& buffer, size_t minSize) @@ -71,11 +72,15 @@ bool zen::filesHaveSameContent(const Zstring& filename1, const Zstring& filename FileInput file1(filename1); //throw (FileError) FileInput file2(filename2); //throw (FileError) - BufferSize bufferSize; + static boost::thread_specific_ptr<std::vector<char>> cpyBuf1; + static boost::thread_specific_ptr<std::vector<char>> cpyBuf2; + if (!cpyBuf1.get()) cpyBuf1.reset(new std::vector<char>()); + if (!cpyBuf2.get()) cpyBuf2.reset(new std::vector<char>()); - static std::vector<char> memory1; - static std::vector<char> memory2; + std::vector<char>& memory1 = *cpyBuf1; + std::vector<char>& memory2 = *cpyBuf2; + BufferSize bufferSize; zen::UInt64 bytesCompared; wxLongLong lastDelayViolation = wxGetLocalTimeMillis(); diff --git a/library/binary.h b/library/binary.h index b796ddbb..4dbbcd45 100644 --- a/library/binary.h +++ b/library/binary.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef BINARY_H_INCLUDED #define BINARY_H_INCLUDED diff --git a/library/custom_grid.cpp b/library/custom_grid.cpp index 97b608fb..ccabbea9 100644 --- a/library/custom_grid.cpp +++ b/library/custom_grid.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "custom_grid.h" #include "resources.h" #include <wx/dc.h> diff --git a/library/custom_grid.h b/library/custom_grid.h index 8b70c417..6b577011 100644 --- a/library/custom_grid.h +++ b/library/custom_grid.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef CUSTOMGRID_H_INCLUDED #define CUSTOMGRID_H_INCLUDED diff --git a/library/db_file.cpp b/library/db_file.cpp index ec1c4464..9429dd1e 100644 --- a/library/db_file.cpp +++ b/library/db_file.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "db_file.h" #include <wx/wfstream.h> #include <wx/zstream.h> @@ -305,7 +305,7 @@ std::pair<DirInfoPtr, DirInfoPtr> zen::loadFromDisk(const BaseDirMapping& baseMa !streamLeft ->second.get() || !streamRight->second.get()) throw FileErrorDatabaseNotExisting(_("Initial synchronization:") + " \n\n" + - _("No matching synchronization session found in database files:") + " \n" + + _("Database files do not share a common synchronization session:") + " \n" + "\"" + fileNameLeft + "\"\n" + "\"" + fileNameRight + "\""); //read streams into DirInfo @@ -342,29 +342,27 @@ private: writeNumberC<bool>(false); //mark last entry } - void processFile(const FileMapping& fileMap, const DirContainer* oldDirInfo) + void processFile(const FileMapping& fileMap, const DirContainer* oldParentDir) { - const Zstring shortName = fileMap.getObjShortName(); - if (fileMap.getCategory() == FILE_EQUAL) //data in sync: write current state { if (!fileMap.isEmpty<side>()) { writeNumberC<bool>(true); //mark beginning of entry - writeStringC(shortName); + writeStringC(fileMap.getShortName<side>()); //save respecting case! (Windows) writeNumberC<boost::int64_t >(to<boost::int64_t>(fileMap.getLastWriteTime<side>())); //last modification time writeNumberC<boost::uint64_t>(to<boost::uint64_t>(fileMap.getFileSize<side>())); //filesize } } else //not in sync: reuse last synchronous state { - if (oldDirInfo) //no data is also a "synchronous state"! + if (oldParentDir) //no data is also a "synchronous state"! { - DirContainer::FileList::const_iterator iter = oldDirInfo->files.find(shortName); - if (iter != oldDirInfo->files.end()) + auto iter = oldParentDir->files.find(fileMap.getObjShortName()); + if (iter != oldParentDir->files.end()) { writeNumberC<bool>(true); //mark beginning of entry - writeStringC(shortName); + writeStringC(iter->first); //save respecting case! (Windows) writeNumberC<boost::int64_t >(to<boost::int64_t>(iter->second.lastWriteTimeRaw)); //last modification time writeNumberC<boost::uint64_t>(to<boost::uint64_t>(iter->second.fileSize)); //filesize } @@ -372,16 +370,14 @@ private: } } - void processLink(const SymLinkMapping& linkObj, const DirContainer* oldDirInfo) + void processLink(const SymLinkMapping& linkObj, const DirContainer* oldParentDir) { - const Zstring shortName = linkObj.getObjShortName(); - - if (linkObj.getCategory() == FILE_EQUAL) //data in sync: write current state + if (linkObj.getLinkCategory() == SYMLINK_EQUAL) //data in sync: write current state { if (!linkObj.isEmpty<side>()) { writeNumberC<bool>(true); //mark beginning of entry - writeStringC(shortName); + writeStringC(linkObj.getShortName<side>()); //save respecting case! (Windows) writeNumberC<boost::int64_t>(to<boost::int64_t>(linkObj.getLastWriteTime<side>())); //last modification time writeStringC(linkObj.getTargetPath<side>()); writeNumberC<boost::int32_t>(linkObj.getLinkType<side>()); @@ -389,13 +385,13 @@ private: } else //not in sync: reuse last synchronous state { - if (oldDirInfo) //no data is also a "synchronous state"! + if (oldParentDir) //no data is also a "synchronous state"! { - DirContainer::LinkList::const_iterator iter = oldDirInfo->links.find(shortName); - if (iter != oldDirInfo->links.end()) + auto iter = oldParentDir->links.find(linkObj.getObjShortName()); + if (iter != oldParentDir->links.end()) { writeNumberC<bool>(true); //mark beginning of entry - writeStringC(shortName); + writeStringC(iter->first); //save respecting case! (Windows) writeNumberC<boost::int64_t>(to<boost::int64_t>(iter->second.lastWriteTimeRaw)); //last modification time writeStringC(iter->second.targetPath); writeNumberC<boost::int32_t>(iter->second.type); @@ -404,35 +400,62 @@ private: } } - void processDir(const DirMapping& dirMap, const DirContainer* oldDirInfo) + void processDir(const DirMapping& dirMap, const DirContainer* oldParentDir) { - const Zstring shortName = dirMap.getObjShortName(); - - const DirContainer* subDirInfo = NULL; - if (oldDirInfo) //no data is also a "synchronous state"! + const DirContainer* oldDir = NULL; + const Zstring* oldDirName = NULL; + if (oldParentDir) //no data is also a "synchronous state"! { - DirContainer::DirList::const_iterator iter = oldDirInfo->dirs.find(shortName); - if (iter != oldDirInfo->dirs.end()) - subDirInfo = &iter->second; + auto iter = oldParentDir->dirs.find(dirMap.getObjShortName()); + if (iter != oldParentDir->dirs.end()) + { + oldDirName = &iter->first; + oldDir = &iter->second; + } } - if (dirMap.getCategory() == FILE_EQUAL) //data in sync: write current state + CompareDirResult cat = dirMap.getDirCategory(); + + if (cat == DIR_EQUAL) //data in sync: write current state { if (!dirMap.isEmpty<side>()) { writeNumberC<bool>(true); //mark beginning of entry - writeStringC(shortName); - execute(dirMap, subDirInfo); //recurse + writeStringC(dirMap.getShortName<side>()); //save respecting case! (Windows) + execute(dirMap, oldDir); //recurse } } else //not in sync: reuse last synchronous state { - if (subDirInfo) //no data is also a "synchronous state"! + if (oldDir) { - writeNumberC<bool>(true); //mark beginning of entry - writeStringC(shortName); + writeNumberC<bool>(true); //mark beginning of entry + writeStringC(*oldDirName); //save respecting case! (Windows) + execute(dirMap, oldDir); //recurse + return; + } + //no data is also a "synchronous state"! + + //else: not in sync AND no "last synchronous state" + //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 + switch (cat) + { + case DIR_LEFT_SIDE_ONLY: //sub-items cannot be in sync + break; + case DIR_RIGHT_SIDE_ONLY: //sub-items cannot be in sync + break; + case DIR_EQUAL: + assert(false); + break; + case DIR_DIFFERENT_METADATA: + writeNumberC<bool>(true); + writeStringC(dirMap.getShortName<side>()); + //ATTENTION: strictly this is a violation of the principle of reporting last synchronous state! + //however in this case this will result in "last sync unsuccessful" for this directory within <automatic> algorithm, which is fine + execute(dirMap, oldDir); //recurse and save sub-items which are in sync + break; } - execute(dirMap, subDirInfo); //recurse } } }; diff --git a/library/db_file.h b/library/db_file.h index a68e6bcf..dc9eb141 100644 --- a/library/db_file.h +++ b/library/db_file.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef DBFILE_H_INCLUDED #define DBFILE_H_INCLUDED diff --git a/library/detect_renaming.cpp b/library/detect_renaming.cpp index 0e19451c..39e7eb4b 100644 --- a/library/detect_renaming.cpp +++ b/library/detect_renaming.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "detect_renaming.h" #include <map> #include <vector> diff --git a/library/detect_renaming.h b/library/detect_renaming.h index 9f360f8e..e94927c0 100644 --- a/library/detect_renaming.h +++ b/library/detect_renaming.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef DETECTRENAMING_H_INCLUDED #define DETECTRENAMING_H_INCLUDED diff --git a/library/dir_exist_async.h b/library/dir_exist_async.h new file mode 100644 index 00000000..661c70d6 --- /dev/null +++ b/library/dir_exist_async.h @@ -0,0 +1,35 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#ifndef DIR_EXIST_HEADER_08173281673432158067342132467183267 +#define DIR_EXIST_HEADER_08173281673432158067342132467183267 + +#include "../shared/check_exist.h" +#include "status_handler.h" +#include "../shared/file_error.h" +#include "../shared/i18n.h" + +//dir existence checking may hang for non-existent network drives => run asynchronously and update UI! +namespace +{ +using namespace zen; //restricted to unnamed namespace! + +bool dirExistsUpdating(const Zstring& dirname, ProcessCallback& procCallback) +{ + using namespace util; + + std::wstring statusText = _("Searching for directory %x..."); + replace(statusText, L"%x", std::wstring(L"\"") + dirname + L"\"", false); + procCallback.reportInfo(statusText); + + auto ft = dirExistsAsync(dirname); + while (!ft.timed_wait(boost::posix_time::milliseconds(UI_UPDATE_INTERVAL))) + procCallback.requestUiRefresh(); //may throw! + return ft.get(); +} +} + +#endif //DIR_EXIST_HEADER_08173281673432158067342132467183267 diff --git a/library/dir_lock.cpp b/library/dir_lock.cpp index 1775026b..6123449b 100644 --- a/library/dir_lock.cpp +++ b/library/dir_lock.cpp @@ -42,15 +42,13 @@ const size_t DETECT_EXITUS_INTERVAL = 30000; //assume abandoned lock; unit [ms] const char LOCK_FORMAT_DESCR[] = "FreeFileSync"; const int LOCK_FORMAT_VER = 1; //lock file format version - -typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class } //worker thread class LifeSigns { public: - LifeSigns(const BasicString& lockfilename) : //throw()!!! siehe SharedDirLock() + LifeSigns(const Zstring& lockfilename) : //throw()!!! siehe SharedDirLock() lockfilename_(lockfilename) {} //thread safety: make deep copy! void operator()() const //thread entry @@ -120,8 +118,7 @@ public: } private: - //make sure this instance is safely copyable! - const BasicString lockfilename_; //thread local! Not ref-counted! + const Zstring lockfilename_; //thread local! atomic ref-count => binary value-type semantics! }; @@ -494,7 +491,7 @@ public: while (!::tryLock(lockfilename)) //throw (FileError) ::waitOnDirLock(lockfilename, callback); // - threadObj = boost::thread(LifeSigns(lockfilename.c_str())); + threadObj = std::move(boost::thread(LifeSigns(lockfilename))); } ~SharedDirLock() diff --git a/library/error_log.cpp b/library/error_log.cpp index eef8572a..28819f40 100644 --- a/library/error_log.cpp +++ b/library/error_log.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "error_log.h" #include <wx/datetime.h> #include "../shared/i18n.h" diff --git a/library/error_log.h b/library/error_log.h index 323a4297..f8a0c909 100644 --- a/library/error_log.h +++ b/library/error_log.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef ERRORLOGGING_H_INCLUDED #define ERRORLOGGING_H_INCLUDED diff --git a/library/hard_filter.cpp b/library/hard_filter.cpp index e96d32fa..c6d18f47 100644 --- a/library/hard_filter.cpp +++ b/library/hard_filter.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "hard_filter.h" #include "../shared/zstring.h" #include <wx/string.h> diff --git a/library/hard_filter.h b/library/hard_filter.h index 0711175a..bb0b6d54 100644 --- a/library/hard_filter.h +++ b/library/hard_filter.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FFS_FILTER_H_INCLUDED #define FFS_FILTER_H_INCLUDED diff --git a/library/icon_buffer.cpp b/library/icon_buffer.cpp index 91487498..5d4dd19b 100644 --- a/library/icon_buffer.cpp +++ b/library/icon_buffer.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "icon_buffer.h" #include <wx/msgdlg.h> #include <map> @@ -13,6 +13,8 @@ #include <wx/log.h> #include "../shared/i18n.h" #include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> +#include "../shared/loki/ScopeGuard.h" +#include <boost/thread/once.hpp> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -28,38 +30,39 @@ using namespace zen; const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to buffer -//--------------------------------------------------------------------------------------------------- -typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class -//avoid reference-counted objects for shared data: NOT THREADSAFE!!! (implicitly shared variable: ref-count) -//--------------------------------------------------------------------------------------------------- - #ifdef FFS_WIN -BasicString getFileExtension(const BasicString& filename) +Zstring getFileExtension(const Zstring& filename) { - const BasicString shortName = filename.AfterLast(Zchar('\\')); //warning: using windows file name separator! + const Zstring shortName = afterLast(filename, Zchar('\\')); //warning: using windows file name separator! - return shortName.find(Zchar('.')) != BasicString::npos ? + return shortName.find(Zchar('.')) != Zstring::npos ? filename.AfterLast(Zchar('.')) : - BasicString(); + Zstring(); } +namespace +{ +std::set<Zstring, LessFilename> exceptions; //thread-safe! +boost::once_flag once = BOOST_ONCE_INIT; // +} + //test for extension for icons that physically have to be retrieved from disc -bool isPriceyExtension(const BasicString& extension) +bool isPriceyExtension(const Zstring& extension) { - static std::set<BasicString, LessFilename> exceptions; //not thread-safe, but called from worker thread only! - if (exceptions.empty()) + boost::call_once(once, []() { - exceptions.insert(Zstr("exe")); - exceptions.insert(Zstr("lnk")); - exceptions.insert(Zstr("ico")); - exceptions.insert(Zstr("ani")); - exceptions.insert(Zstr("cur")); - exceptions.insert(Zstr("url")); - exceptions.insert(Zstr("msc")); - exceptions.insert(Zstr("scr")); - } + exceptions.insert(L"exe"); + exceptions.insert(L"lnk"); + exceptions.insert(L"ico"); + exceptions.insert(L"ani"); + exceptions.insert(L"cur"); + exceptions.insert(L"url"); + exceptions.insert(L"msc"); + exceptions.insert(L"scr"); + }); + return exceptions.find(extension) != exceptions.end(); } #endif @@ -82,7 +85,7 @@ public: #ifdef FFS_WIN ::CopyIcon(other.handle_) #elif defined FFS_LINUX - gdk_pixbuf_copy(other.handle_) //create new Pix buf with reference count 1 or return 0 on error + ::gdk_pixbuf_copy(other.handle_) //create new Pix buf with reference count 1 or return 0 on error #endif ) {} @@ -98,14 +101,11 @@ public: #ifdef FFS_WIN ::DestroyIcon(handle_); #elif defined FFS_LINUX - g_object_unref(handle_); + ::g_object_unref(handle_); #endif } - void swap(IconHolder& other) //throw() - { - std::swap(handle_, other.handle_); - } + void swap(IconHolder& other) { std::swap(handle_, other.handle_); } //throw() wxIcon toWxIcon() const //copy HandleType, caller needs to take ownership! { @@ -130,7 +130,7 @@ private: }; -IconHolder getAssociatedIcon(const BasicString& filename) +IconHolder getAssociatedIcon(const Zstring& filename) { #ifdef FFS_WIN //despite what docu says about SHGetFileInfo() it can't handle all relative filenames, e.g. "\DirName" @@ -195,7 +195,7 @@ IconHolder getAssociatedIcon(const BasicString& filename) } #ifdef FFS_WIN -IconHolder getAssociatedIconByExt(const BasicString& extension) +IconHolder getAssociatedIconByExt(const Zstring& extension) { SHFILEINFO fileInfo = {}; //initialize hIcon -> fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! @@ -211,80 +211,69 @@ IconHolder getAssociatedIconByExt(const BasicString& extension) #endif -const wxIcon& getDirectoryIcon() +wxIcon getDirectoryIcon() { - static wxIcon folderIcon; +#ifdef FFS_WIN + wxIcon folderIcon; - static bool isInitalized = false; //not thread-safe, but called from GUI thread only! - if (!isInitalized) - { - isInitalized = true; + SHFILEINFO fileInfo = {}; //initialize hIcon -#ifdef FFS_WIN - SHFILEINFO fileInfo = {}; //initialize hIcon + //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! + if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string + FILE_ATTRIBUTE_DIRECTORY, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_DIRECTORY, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && + fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + { + folderIcon.SetHICON(fileInfo.hIcon); //transfer ownership! + folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); + } - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - folderIcon.SetHICON(fileInfo.hIcon); //transfer ownership! - folderIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } + return folderIcon; #elif defined FFS_LINUX - folderIcon = ::getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/" + return ::getAssociatedIcon(Zstr("/usr/")).toWxIcon(); //all directories will look like "/usr/" #endif - } - return folderIcon; } -const wxIcon& getFileIcon() +wxIcon getFileIcon() { - static wxIcon fileIcon; - - static bool isInitalized = false; //not thread-safe, but called from GUI thread only! - if (!isInitalized) - { - isInitalized = true; + wxIcon fileIcon; #ifdef FFS_WIN - SHFILEINFO fileInfo = {}; //initialize hIcon + SHFILEINFO fileInfo = {}; //initialize hIcon - //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! - if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string - FILE_ATTRIBUTE_NORMAL, - &fileInfo, - sizeof(fileInfo), - SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && + //NOTE: CoInitializeEx()/CoUninitialize() needs to be called for THIS thread! + if (::SHGetFileInfo(Zstr("dummy"), //Windows Seven doesn't like this parameter to be an empty string + FILE_ATTRIBUTE_NORMAL, + &fileInfo, + sizeof(fileInfo), + SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES) && - fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! - { - fileIcon.SetHICON(fileInfo.hIcon); //transfer ownership! - fileIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); - } + fileInfo.hIcon != 0) //fix for weird error: SHGetFileInfo() might return successfully WITHOUT filling fileInfo.hIcon!! + { + fileIcon.SetHICON(fileInfo.hIcon); //transfer ownership! + fileIcon.SetSize(IconBuffer::ICON_SIZE, IconBuffer::ICON_SIZE); + } #elif defined FFS_LINUX - try + try + { + Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); + if (iconTheme) { - Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default(); - if (iconTheme) - { - Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (!iconPixbuf) - iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); - if (iconPixbuf) - fileIcon.SetPixbuf(iconPixbuf->gobj_copy()); // transfer ownership!! - } + Glib::RefPtr<Gdk::Pixbuf> iconPixbuf = iconTheme->load_icon("misc", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (!iconPixbuf) + iconPixbuf = iconTheme->load_icon("text-x-generic", IconBuffer::ICON_SIZE, Gtk::ICON_LOOKUP_USE_BUILTIN); + if (iconPixbuf) + fileIcon.SetPixbuf(iconPixbuf->gobj_copy()); // transfer ownership!! } - catch (const Glib::Error&) {} -#endif } + catch (const Glib::Error&) {} +#endif return fileIcon; } @@ -293,37 +282,66 @@ const wxIcon& getFileIcon() //---------------------- Shared Data ------------------------- struct WorkLoad { - std::vector<BasicString> filesToLoad; //processes last elements of vector first! - boost::mutex mutex; - boost::condition_variable condition; //signal event: data for processing available +public: + Zstring extractNextFile() //context of worker thread, blocking + { + boost::unique_lock<boost::mutex> dummy(lockFiles); + + while (filesToLoad.empty()) + conditionNewFiles.timed_wait(dummy, boost::get_system_time() + boost::posix_time::milliseconds(50)); //interruption point! + + Zstring fileName = filesToLoad.back(); + filesToLoad.pop_back(); + return fileName; + } + + void setWorkload(const std::vector<Zstring>& newLoad) //context of main thread + { + boost::unique_lock<boost::mutex> dummy(lockFiles); + filesToLoad = newLoad; + + conditionNewFiles.notify_one(); + //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref + } + +private: + std::vector<Zstring> filesToLoad; //processes last elements of vector first! + boost::mutex lockFiles; + boost::condition_variable conditionNewFiles; //signal event: data for processing available }; -typedef std::map<BasicString, IconHolder, LessFilename> NameIconMap; //entryName/icon -> ATTENTION: avoid ref-counting for this shared data structure! -typedef std::queue<BasicString> IconDbSequence; //entryName -struct Buffer +typedef std::map<Zstring, IconHolder, LessFilename> NameIconMap; //entryName/icon -> note: Zstring is thread-safe +typedef std::queue<Zstring> IconDbSequence; //entryName + +class Buffer { - boost::mutex lockAccess; - NameIconMap iconMappping; //use synchronisation when accessing this! +public: + bool requestFileIcon(const Zstring& fileName, wxIcon* icon = NULL); + void insertIntoBuffer(const Zstring& entryName, const IconHolder& icon); //called by worker thread + +private: + boost::mutex lockBuffer; + NameIconMap iconMappping; //use synchronisation when accessing this! IconDbSequence iconSequence; //save sequence of buffer entry to delete oldest elements }; //------------------------------------------------------------ -bool requestFileIcon(Buffer& buf, const Zstring& fileName, wxIcon* icon = NULL) +bool Buffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) //context of main AND worker thread { - boost::lock_guard<boost::mutex> dummy(buf.lockAccess); + boost::lock_guard<boost::mutex> dummy(lockBuffer); #ifdef FFS_WIN //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - const BasicString extension = getFileExtension(BasicString(fileName)); - const BasicString searchString = isPriceyExtension(extension) ? BasicString(fileName) : extension; - auto iter = buf.iconMappping.find(searchString); + const Zstring extension = getFileExtension(fileName); + const Zstring searchString = isPriceyExtension(extension) ? fileName : extension; + auto iter = iconMappping.find(searchString); #elif defined FFS_LINUX - auto iter = buf.iconMappping.find(BasicString(fileName)); + auto iter = iconMappping.find(fileName); #endif - if (iter == buf.iconMappping.end()) + if (iter == iconMappping.end()) return false; if (icon != NULL) @@ -332,23 +350,23 @@ bool requestFileIcon(Buffer& buf, const Zstring& fileName, wxIcon* icon = NULL) } -void insertIntoBuffer(Buffer& buf, const BasicString& entryName, const IconHolder& icon) //called by worker thread +void Buffer::insertIntoBuffer(const Zstring& entryName, const IconHolder& icon) //called by worker thread { - boost::lock_guard<boost::mutex> dummy(buf.lockAccess); + boost::lock_guard<boost::mutex> dummy(lockBuffer); //thread saftey: icon uses ref-counting! But is NOT shared with main thread! - auto rc = buf.iconMappping.insert(std::make_pair(entryName, icon)); + auto rc = iconMappping.insert(std::make_pair(entryName, icon)); if (rc.second) //if insertion took place - buf.iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! + iconSequence.push(entryName); //note: sharing Zstring with IconDB!!! - assert(buf.iconMappping.size() == buf.iconSequence.size()); + assert(iconMappping.size() == iconSequence.size()); //remove elements if buffer becomes too big: - if (buf.iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) + if (iconMappping.size() > BUFFER_SIZE_MAX) //limit buffer size: critical because GDI resources are limited (e.g. 10000 on XP per process) { //remove oldest element - buf.iconMappping.erase(buf.iconSequence.front()); - buf.iconSequence.pop(); + iconMappping.erase(iconSequence.front()); + iconSequence.pop(); } } //################################################################################################################################################ @@ -364,10 +382,8 @@ public: void operator()(); //thread entry private: - void doWork(); - - std::shared_ptr<WorkLoad> workload_; - std::shared_ptr<Buffer> buffer_; + std::shared_ptr<WorkLoad> workload_; //main/worker thread may access different shared_ptr instances safely (even though they have the same target!) + std::shared_ptr<Buffer> buffer_; //http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety }; @@ -375,79 +391,34 @@ void WorkerThread::operator()() //thread entry { //failure to initialize COM for each thread is a source of hard to reproduce bugs: https://sourceforge.net/tracker/?func=detail&aid=3160472&group_id=234430&atid=1093080 #ifdef FFS_WIN - struct ThreadInitializer - { - ThreadInitializer () { ::CoInitializeEx(NULL, COINIT_MULTITHREADED); } - ~ThreadInitializer() { ::CoUninitialize(); } - } dummy1; + ::CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE); + Loki::ScopeGuard dummy = Loki::MakeGuard([]() { ::CoUninitialize(); }); + (void)dummy; #endif - try - { - while (true) - { - { - boost::unique_lock<boost::mutex> dummy(workload_->mutex); - while(workload_->filesToLoad.empty()) - workload_->condition.wait(dummy); //interruption point! - //shared.condition.timed_wait(dummy, boost::get_system_time() + boost::posix_time::milliseconds(100)); - } - - doWork(); //no need to lock the complete method! - } - } - catch (boost::thread_interrupted&) - { - throw; //this is the only reasonable exception! - } - catch (const std::exception& e) //exceptions must be catched per thread - { - wxSafeShowMessage(wxString(_("An exception occurred!")) + wxT("(Icon buffer)"), wxString::FromAscii(e.what())); //simple wxMessageBox won't do for threads - } - catch (...) //exceptions must be catched per thread - { - wxSafeShowMessage(wxString(_("An exception occurred!")) + wxT("(Icon buffer2)"), wxT("Unknown exception in icon thread!")); //simple wxMessageBox won't do for threads - } -} - - -void WorkerThread::doWork() -{ - //do work: get the file icon. while (true) { - BasicString fileName; - { - boost::lock_guard<boost::mutex> dummy(workload_->mutex); - if (workload_->filesToLoad.empty()) - break; //enter waiting state - fileName = workload_->filesToLoad.back(); //deep copy - workload_->filesToLoad.pop_back(); - } + boost::this_thread::interruption_point(); - if (requestFileIcon(*buffer_, Zstring(fileName))) //thread safety: Zstring okay, won't be reference-counted in requestIcon() + const Zstring fileName = workload_->extractNextFile(); //start work: get next icon to load + + if (buffer_->requestFileIcon(fileName)) continue; //icon already in buffer: skip #ifdef FFS_WIN - const BasicString extension = getFileExtension(fileName); //thread-safe: no sharing! + const Zstring extension = getFileExtension(fileName); if (isPriceyExtension(extension)) //"pricey" extensions are stored with fullnames and are read from disk, while cheap ones require just the extension - { - const IconHolder newIcon = getAssociatedIcon(fileName); - insertIntoBuffer(*buffer_, fileName, newIcon); - } + buffer_->insertIntoBuffer(fileName, getAssociatedIcon(fileName)); else //no read-access to disk! determine icon by extension - { - const IconHolder newIcon = getAssociatedIconByExt(extension); - insertIntoBuffer(*buffer_, extension, newIcon); - } + buffer_->insertIntoBuffer(extension, getAssociatedIconByExt(extension)); + #elif defined FFS_LINUX const IconHolder newIcon = getAssociatedIcon(fileName); - insertIntoBuffer(*buffer_, fileName, newIcon); + buffer_->insertIntoBuffer(fileName, newIcon); #endif } } - //######################### redirect to impl ##################################################### struct IconBuffer::Pimpl @@ -456,7 +427,6 @@ struct IconBuffer::Pimpl workload(std::make_shared<WorkLoad>()), buffer(std::make_shared<Buffer>()) {} - std::shared_ptr<WorkLoad> workload; std::shared_ptr<Buffer> buffer; @@ -477,9 +447,6 @@ IconBuffer::~IconBuffer() pimpl->worker.join(); } -const wxIcon& IconBuffer::getDirectoryIcon() { return ::getDirectoryIcon(); } - -const wxIcon& IconBuffer::getFileIcon() { return ::getFileIcon(); } IconBuffer& IconBuffer::getInstance() { @@ -487,19 +454,18 @@ IconBuffer& IconBuffer::getInstance() return instance; } -bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return ::requestFileIcon(*pimpl->buffer, fileName, icon); } - -void IconBuffer::setWorkload(const std::vector<Zstring>& load) -{ - { - boost::lock_guard<boost::mutex> dummy(pimpl->workload->mutex); +bool IconBuffer::requestFileIcon(const Zstring& fileName, wxIcon* icon) { return pimpl->buffer->requestFileIcon(fileName, icon); } - pimpl->workload->filesToLoad.clear(); +void IconBuffer::setWorkload(const std::vector<Zstring>& load) { pimpl->workload->setWorkload(load); } - std::transform(load.begin(), load.end(), std::back_inserter(pimpl->workload->filesToLoad), - [](const Zstring& file) { return BasicString(file); }); //make DEEP COPY from Zstring - } +const wxIcon& IconBuffer::getDirectoryIcon() +{ + static wxIcon dirIcon = ::getDirectoryIcon(); + return dirIcon; +} - pimpl->workload->condition.notify_one(); - //condition handling, see: http://www.boost.org/doc/libs/1_43_0/doc/html/thread/synchronization.html#thread.synchronization.condvar_ref +const wxIcon& IconBuffer::getFileIcon() +{ + static wxIcon fileIcon = ::getFileIcon(); + return fileIcon; } diff --git a/library/icon_buffer.h b/library/icon_buffer.h index 87f0cd4a..00370d1b 100644 --- a/library/icon_buffer.h +++ b/library/icon_buffer.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef ICONBUFFER_H_INCLUDED #define ICONBUFFER_H_INCLUDED diff --git a/library/lock_holder.h b/library/lock_holder.h index dc88ce71..172209dc 100644 --- a/library/lock_holder.h +++ b/library/lock_holder.h @@ -5,6 +5,7 @@ #include "../shared/zstring.h" #include "dir_lock.h" #include "status_handler.h" +#include "dir_exist_async.h" namespace zen { @@ -16,7 +17,10 @@ class LockHolder public: void addDir(const Zstring& dirnameFmt, ProcessCallback& procCallback) //resolved dirname ending with path separator { - if (dirnameFmt.empty()) return; + if (dirnameFmt.empty() || + !dirExistsUpdating(dirnameFmt, procCallback)) + return; + if (lockHolder.find(dirnameFmt) != lockHolder.end()) return; assert(dirnameFmt.EndsWith(FILE_NAME_SEPARATOR)); //this is really the contract, formatting does other things as well, e.g. macro substitution @@ -37,7 +41,7 @@ public: catch (const FileError& e) { bool dummy = false; //this warning shall not be shown but logged only - procCallback.reportWarning(e.msg(), dummy); + procCallback.reportWarning(e.msg(), dummy); //may throw! } } diff --git a/library/norm_filter.h b/library/norm_filter.h index 2e55b43f..609c81db 100644 --- a/library/norm_filter.h +++ b/library/norm_filter.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef NORM_FILTER_H_INCLUDED #define NORM_FILTER_H_INCLUDED diff --git a/library/parallel_scan.cpp b/library/parallel_scan.cpp new file mode 100644 index 00000000..2f91763a --- /dev/null +++ b/library/parallel_scan.cpp @@ -0,0 +1,617 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#include "parallel_scan.h" +#include <boost/detail/atomic_count.hpp> +#include "db_file.h" +#include "lock_holder.h" +#include "../shared/i18n.h" +#include "../shared/file_traverser.h" +#include "../shared/file_error.h" +#include "../shared/string_conv.h" +#include "../shared/check_exist.h" +#include "../shared/boost_thread_wrap.h" //include <boost/thread.hpp> +#include "loki/ScopeGuard.h" +//#include "../shared/file_id.h" + +/* +#ifdef FFS_WIN +#include <wx/msw/wrapwin.h> //includes "windows.h" +#include "WinIoCtl.h" + +#elif defined FFS_LINUX +#endif +*/ +using namespace zen; + + +#ifndef BOOST_HAS_THREADS +#error just some paranoia check... +#endif + + +namespace +{ +/* +#ifdef FFS_WIN + +struct DiskInfo +{ + DiskInfo() : + driveType(DRIVE_UNKNOWN), + diskID(-1) {} + + UINT driveType; + int diskID; // -1 if id could not be determined, this one is filled if driveType == DRIVE_FIXED or DRIVE_REMOVABLE; +}; + +inline +bool operator<(const DiskInfo& lhs, const DiskInfo& rhs) +{ + if (lhs.driveType != rhs.driveType) + return lhs.driveType < rhs.driveType; + + if (lhs.diskID < 0 || rhs.diskID < 0) + return false; + //consider "same", reason: one volume may be uniquely associated with one disk, while the other volume is associated to the same disk AND another one! + //volume <-> disk is 0..N:1..N + + return lhs.diskID < rhs.diskID ; +} + + +DiskInfo retrieveDiskInfo(const Zstring& pathName) +{ + std::vector<wchar_t> volName(std::max(pathName.size(), static_cast<size_t>(10000))); + + DiskInfo output; + + //full pathName need not yet exist! + if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, + &volName[0], //__out LPTSTR lpszVolumePathName, + static_cast<DWORD>(volName.size()))) //__in DWORD cchBufferLength + return output; + + const Zstring rootPathName = &volName[0]; + + output.driveType = ::GetDriveType(rootPathName.c_str()); + + if (output.driveType == DRIVE_NO_ROOT_DIR) //these two should be the same error category + output.driveType = DRIVE_UNKNOWN; + + if (output.driveType != DRIVE_FIXED && output.driveType != DRIVE_REMOVABLE) + return output; //no reason to get disk ID + + //go and find disk id: + + //format into form: "\\.\C:" + Zstring volnameFmt = rootPathName; + if (endsWith(volnameFmt, FILE_NAME_SEPARATOR)) + volnameFmt.resize(volnameFmt.size() - 1); + volnameFmt = L"\\\\.\\" + volnameFmt; + + HANDLE hVolume = ::CreateFile(volnameFmt.c_str(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + if (hVolume == INVALID_HANDLE_VALUE) + return output; + + Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hVolume); + (void)dummy; //silence warning "unused variable" + + std::vector<char> buffer(sizeof(VOLUME_DISK_EXTENTS) + sizeof(DISK_EXTENT)); //reserve buffer for at most one disk! call below will then fail if volume spans multiple disks! + + DWORD bytesReturned = 0; + if (!::DeviceIoControl(hVolume, // handle to device + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, // dwIoControlCode + NULL, // lpInBuffer + 0, // nInBufferSize + &buffer[0], // output buffer + static_cast<DWORD>(buffer.size()), // size of output buffer + &bytesReturned, // number of bytes returned + NULL)) // OVERLAPPED structure + return output; + + const VOLUME_DISK_EXTENTS& volDisks = *reinterpret_cast<VOLUME_DISK_EXTENTS*>(&buffer[0]); + + if (volDisks.NumberOfDiskExtents != 1) + return output; + + output.diskID = volDisks.Extents[0].DiskNumber; + + return output; +} + +#elif defined FFS_LINUX +#endif +*/ + +/* +PERF NOTE + +-------------------------------------------- +|Testcase: Reading from two different disks| +-------------------------------------------- +Windows 7: + 1st(unbuffered) |2nd (OS buffered) + ---------------------------------- +1 Thread: 57s | 8s +2 Threads: 39s | 7s + +-------------------------------------------------- +|Testcase: Reading two directories from same disk| +-------------------------------------------------- +Windows 7: Windows XP: + 1st(unbuffered) |2nd (OS buffered) 1st(unbuffered) |2nd (OS buffered) + ---------------------------------- ---------------------------------- +1 Thread: 41s | 13s 1 Thread: 45s | 13s +2 Threads: 42s | 11s 2 Threads: 38s | 8s + +=> Traversing does not take any advantage of file locality so that even multiple threads operating on the same disk impose no performance overhead. +*/ + + +std::vector<std::set<DirectoryKey>> separateByDistinctDisk(const std::set<DirectoryKey>& dirkeys) +{ + //see perf note: use one thread per dirkey: + typedef std::map<int, std::set<DirectoryKey>> DiskKeyMapping; + DiskKeyMapping tmp; + int index = 0; + std::for_each(dirkeys.begin(), dirkeys.end(), + [&](const DirectoryKey& key) { tmp[++index].insert(key); }); + + /* + //use one thread per physical disk: + typedef std::map<DiskInfo, std::set<DirectoryKey>> DiskKeyMapping; + DiskKeyMapping tmp; + std::for_each(dirkeys.begin(), dirkeys.end(), + [&](const DirectoryKey& key) { tmp[retrieveDiskInfo(key.dirnameFull_)].insert(key); }); + */ + std::vector<std::set<DirectoryKey>> buckets; + std::transform(tmp.begin(), tmp.end(), std::back_inserter(buckets), + [&](const DiskKeyMapping::value_type& diskToKey) { return diskToKey.second; }); + return buckets; +} + +//------------------------------------------------------------------------------------------ +typedef Zbase<wchar_t, StorageRefCountThreadSafe> BasicWString; //thread safe string class for UI texts + + +class AsyncCallback +{ +public: + AsyncCallback() : + notifyingThreadID(-1), + textScanning(_("Scanning:")), + itemsScanned(0), + activeWorker(0) {} + + FillBufferCallback::HandleError reportError(const std::wstring& msg) //blocking call: context of worker thread + { + boost::unique_lock<boost::mutex> dummy(lockErrorMsg); + while(!errorMsg.empty() || errorResponse.get()) + conditionCanReportError.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! + + errorMsg = BasicWString(msg); + + while(!errorResponse.get()) + conditionGotResponse.timed_wait(dummy, boost::posix_time::milliseconds(50)); //interruption point! + + FillBufferCallback::HandleError rv = *errorResponse; + + errorMsg.clear(); + errorResponse.reset(); + conditionCanReportError.notify_one(); + return rv; + } + + void processErrors(FillBufferCallback& callback) //context of main thread, call repreatedly + { + boost::lock_guard<boost::mutex> dummy(lockErrorMsg); + if (!errorMsg.empty() && !errorResponse.get()) + { + FillBufferCallback::HandleError rv = callback.reportError(cvrtString<std::wstring>(errorMsg)); //throw! + errorResponse.reset(new FillBufferCallback::HandleError(rv)); + conditionGotResponse.notify_one(); + } + } + + void setNotifyingThread(int threadID) { notifyingThreadID = threadID; } //context of main thread + + void reportCurrentFile(const Zstring& filename, int threadID) //context of worker thread + { + if (threadID != notifyingThreadID) return; //only one thread may report status + + boost::lock_guard<boost::mutex> dummy(lockCurrentStatus); + currentFile = filename; + currentStatus.clear(); + } + + void reportCurrentStatus(const std::wstring& status, int threadID) //context of worker thread + { + if (threadID != notifyingThreadID) return; //only one thread may report status + + boost::lock_guard<boost::mutex> dummy(lockCurrentStatus); + currentFile.clear(); + currentStatus = BasicWString(status); //we cannot assume std::wstring to be thread safe (yet)! + } + + std::wstring getCurrentStatus() //context of main thread, call repreatedly + { + std::wstring filename; + std::wstring statusMsg; + { + boost::lock_guard<boost::mutex> dummy(lockCurrentStatus); + if (!currentFile.empty()) + filename = utf8CvrtTo<std::wstring>(currentFile); + else if (!currentStatus.empty()) + statusMsg = cvrtString<std::wstring>(currentStatus); + } + + if (!filename.empty()) + { + std::wstring statusText = cvrtString<std::wstring>(textScanning); + const long activeCount = activeWorker; + if (activeCount >= 2) + { + statusText += L" " + _P("[1 Thread]", "[%x Threads]", activeCount); + replace(statusText, L"%x", toString<std::wstring>(activeCount)); + } + statusText += std::wstring(L" \n") + L'\"' + filename + L'\"'; + return statusText; + } + else + return statusMsg; + } + + void incItemsScanned() { ++itemsScanned; } + long getItemsScanned() const { return itemsScanned; } + + void incActiveWorker() { ++activeWorker; } + void decActiveWorker() { --activeWorker; } + long getActiveWorker() const { return activeWorker; } + +private: + //---- error handling ---- + boost::mutex lockErrorMsg; + boost::condition_variable conditionCanReportError; + boost::condition_variable conditionGotResponse; + BasicWString errorMsg; + std::unique_ptr<FillBufferCallback::HandleError> errorResponse; + + //---- status updates ---- + volatile int notifyingThreadID; //theoretically racy, but there is nothing that could go wrong... + //CAVEAT: do NOT use boost::thread::id as long as this showstopper exists: https://svn.boost.org/trac/boost/ticket/5754 + boost::mutex lockCurrentStatus; //use a different lock for current file: continue traversing while some thread may process an error + Zstring currentFile; //only one of these two is filled at a time! + BasicWString currentStatus; // + + const BasicWString textScanning; //this one is (currently) not shared and could be made a std::wstring, but we stay consistent and use thread-safe variables in this class only! + + //---- status updates II (lock free) ---- + boost::detail::atomic_count itemsScanned; + boost::detail::atomic_count activeWorker; +}; +//------------------------------------------------------------------------------------------------- + +class DirCallback; + +struct TraverserConfig +{ +public: + TraverserConfig(int threadID, + SymLinkHandling handleSymlinks, + const HardFilter::FilterRef& filter, + std::set<Zstring>& failedReads, + AsyncCallback& acb) : + handleSymlinks_(handleSymlinks), + filterInstance(filter), + failedReads_(failedReads), + acb_(acb), + threadID_(threadID) {} + + typedef std::shared_ptr<DirCallback> CallbackPointer; + std::vector<CallbackPointer> callBackBox; //collection of callback pointers to handle ownership + + const SymLinkHandling handleSymlinks_; + const HardFilter::FilterRef filterInstance; //always bound! + std::set<Zstring>& failedReads_; //relative postfixed names of directories that could not be read (empty for root) + + AsyncCallback& acb_; + int threadID_; +}; + + +class DirCallback : public zen::TraverseCallback +{ +public: + DirCallback(TraverserConfig& config, + const Zstring& relNameParentPf, //postfixed with FILE_NAME_SEPARATOR! + DirContainer& output) : + cfg(config), + relNameParentPf_(relNameParentPf), + output_(output) {} + + virtual void onFile (const Zchar* shortName, const Zstring& fullName, const FileInfo& details); + virtual void onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details); + virtual ReturnValDir onDir (const Zchar* shortName, const Zstring& fullName); + virtual HandleError onError (const std::wstring& errorText); + +private: + TraverserConfig& cfg; + const Zstring relNameParentPf_; + DirContainer& output_; +}; + + +void DirCallback::onFile(const Zchar* shortName, const Zstring& fullName, const FileInfo& details) +{ + boost::this_thread::interruption_point(); + + const Zstring fileNameShort = shortName; + + //do not list the database file(s) sync.ffs_db, sync.x64.ffs_db, etc. or lock files + if (endsWith(fileNameShort, SYNC_DB_FILE_ENDING) || + endsWith(fileNameShort, LOCK_FILE_ENDING)) + return; + + //update status information no matter whether object is excluded or not! + cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); + + //------------------------------------------------------------------------------------ + //apply filter before processing (use relative name!) + if (!cfg.filterInstance->passFileFilter(relNameParentPf_ + fileNameShort)) + return; + + // std::string fileId = details.fileSize >= 1024 * 1024U ? + // util::retrieveFileID(fullName) : + // std::string(); + /* + Perf test Windows 7, SSD, 350k files, 50k dirs, files > 1MB: 7000 + regular: 6.9s + ID per file: 43.9s + ID per file > 1MB: 7.2s + ID per dir: 8.4s + + Linux: retrieveFileID takes about 50% longer in VM! (avoidable because of redundant stat() call!) + */ + + output_.addSubFile(fileNameShort, FileDescriptor(details.lastWriteTimeRaw, details.fileSize)); + + cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator +} + + +void DirCallback::onSymlink(const Zchar* shortName, const Zstring& fullName, const SymlinkInfo& details) +{ + boost::this_thread::interruption_point(); + + if (cfg.handleSymlinks_ == SYMLINK_IGNORE) + return; + + //update status information no matter whether object is excluded or not! + cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); + + //------------------------------------------------------------------------------------ + const Zstring& relName = relNameParentPf_ + shortName; + + //apply filter before processing (use relative name!) + if (!cfg.filterInstance->passFileFilter(relName)) //always use file filter: Link type may not be "stable" on Linux! + return; + + output_.addSubLink(shortName, LinkDescriptor(details.lastWriteTimeRaw, details.targetPath, details.dirLink ? LinkDescriptor::TYPE_DIR : LinkDescriptor::TYPE_FILE)); + + cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator +} + + +TraverseCallback::ReturnValDir DirCallback::onDir(const Zchar* shortName, const Zstring& fullName) +{ + boost::this_thread::interruption_point(); + + //update status information no matter whether object is excluded or not! + cfg.acb_.reportCurrentFile(fullName, cfg.threadID_); + + //------------------------------------------------------------------------------------ + const Zstring& relName = relNameParentPf_ + shortName; + + //apply filter before processing (use relative name!) + bool subObjMightMatch = true; + if (!cfg.filterInstance->passDirFilter(relName, &subObjMightMatch)) + { + if (!subObjMightMatch) + return Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_IGNORE>(); //do NOT traverse subdirs + } + else + cfg.acb_.incItemsScanned(); //add 1 element to the progress indicator + + DirContainer& subDir = output_.addSubDir(shortName); + + TraverserConfig::CallbackPointer subDirCallback = std::make_shared<DirCallback>(cfg, relName + FILE_NAME_SEPARATOR, subDir); + cfg.callBackBox.push_back(subDirCallback); //handle lifetime + //attention: ensure directory filtering is applied later to exclude actually filtered directories + return ReturnValDir(Loki::Int2Type<ReturnValDir::TRAVERSING_DIR_CONTINUE>(), *subDirCallback.get()); +} + + +DirCallback::HandleError DirCallback::onError(const std::wstring& errorText) +{ + switch (cfg.acb_.reportError(errorText)) + { + case FillBufferCallback::TRAV_ERROR_IGNORE: + cfg.failedReads_.insert(relNameParentPf_); + return TRAV_ERROR_IGNORE; + + case FillBufferCallback::TRAV_ERROR_RETRY: + return TRAV_ERROR_RETRY; + } + + assert(false); + return TRAV_ERROR_IGNORE; +} + + +#ifdef FFS_WIN +class DstHackCallbackImpl : public DstHackCallback +{ +public: + DstHackCallbackImpl(AsyncCallback& acb, int threadID) : + acb_(acb), + threadID_(threadID), + textApplyingDstHack(toZ(_("Encoding extended time information: %x")).Replace(Zstr("%x"), Zstr("\n\"%x\""))) {} + +private: + virtual void requestUiRefresh(const Zstring& filename) //applying DST hack imposes significant one-time performance drawback => callback to inform user + { + Zstring statusText = textApplyingDstHack; + replace(statusText, Zstr("%x"), filename); + acb_.reportCurrentStatus(utf8CvrtTo<std::wstring>(statusText), threadID_); + } + + AsyncCallback& acb_; + int threadID_; + const Zstring textApplyingDstHack; +}; +#endif +//------------------------------------------------------------------------------------------ + + +class WorkerThread +{ +public: + WorkerThread(int threadID, + const std::shared_ptr<AsyncCallback>& acb, + const std::vector<std::pair<DirectoryKey, DirectoryValue*>>& workload) : + threadID_(threadID), + acb_(acb), + workload_(workload) {} + + void operator()() //thread entry + { + acb_->incActiveWorker(); + Loki::ScopeGuard dummy = Loki::MakeGuard([&]() { acb_->decActiveWorker(); }); + (void)dummy; + + std::for_each(workload_.begin(), workload_.end(), + [&](std::pair<DirectoryKey, DirectoryValue*>& item) + { + const Zstring& directoryName = item.first.dirnameFull_; + DirectoryValue& dirVal = *item.second; + + acb_->reportCurrentFile(directoryName, threadID_); //just in case directory existence check is blocking! + + if (!directoryName.empty() && + util::dirExistsAsync(directoryName).get()) //blocking + interruption point! + //folder existence already checked in startCompareProcess(): do not treat as error when arriving here! + //perf note: missing network drives should not delay here, as Windows buffers result of last existence check for a short time + { + TraverserConfig travCfg(threadID_, + item.first.handleSymlinks_, //shared by all(!) instances of DirCallback while traversing a folder hierarchy + item.first.filter_, + dirVal.failedReads, + *acb_); + + DirCallback traverser(travCfg, + Zstring(), + dirVal.dirCont); + + bool followSymlinks = false; + switch (item.first.handleSymlinks_) + { + case SYMLINK_IGNORE: + followSymlinks = false; //=> symlinks will be reported via onSymlink() where they are excluded + break; + case SYMLINK_USE_DIRECTLY: + followSymlinks = false; + break; + case SYMLINK_FOLLOW_LINK: + followSymlinks = true; + break; + } + + DstHackCallback* dstCallbackPtr = NULL; +#ifdef FFS_WIN + DstHackCallbackImpl dstCallback(*acb_, threadID_); + dstCallbackPtr = &dstCallback; +#endif + + //get all files and folders from directoryPostfixed (and subdirectories) + traverseFolder(directoryName, followSymlinks, traverser, dstCallbackPtr); //exceptions may be thrown! + } + }); + } + +private: + int threadID_; + std::shared_ptr<AsyncCallback> acb_; + std::vector<std::pair<DirectoryKey, DirectoryValue*>> workload_; +}; +} + + +void zen::fillBuffer(const std::set<DirectoryKey>& keysToRead, //in + std::map<DirectoryKey, DirectoryValue>& buf, //out + FillBufferCallback& callback, + size_t statusInterval) +{ + buf.clear(); + + std::vector<std::set<DirectoryKey>> buckets = separateByDistinctDisk(keysToRead); //one bucket per physical device + + std::vector<boost::thread> worker; //note: GCC doesn't allow to construct an array of empty threads since they would be initialized by const boost::thread& + worker.reserve(buckets.size()); + + Loki::ScopeGuard guardWorker = Loki::MakeGuard([&]() + { + std::for_each(worker.begin(), worker.end(), std::mem_fun_ref(&boost::thread::interrupt)); //interrupt all at once, then join + std::for_each(worker.begin(), worker.end(), std::mem_fun_ref(&boost::thread::join)); + }); + + std::shared_ptr<AsyncCallback> acb = std::make_shared<AsyncCallback>(); + + //init worker threads + for (auto iter = buckets.begin(); iter != buckets.end(); ++iter) + { + int threadID = iter - buckets.begin(); + const std::set<DirectoryKey>& bucket = *iter; + + std::vector<std::pair<DirectoryKey, DirectoryValue*>> workload; + std::for_each(bucket.begin(), bucket.end(), + [&](const DirectoryKey& key) + { + auto rv = buf.insert(std::make_pair(key, DirectoryValue())); + assert(rv.second); + workload.push_back(std::make_pair(key, &rv.first->second)); + }); + + worker.push_back(boost::thread(WorkerThread(threadID, acb, workload))); + } + + //wait until done + for (auto iter = worker.begin(); iter != worker.end(); ++iter) + { + boost::thread& wt = *iter; + int threadID = iter - worker.begin(); + + acb->setNotifyingThread(threadID); //process info messages of first (active) thread only + + do + { + //update status + callback.reportStatus(acb->getCurrentStatus(), acb->getItemsScanned()); //throw! + + //process errors + acb->processErrors(callback); + } + while (!wt.timed_join(boost::posix_time::milliseconds(statusInterval))); + } + + guardWorker.Dismiss(); +} diff --git a/library/parallel_scan.h b/library/parallel_scan.h new file mode 100644 index 00000000..f36c5ec7 --- /dev/null +++ b/library/parallel_scan.h @@ -0,0 +1,74 @@ +// ************************************************************************** +// * This file is part of the FreeFileSync project. It is distributed under * +// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * +// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#ifndef PARALLEL_SCAN_H_INCLUDED +#define PARALLEL_SCAN_H_INCLUDED + +#include <map> +#include <set> +#include "hard_filter.h" +#include "../structures.h" +#include "../file_hierarchy.h" + +namespace zen +{ +struct DirectoryKey +{ + DirectoryKey(const Zstring& dirnameFull, + const HardFilter::FilterRef& filter, + SymLinkHandling handleSymlinks) : + dirnameFull_(dirnameFull), + filter_(filter), + handleSymlinks_(handleSymlinks) {} + + Zstring dirnameFull_; + HardFilter::FilterRef filter_; //filter interface: always bound by design! + SymLinkHandling handleSymlinks_; +}; + +inline +bool operator<(const DirectoryKey& lhs, const DirectoryKey& rhs) +{ + if (lhs.handleSymlinks_ != rhs.handleSymlinks_) + return lhs.handleSymlinks_ < rhs.handleSymlinks_; + + if (!EqualFilename()(lhs.dirnameFull_, rhs.dirnameFull_)) + return LessFilename()(lhs.dirnameFull_, rhs.dirnameFull_); + + return *lhs.filter_ < *rhs.filter_; +} + + +struct DirectoryValue +{ + DirContainer dirCont; + std::set<Zstring> failedReads; //relative postfixed names of directories that could not be read (empty for root), e.g. access denied, or temporal network drop +}; + + +class FillBufferCallback +{ +public: + virtual ~FillBufferCallback() {} + + enum HandleError + { + TRAV_ERROR_RETRY, + TRAV_ERROR_IGNORE + }; + virtual HandleError reportError (const std::wstring& errorText) = 0; //may throw! + virtual void reportStatus(const std::wstring& statusMsg, int itemTotal) = 0; // +}; + +//attention: ensure directory filtering is applied later to exclude filtered directories which have been erroneously kept + +void fillBuffer(const std::set<DirectoryKey>& keysToRead, //in + std::map<DirectoryKey, DirectoryValue>& buf, //out + FillBufferCallback& callback, + size_t statusInterval); //unit: [ms] +} + +#endif // PARALLEL_SCAN_H_INCLUDED diff --git a/library/process_xml.cpp b/library/process_xml.cpp index 69dad7f3..276bc977 100644 --- a/library/process_xml.cpp +++ b/library/process_xml.cpp @@ -3,9 +3,9 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "process_xml.h" -#include <zenxml/zenxml.h> +#include <zenXml/zenxml.h> #include "../shared/i18n.h" #include "../shared/global_func.h" #include "../shared/standard_paths.h" @@ -42,10 +42,10 @@ XmlType xmlAccess::getXmlType(const wxString& filename) //throw() XmlDoc doc; try { - std::string stream = loadStream(filename); //throw XmlFileError - parse(stream, doc); //throw XmlParsingError + //do NOT use zen::loadStream as it will superfluously load even huge files! + loadXmlDocument(filename, doc); //throw FfsXmlError, quick exit if file is not an FFS XML } - catch (const zen::XmlError&) //catch XmlFileError, XmlParsingError + catch (const FfsXmlError&) { return XML_TYPE_OTHER; } diff --git a/library/process_xml.h b/library/process_xml.h index d7437825..e8a11962 100644 --- a/library/process_xml.h +++ b/library/process_xml.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef PROCESSXML_H_INCLUDED #define PROCESSXML_H_INCLUDED diff --git a/library/resources.cpp b/library/resources.cpp index 179033a7..cbfd7c06 100644 --- a/library/resources.cpp +++ b/library/resources.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "resources.h" #include <wx/wfstream.h> #include <wx/zipstrm.h> diff --git a/library/resources.h b/library/resources.h index 691d5a6b..f812ac90 100644 --- a/library/resources.h +++ b/library/resources.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef RESOURCES_H_INCLUDED #define RESOURCES_H_INCLUDED diff --git a/library/soft_filter.h b/library/soft_filter.h index 0a406907..bde812b0 100644 --- a/library/soft_filter.h +++ b/library/soft_filter.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SOFT_FILTER_H_INCLUDED #define SOFT_FILTER_H_INCLUDED diff --git a/library/statistics.cpp b/library/statistics.cpp index 812c869b..a29f60af 100644 --- a/library/statistics.cpp +++ b/library/statistics.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "statistics.h" #include <wx/ffile.h> diff --git a/library/statistics.h b/library/statistics.h index a9692d77..0ec82c14 100644 --- a/library/statistics.h +++ b/library/statistics.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef STATISTICS_H_INCLUDED #define STATISTICS_H_INCLUDED diff --git a/library/status_handler.cpp b/library/status_handler.cpp index 0a131899..9c2fdd67 100644 --- a/library/status_handler.cpp +++ b/library/status_handler.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "status_handler.h" #include <wx/app.h> #include <wx/timer.h> diff --git a/library/status_handler.h b/library/status_handler.h index a7984790..a295b6a0 100644 --- a/library/status_handler.h +++ b/library/status_handler.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef STATUSHANDLER_H_INCLUDED #define STATUSHANDLER_H_INCLUDED diff --git a/shared/IFileOperation/dll_main.cpp b/shared/IFileOperation/dll_main.cpp index ab387012..3805c99d 100644 --- a/shared/IFileOperation/dll_main.cpp +++ b/shared/IFileOperation/dll_main.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #define WIN32_LEAN_AND_MEAN #include <windows.h> diff --git a/shared/IFileOperation/file_op.cpp b/shared/IFileOperation/file_op.cpp index fc942f44..a2cf9413 100644 --- a/shared/IFileOperation/file_op.cpp +++ b/shared/IFileOperation/file_op.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "file_op.h" #include "../com_ptr.h" #include "../com_error.h" diff --git a/shared/IFileOperation/file_op.h b/shared/IFileOperation/file_op.h index 25098e45..c1bb26a2 100644 --- a/shared/IFileOperation/file_op.h +++ b/shared/IFileOperation/file_op.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef RECYCLER_DLL_H #define RECYCLER_DLL_H diff --git a/shared/ShadowCopy/LockFile.cpp b/shared/ShadowCopy/LockFile.cpp index 0761a3e3..7df3ec66 100644 --- a/shared/ShadowCopy/LockFile.cpp +++ b/shared/ShadowCopy/LockFile.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include <string> #include <iostream> diff --git a/shared/ShadowCopy/dll_main.cpp b/shared/ShadowCopy/dll_main.cpp index ab387012..3805c99d 100644 --- a/shared/ShadowCopy/dll_main.cpp +++ b/shared/ShadowCopy/dll_main.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #define WIN32_LEAN_AND_MEAN #include <windows.h> diff --git a/shared/ShadowCopy/shadow.cpp b/shared/ShadowCopy/shadow.cpp index d536470a..8b7b9971 100644 --- a/shared/ShadowCopy/shadow.cpp +++ b/shared/ShadowCopy/shadow.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "shadow.h" #include <algorithm> #include <string> diff --git a/shared/ShadowCopy/shadow.h b/shared/ShadowCopy/shadow.h index a96d8578..b21395da 100644 --- a/shared/ShadowCopy/shadow.h +++ b/shared/ShadowCopy/shadow.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SHADOWCOPY_H #define SHADOWCOPY_H diff --git a/shared/Taskbar_Seven/dll_main.cpp b/shared/Taskbar_Seven/dll_main.cpp index ab387012..3805c99d 100644 --- a/shared/Taskbar_Seven/dll_main.cpp +++ b/shared/Taskbar_Seven/dll_main.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #define WIN32_LEAN_AND_MEAN #include <windows.h> diff --git a/shared/Taskbar_Seven/taskbar.cpp b/shared/Taskbar_Seven/taskbar.cpp index 297a0739..bbbaaf7d 100644 --- a/shared/Taskbar_Seven/taskbar.cpp +++ b/shared/Taskbar_Seven/taskbar.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "taskbar.h" #define WIN32_LEAN_AND_MEAN diff --git a/shared/Taskbar_Seven/taskbar.h b/shared/Taskbar_Seven/taskbar.h index d52420ab..3bae28a9 100644 --- a/shared/Taskbar_Seven/taskbar.h +++ b/shared/Taskbar_Seven/taskbar.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef TASKBAR_SEVEN_DLL_H #define TASKBAR_SEVEN_DLL_H diff --git a/shared/app_main.cpp b/shared/app_main.cpp index b727724f..28ce1e09 100644 --- a/shared/app_main.cpp +++ b/shared/app_main.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "app_main.h" #include <wx/window.h> #include <wx/app.h> diff --git a/shared/app_main.h b/shared/app_main.h index 33ddcfb6..fa538771 100644 --- a/shared/app_main.h +++ b/shared/app_main.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef APPMAIN_H_INCLUDED #define APPMAIN_H_INCLUDED diff --git a/shared/build_info.h b/shared/build_info.h index c3be26bd..f13b2dbc 100644 --- a/shared/build_info.h +++ b/shared/build_info.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef BUILDINFO_H_INCLUDED #define BUILDINFO_H_INCLUDED diff --git a/shared/c_dll.h b/shared/c_dll.h index f913cde9..15597a9c 100644 --- a/shared/c_dll.h +++ b/shared/c_dll.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef C_DLL_HEADER #define C_DLL_HEADER diff --git a/shared/check_exist.cpp b/shared/check_exist.cpp deleted file mode 100644 index 6c6c0871..00000000 --- a/shared/check_exist.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// ************************************************************************** -// * This file is part of the FreeFileSync project. It is distributed under * -// * GNU General Public License: http://www.gnu.org/licenses/gpl.html * -// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * -// ************************************************************************** -// -#include "check_exist.h" -#include "file_handling.h" -#include <memory> -#include "boost_thread_wrap.h" //include <boost/thread.hpp> - - -/* -#ifdef __MINGW32__ -//oh well, nothing is for free... -//https://svn.boost.org/trac/boost/ticket/4258 -extern "C" void tss_cleanup_implemented() {}; -#endif -*/ - -namespace -{ -typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class - -template <bool (*fun)(const Zstring&)> -util::ResultExist checkExistence(const Zstring& objName, size_t timeout) //timeout in ms -{ - using namespace util; - - std::shared_ptr<bool> isExisting = std::make_shared<bool>(false); //no mutex required: accessed after thread has finished only! - BasicString filename = objName.c_str(); //using thread safe string without ref-count! - - boost::thread worker([=]() { *isExisting = fun(filename.c_str()); }); //throw() - - if (worker.timed_join(boost::posix_time::milliseconds(timeout))) - return *isExisting ? EXISTING_TRUE : EXISTING_FALSE; - else - return EXISTING_TIMEOUT; - /* - main/worker thread may access different shared_ptr instances safely (even though they have the same target!) - http://www.boost.org/doc/libs/1_43_0/libs/smart_ptr/shared_ptr.htm?sess=8153b05b34d890e02d48730db1ff7ddc#ThreadSafety - */ -} -} - - -util::ResultExist util::fileExists(const Zstring& filename, size_t timeout) //timeout in ms -{ - assert(!filename.empty()); - return ::checkExistence<zen::fileExists>(filename, timeout); -} - - -util::ResultExist util::dirExists(const Zstring& dirname, size_t timeout) //timeout in ms -{ - assert(!dirname.empty()); - return ::checkExistence<zen::dirExists>(dirname, timeout); -} diff --git a/shared/check_exist.h b/shared/check_exist.h index 6ec5534c..14a8a3f8 100644 --- a/shared/check_exist.h +++ b/shared/check_exist.h @@ -3,23 +3,86 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef CHECKEXIST_H_INCLUDED #define CHECKEXIST_H_INCLUDED #include "zstring.h" +#include "boost_thread_wrap.h" //include <boost/thread.hpp> +#include "file_handling.h" + namespace util { +//check for file or folder existence asynchronously +boost::unique_future<bool> somethingExistsAsync(const Zstring& somename); +boost::unique_future<bool> fileExistsAsync(const Zstring& filename); +boost::unique_future<bool> dirExistsAsync(const Zstring& dirname); + +//some syntactic sugar: enum ResultExist { EXISTING_TRUE, EXISTING_FALSE, - EXISTING_TIMEOUT + EXISTING_NOT_READY }; -ResultExist fileExists(const Zstring& filename, size_t timeout); //timeout in ms -ResultExist dirExists( const Zstring& dirname, size_t timeout); //timeout in ms +ResultExist somethingExists(const Zstring& somename, size_t timeout); +ResultExist fileExists(const Zstring& filename, size_t timeout); +ResultExist dirExists(const Zstring& dirname, size_t timeout); + + + + + + + + + + + + +//################## implementation ########################## +template <bool (*fun)(const Zstring&)> +boost::unique_future<bool> objExistsAsync(const Zstring& objname) +{ + //thread safety: make it a pure value type for use in the thread! + const Zstring objnameVal = objname; //atomic ref-count => binary value-type semantics! + boost::packaged_task<bool> pt([=] { return (*fun)(objnameVal); }); + auto fut = pt.get_future(); + boost::thread(std::move(pt)); + return std::move(fut); +} + + +inline +boost::unique_future<bool> somethingExistsAsync(const Zstring& somename) { return objExistsAsync<&zen::somethingExists>(somename); } + +inline +boost::unique_future<bool> fileExistsAsync(const Zstring& filename) { return objExistsAsync<&zen::fileExists>(filename); } + +inline +boost::unique_future<bool> dirExistsAsync(const Zstring& dirname) { return objExistsAsync<&zen::dirExists>(dirname); } + + +template <bool (*fun)(const Zstring&)> inline +ResultExist objExists(const Zstring& objname, size_t timeout) +{ + auto ft = objExistsAsync<fun>(objname); + if (!ft.timed_wait(boost::posix_time::milliseconds(timeout))) + return EXISTING_NOT_READY; + return ft.get() ? EXISTING_TRUE : EXISTING_FALSE; +} + + +inline +ResultExist somethingExists(const Zstring& somename, size_t timeout) { return objExists<&zen::somethingExists>(somename, timeout); } + +inline +ResultExist fileExists(const Zstring& filename, size_t timeout) { return objExists<&zen::fileExists>(filename, timeout); } + +inline +ResultExist dirExists(const Zstring& dirname, size_t timeout) { return objExists<&zen::dirExists>(dirname, timeout); } } #endif // CHECKEXIST_H_INCLUDED diff --git a/shared/com_error.h b/shared/com_error.h index 909e04c1..39f26555 100644 --- a/shared/com_error.h +++ b/shared/com_error.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef COM_ERROR_HEADER #define COM_ERROR_HEADER diff --git a/shared/com_ptr.h b/shared/com_ptr.h index fa1b3e8f..be1776fd 100644 --- a/shared/com_ptr.h +++ b/shared/com_ptr.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SMART_COM_PTR_H #define SMART_COM_PTR_H diff --git a/shared/com_util.h b/shared/com_util.h index 4a8c4f54..b8fc777d 100644 --- a/shared/com_util.h +++ b/shared/com_util.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef COM_UTILITY_HEADER #define COM_UTILITY_HEADER diff --git a/shared/custom_button.cpp b/shared/custom_button.cpp index 98c0a3cd..0c4c3019 100644 --- a/shared/custom_button.cpp +++ b/shared/custom_button.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "custom_button.h" #include <wx/dcmemory.h> #include <wx/image.h> diff --git a/shared/custom_button.h b/shared/custom_button.h index 752c763e..8500e59b 100644 --- a/shared/custom_button.h +++ b/shared/custom_button.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef CUSTOMBUTTON_H_INCLUDED #define CUSTOMBUTTON_H_INCLUDED diff --git a/shared/custom_combo_box.cpp b/shared/custom_combo_box.cpp index f6084fdf..fb16a303 100644 --- a/shared/custom_combo_box.cpp +++ b/shared/custom_combo_box.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "custom_combo_box.h" diff --git a/shared/custom_combo_box.h b/shared/custom_combo_box.h index 070790a6..3ddd73b0 100644 --- a/shared/custom_combo_box.h +++ b/shared/custom_combo_box.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef CUSTOMCOMBOBOX_H_INCLUDED #define CUSTOMCOMBOBOX_H_INCLUDED diff --git a/shared/custom_tooltip.cpp b/shared/custom_tooltip.cpp index f19f3822..184c2716 100644 --- a/shared/custom_tooltip.cpp +++ b/shared/custom_tooltip.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "custom_tooltip.h" #include <wx/stattext.h> #include <wx/sizer.h> diff --git a/shared/custom_tooltip.h b/shared/custom_tooltip.h index c5cb132b..c68856b4 100644 --- a/shared/custom_tooltip.h +++ b/shared/custom_tooltip.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef CUSTOMTOOLTIP_H_INCLUDED #define CUSTOMTOOLTIP_H_INCLUDED diff --git a/shared/debug_log.h b/shared/debug_log.h index 8e8090f0..561eee9d 100644 --- a/shared/debug_log.h +++ b/shared/debug_log.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef DEBUG_LOG_HEADER_017324601673246392184621895740256342 #define DEBUG_LOG_HEADER_017324601673246392184621895740256342 diff --git a/shared/debug_new.cpp b/shared/debug_new.cpp index a7324239..23a26a4e 100644 --- a/shared/debug_new.cpp +++ b/shared/debug_new.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "debug_new.h" #ifdef _MSC_VER diff --git a/shared/debug_new.h b/shared/debug_new.h index afa66a55..b8911b4d 100644 --- a/shared/debug_new.h +++ b/shared/debug_new.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef DEBUGNEW_H_INCLUDED #define DEBUGNEW_H_INCLUDED diff --git a/shared/dir_name.cpp b/shared/dir_name.cpp index 6566bb3a..6945fb9c 100644 --- a/shared/dir_name.cpp +++ b/shared/dir_name.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "dir_name.h" #include <wx/dnd.h> #include <wx/window.h> @@ -40,7 +40,8 @@ void setDirectoryNameImpl(const wxString& dirname, wxDirPickerCtrl* dirPicker, w if (dirPicker) { - if (!dirFormatted.empty() && util::dirExists(toZ(dirFormatted), timeout) == util::EXISTING_TRUE) //potentially slow network access: wait 200ms at most + if (!dirFormatted.empty() && + util::dirExists(toZ(dirFormatted), timeout) == util::EXISTING_TRUE) //potentially slow network access: wait 200ms at most dirPicker->SetPath(dirFormatted); } } diff --git a/shared/dir_name.h b/shared/dir_name.h index 4bf07231..6e783d75 100644 --- a/shared/dir_name.h +++ b/shared/dir_name.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef DRAGANDDROP_H_INCLUDED #define DRAGANDDROP_H_INCLUDED diff --git a/shared/dir_watcher.cpp b/shared/dir_watcher.cpp index 8e0b7f94..9c3a00c7 100644 --- a/shared/dir_watcher.cpp +++ b/shared/dir_watcher.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "dir_watcher.h" #include "last_error.h" #include "i18n.h" @@ -32,23 +32,22 @@ using namespace zen; #ifdef FFS_WIN namespace { -typedef Zbase<Zchar, StorageDeepCopy> BasicString; //thread safe string class for file names -typedef Zbase<wchar_t, StorageDeepCopy> BasicWString; //thread safe string class for UI texts +typedef Zbase<wchar_t> BasicWString; //thread safe string class for UI texts struct SharedData { boost::mutex lockAccess; - std::set<BasicString> changedFiles; //get rid of duplicate entries (actually occur!) + std::set<Zstring> changedFiles; //get rid of duplicate entries (actually occur!) BasicWString errorMsg; //non-empty if errors occured in thread }; -void addChanges(SharedData& shared, const char* buffer, DWORD bytesWritten, const BasicString& dirname) //throw () +void addChanges(SharedData& shared, const char* buffer, DWORD bytesWritten, const Zstring& dirname) //throw () { boost::lock_guard<boost::mutex> dummy(shared.lockAccess); - std::set<BasicString>& output = shared.changedFiles; + std::set<Zstring>& output = shared.changedFiles; if (bytesWritten == 0) //according to docu this may happen in case of internal buffer overflow: report some "dummy" change output.insert(L"Overflow!"); @@ -59,14 +58,14 @@ void addChanges(SharedData& shared, const char* buffer, DWORD bytesWritten, cons { const FILE_NOTIFY_INFORMATION& notifyInfo = reinterpret_cast<const FILE_NOTIFY_INFORMATION&>(*bufPos); - const BasicString fullname = dirname + BasicString(notifyInfo.FileName, notifyInfo.FileNameLength / sizeof(WCHAR)); + const Zstring fullname = dirname + Zstring(notifyInfo.FileName, notifyInfo.FileNameLength / sizeof(WCHAR)); //skip modifications sent by changed directories: reason for change, child element creation/deletion, will notify separately! bool skip = false; if (notifyInfo.Action == FILE_ACTION_MODIFIED) { //note: this check will not work if top watched directory has been renamed - const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(Zstring(fullname)).c_str()); + const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(fullname).c_str()); bool isDir = ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY); //returns true for (dir-)symlinks also skip = isDir; } @@ -90,9 +89,7 @@ void getChanges(SharedData& shared, std::vector<Zstring>& output) //throw FileEr if (!shared.errorMsg.empty()) throw zen::FileError(shared.errorMsg.c_str()); - std::transform(shared.changedFiles.begin(), shared.changedFiles.end(), - std::back_inserter(output), [](const BasicString& str) { return Zstring(str); }); - + output.assign(shared.changedFiles.begin(), shared.changedFiles.end()); shared.changedFiles.clear(); } @@ -107,7 +104,7 @@ void reportError(SharedData& shared, const BasicWString& errorMsg) //throw () class ReadChangesAsync { public: - ReadChangesAsync(const BasicString& directory, //make sure to not leak in thread-unsafe types! + ReadChangesAsync(const Zstring& directory, //make sure to not leak in thread-unsafe types! const std::shared_ptr<SharedData>& shared) : shared_(shared), dirname(directory) @@ -239,7 +236,7 @@ private: //shared between main and worker: std::shared_ptr<SharedData> shared_; //worker thread only: - BasicString dirname; //conceptually thread-only, but technically moved to thread-local storage on instance creation: -> must not use ref-copying! + Zstring dirname; //thread safe! HANDLE hDir; }; @@ -250,7 +247,7 @@ public: HandleVolumeRemoval(HANDLE hDir, boost::thread& worker, const std::shared_ptr<SharedData>& shared, - const BasicString& dirname) : + const Zstring& dirname) : NotifyRequestDeviceRemoval(hDir), //throw FileError worker_(worker), shared_(shared), @@ -283,7 +280,7 @@ private: boost::thread& worker_; std::shared_ptr<SharedData> shared_; - BasicString dirname_; + Zstring dirname_; bool removalRequested; bool operationComplete; }; @@ -304,8 +301,8 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError { pimpl_->shared = std::make_shared<SharedData>(); - ReadChangesAsync reader(BasicString(directory), pimpl_->shared); //throw FileError - pimpl_->volRemoval.reset(new HandleVolumeRemoval(reader.getDirHandle(), pimpl_->worker, pimpl_->shared, BasicString(directory))); //throw FileError + ReadChangesAsync reader(directory, pimpl_->shared); //throw FileError + pimpl_->volRemoval.reset(new HandleVolumeRemoval(reader.getDirHandle(), pimpl_->worker, pimpl_->shared, directory)); //throw FileError pimpl_->worker = boost::thread(std::move(reader)); } diff --git a/shared/dir_watcher.h b/shared/dir_watcher.h index 572515dc..c2bab1d5 100644 --- a/shared/dir_watcher.h +++ b/shared/dir_watcher.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef DIR_WATCHER_348577025748023458 #define DIR_WATCHER_348577025748023458 diff --git a/shared/dll_loader.cpp b/shared/dll_loader.cpp index 029cabc5..4e2c0e65 100644 --- a/shared/dll_loader.cpp +++ b/shared/dll_loader.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "dll_loader.h" #include <map> diff --git a/shared/dll_loader.h b/shared/dll_loader.h index 3efb0d96..252e7598 100644 --- a/shared/dll_loader.h +++ b/shared/dll_loader.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef DLLLOADER_H_INCLUDED #define DLLLOADER_H_INCLUDED @@ -23,7 +23,7 @@ namespace util /* load function from a DLL library, e.g. from kernel32.dll -NOTE: you're allowed to take a static reference to the return value to optimize performance! :) +NOTE: you're allowed to take a static reference to the return value! :) */ template <typename FunctionType> FunctionType getDllFun(const std::wstring& libraryName, const std::string& functionName); diff --git a/shared/dst_hack.cpp b/shared/dst_hack.cpp index a64df448..d52a335f 100644 --- a/shared/dst_hack.cpp +++ b/shared/dst_hack.cpp @@ -28,8 +28,8 @@ Zstring getVolumeName(const Zstring& filename) // volumePath += FILE_NAME_SEPARATOR; Zstring nameFmt = removeLongPathPrefix(filename); //throw() - if (!nameFmt.EndsWith(Zstr("\\"))) - nameFmt += Zstr("\\"); //GetVolumeInformation expects trailing backslash + if (!endsWith(nameFmt, FILE_NAME_SEPARATOR)) + nameFmt += FILE_NAME_SEPARATOR; //GetVolumeInformation expects trailing backslash if (nameFmt.StartsWith(Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\" { @@ -58,8 +58,6 @@ Zstring getVolumeName(const Zstring& filename) bool dst::isFatDrive(const Zstring& fileName) //throw() { - using namespace zen; - const size_t BUFFER_SIZE = MAX_PATH + 1; wchar_t fsName[BUFFER_SIZE]; diff --git a/shared/file_drop.h b/shared/file_drop.h index c85d4cbb..8b11ccfb 100644 --- a/shared/file_drop.h +++ b/shared/file_drop.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FILE_DROP_H_INCLUDED #define FILE_DROP_H_INCLUDED diff --git a/shared/file_error.h b/shared/file_error.h index ae9cc83e..d814e05d 100644 --- a/shared/file_error.h +++ b/shared/file_error.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FILEERROR_H_INCLUDED #define FILEERROR_H_INCLUDED @@ -35,7 +35,7 @@ DEFINE_NEW_FILE_ERROR(ErrorFileLocked); -//facilitate usage of std::wstring for error messages: +//----------- facilitate usage of std::wstring for error messages -------------------- //allow implicit UTF8 conversion: since std::wstring models a GUI string, convenience is more important than performance inline std::wstring operator+(const std::wstring& lhs, const Zstring& rhs) { return std::wstring(lhs) += zen::utf8CvrtTo<std::wstring>(rhs); } diff --git a/shared/file_handling.cpp b/shared/file_handling.cpp index a5e7fa9f..6b41f4f5 100644 --- a/shared/file_handling.cpp +++ b/shared/file_handling.cpp @@ -3,11 +3,10 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "file_handling.h" #include <map> #include <algorithm> -#include <boost/bind.hpp> #include <stdexcept> #include "last_error.h" #include "file_traverser.h" @@ -16,6 +15,8 @@ #include "file_io.h" #include "i18n.h" #include "assert_static.h" +#include <boost/thread/tss.hpp> +#include <boost/thread/once.hpp> #ifdef FFS_WIN #include "privilege.h" @@ -24,6 +25,7 @@ #include "long_path_prefix.h" #include <Aclapi.h> #include "dst_hack.h" +#include "file_update_handle.h" #elif defined FFS_LINUX #include <sys/stat.h> @@ -99,68 +101,10 @@ bool zen::somethingExists(const Zstring& objname) //throw() check whether #ifdef FFS_WIN namespace { -//manage file handle to update existing files (temporarily resetting read-only if necessary) -//CreateFileCmd: lambda directly returning non-owned file handle from ::CreateFile() -class FileUpdateHandle -{ -public: - template <class CreateFileCmd> - FileUpdateHandle(const Zstring& filename, CreateFileCmd cmd) : - filenameFmt(applyLongPathPrefix(filename)), - hFile(INVALID_HANDLE_VALUE), - attr(INVALID_FILE_ATTRIBUTES) - { - hFile = cmd(); - if (hFile != INVALID_HANDLE_VALUE) - return; - - const DWORD lastError = ::GetLastError(); - if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only - { - Loki::ScopeGuard guardErrorCode = Loki::MakeGuard(::SetLastError, lastError); //transactional behavior: ensure cleanup (e.g. network drop) -> cref [!] - - //read-only file attribute may cause trouble: temporarily reset it - const DWORD tmpAttr = ::GetFileAttributes(filenameFmt.c_str()); - if (tmpAttr != INVALID_FILE_ATTRIBUTES && (tmpAttr & FILE_ATTRIBUTE_READONLY)) - { - if (::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL)) - { - guardErrorCode.Dismiss(); - attr = tmpAttr; //"create" guard on read-only attribute - - //now try again - hFile = cmd(); - } - } - } - } - - ~FileUpdateHandle() - { - if (hFile != INVALID_HANDLE_VALUE) - ::CloseHandle(hFile); - - if (attr != INVALID_FILE_ATTRIBUTES) - ::SetFileAttributes(filenameFmt.c_str(), attr); - } - - //may return INVALID_FILE_ATTRIBUTES, in which case ::GetLastError() may be called directly after FileUpdateHandle() - HANDLE get() const { return hFile; } - -private: - FileUpdateHandle(const FileUpdateHandle&); - FileUpdateHandle& operator=(const FileUpdateHandle&); - - Zstring filenameFmt; - HANDLE hFile; - DWORD attr; -}; - - zen::UInt64 getFileSizeSymlink(const Zstring& linkName) //throw (FileError) { //open handle to target of symbolic link - const HANDLE hFile = ::CreateFile(zen::applyLongPathPrefix(linkName).c_str(), + const HANDLE hFile = ::CreateFile(applyLongPathPrefix(linkName).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, @@ -168,28 +112,28 @@ zen::UInt64 getFileSizeSymlink(const Zstring& linkName) //throw (FileError) FILE_FLAG_BACKUP_SEMANTICS, NULL); if (hFile == INVALID_HANDLE_VALUE) - throw FileError(_("Error reading file attributes:") + "\n\"" + linkName + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + linkName + "\"" + "\n\n" + getLastErrorFormatted()); Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hFile); (void)dummy; //silence warning "unused variable" BY_HANDLE_FILE_INFORMATION fileInfo = {}; if (!::GetFileInformationByHandle(hFile, &fileInfo)) - throw FileError(_("Error reading file attributes:") + "\n\"" + linkName + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + linkName + "\"" + "\n\n" + getLastErrorFormatted()); - return zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); + return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); } } #endif -zen::UInt64 zen::getFilesize(const Zstring& filename) //throw (FileError) +UInt64 zen::getFilesize(const Zstring& filename) //throw (FileError) { #ifdef FFS_WIN WIN32_FIND_DATA fileInfo = {}; const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filename).c_str(), &fileInfo); if (searchHandle == INVALID_HANDLE_VALUE) - throw FileError(_("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); ::FindClose(searchHandle); @@ -197,14 +141,14 @@ zen::UInt64 zen::getFilesize(const Zstring& filename) //throw (FileError) if (isSymbolicLink) return getFileSizeSymlink(filename); //throw (FileError) - return zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); + return UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); #elif defined FFS_LINUX struct stat fileInfo = {}; if (::stat(filename.c_str(), &fileInfo) != 0) //follow symbolic links - throw FileError(_("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted()); - return zen::UInt64(fileInfo.st_size); + return UInt64(fileInfo.st_size); #endif } @@ -214,7 +158,7 @@ namespace #ifdef FFS_WIN DWORD retrieveVolumeSerial(const Zstring& pathName) //return 0 on error! { - std::vector<wchar_t> buffer(std::max(pathName.size(), static_cast<size_t>(10000))); + std::vector<wchar_t> buffer(10000); //full pathName need not yet exist! if (!::GetVolumePathName(pathName.c_str(), //__in LPCTSTR lpszFileName, @@ -263,14 +207,14 @@ dev_t retrieveVolumeSerial(const Zstring& pathName) //return 0 on error! } -zen::ResponseSameVol zen::onSameVolume(const Zstring& folderLeft, const Zstring& folderRight) //throw() +zen::ResponseSame zen::onSameVolume(const Zstring& folderLeft, const Zstring& folderRight) //throw() { const auto serialLeft = retrieveVolumeSerial(folderLeft); //returns 0 on error! const auto serialRight = retrieveVolumeSerial(folderRight); //returns 0 on error! if (serialLeft == 0 || serialRight == 0) - return VOLUME_CANT_SAY; + return IS_SAME_CANT_SAY; - return serialLeft == serialRight ? VOLUME_SAME : VOLUME_DIFFERENT; + return serialLeft == serialRight ? IS_SAME_YES : IS_SAME_NO; } @@ -302,12 +246,12 @@ bool zen::removeFile(const Zstring& filename) //throw (FileError); #endif //no error situation if file is not existing! manual deletion relies on it! - //perf: place check in error handling block + //perf: check is placed in error handling block //warning: this call changes error code!! if (!somethingExists(filename)) return false; //neither file nor any other object (e.g. broken symlink) with that name existing - throw FileError(_("Error deleting file:") + "\n\"" + filename + "\"" + "\n\n" + zen::getLastErrorFormatted(lastError)); + throw FileError(_("Error deleting file:") + "\n\"" + filename + "\"" + "\n\n" + getLastErrorFormatted(lastError)); } return true; } @@ -364,7 +308,7 @@ void renameFileInternal(const Zstring& oldName, const Zstring& newName) //throw } } - std::wstring errorMessage = _("Error moving file:") + "\n\"" + oldName + "\" ->\n\"" + newName + "\"" + "\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error moving file:") + "\n\"" + oldName + "\" ->\n\"" + newName + "\"" + "\n\n" + getLastErrorFormatted(lastError); if (lastError == ERROR_NOT_SAME_DEVICE) throw ErrorDifferentVolume(errorMessage); @@ -379,7 +323,7 @@ void renameFileInternal(const Zstring& oldName, const Zstring& newName) //throw { const int lastError = errno; - std::wstring errorMessage = _("Error moving file:") + "\n\"" + oldName + "\" ->\n\"" + newName + "\"" + "\n\n" + zen::getLastErrorFormatted(lastError); + std::wstring errorMessage = _("Error moving file:") + "\n\"" + oldName + "\" ->\n\"" + newName + "\"" + "\n\n" + getLastErrorFormatted(lastError); if (lastError == EXDEV) throw ErrorDifferentVolume(errorMessage); @@ -399,7 +343,7 @@ void renameFileInternal(const Zstring& oldName, const Zstring& newName) //throw template <typename Function> Zstring getFilenameFmt(const Zstring& filename, Function fun) //throw(); returns empty string on error { - const Zstring filenameFmt = zen::applyLongPathPrefix(filename); + const Zstring filenameFmt = applyLongPathPrefix(filename); const DWORD bufferSize = fun(filenameFmt.c_str(), NULL, 0); if (bufferSize == 0) @@ -430,11 +374,11 @@ Zstring createTemp8Dot3Name(const Zstring& fileName) //find a unique 8.3 short n for (int index = 0; index < 100000000; ++index) //filename must be representable by <= 8 characters { const Zstring output = pathPrefix + Zstring::fromNumber(index) + Zchar('.') + extension; - if (!zen::somethingExists(output)) //ensure uniqueness + if (!somethingExists(output)) //ensure uniqueness return output; } - throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...\n") + zen::utf8CvrtTo<std::string>(pathPrefix)); + throw std::runtime_error(std::string("100000000 files, one for each number, exist in this directory? You're kidding...\n") + utf8CvrtTo<std::string>(pathPrefix)); } @@ -446,7 +390,7 @@ bool fix8Dot3NameClash(const Zstring& oldName, const Zstring& newName) //throw if (newName.find(FILE_NAME_SEPARATOR) == Zstring::npos) return false; - if (zen::somethingExists(newName)) //name OR directory! + if (somethingExists(newName)) //name OR directory! { const Zstring fileNameOrig = newName.AfterLast(FILE_NAME_SEPARATOR); //returns the whole string if ch not found const Zstring fileNameShort = getFilenameFmt(newName, ::GetShortPathName).AfterLast(FILE_NAME_SEPARATOR); //throw() returns empty string on error @@ -467,7 +411,7 @@ bool fix8Dot3NameClash(const Zstring& oldName, const Zstring& newName) //throw //move already existing short name out of the way for now renameFileInternal(unrelatedPathLong, parkedTarget); //throw (FileError: ErrorDifferentVolume); - //DON'T call zen::renameFile() to avoid reentrance! + //DON'T call renameFile() to avoid reentrance! //schedule cleanup; the file system should assign this unrelated file a new (unique) short name Loki::ScopeGuard guard = Loki::MakeGuard(renameFileInternal, parkedTarget, unrelatedPathLong);//equivalent to Boost.ScopeExit in this case @@ -508,7 +452,7 @@ public: virtual void deleteTargetFile(const Zstring& targetFile) { assert(!fileExists(targetFile)); } - virtual void updateCopyStatus(zen::UInt64 totalBytesTransferred) + virtual void updateCopyStatus(UInt64 totalBytesTransferred) { moveCallback.requestUiRefresh(sourceFile_); } @@ -665,7 +609,7 @@ void moveDirectoryImpl(const Zstring& sourceDir, const Zstring& targetDir, bool //move files for (TraverseOneLevel::NameList::const_iterator i = fileList.begin(); i != fileList.end(); ++i) - zen::moveFile(i->second, targetDirFormatted + i->first, ignoreExisting, callback); //throw (FileError: ErrorTargetExisting); + moveFile(i->second, targetDirFormatted + i->first, ignoreExisting, callback); //throw (FileError: ErrorTargetExisting); //move directories for (TraverseOneLevel::NameList::const_iterator i = dirList.begin(); i != dirList.end(); ++i) @@ -753,7 +697,7 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) #elif defined FFS_LINUX if (::unlink(directory.c_str()) != 0) #endif - throw FileError(_("Error deleting directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error deleting directory:") + "\n\"" + directory + "\"" + "\n\n" + getLastErrorFormatted()); if (callback) callback->notifyDeletion(directory); //once per symlink return; @@ -764,7 +708,7 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) //get all files and directories from current directory (WITHOUT subdirectories!) FilesDirsOnlyTraverser traverser(fileList, dirList); - zen::traverseFolder(directory, false, traverser); //don't follow symlinks + traverseFolder(directory, false, traverser); //don't follow symlinks //delete files for (std::vector<Zstring>::const_iterator i = fileList.begin(); i != fileList.end(); ++i) @@ -784,7 +728,7 @@ void zen::removeDirectory(const Zstring& directory, CallbackRemoveDir* callback) if (::rmdir(directory.c_str()) != 0) #endif { - throw FileError(_("Error deleting directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error deleting directory:") + "\n\"" + directory + "\"" + "\n\n" + getLastErrorFormatted()); } if (callback) callback->notifyDeletion(directory); //and once per folder } @@ -801,7 +745,7 @@ void zen::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool if (!::GetFileAttributesEx(applyLongPathPrefix(sourceObj).c_str(), //__in LPCTSTR lpFileName, GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, &sourceAttr)) //__out LPVOID lpFileInformation - throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + getLastErrorFormatted()); const bool isReparsePoint = (sourceAttr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; const bool isDirectory = (sourceAttr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; @@ -816,7 +760,7 @@ void zen::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory; no FILE_FLAG_OPEN_REPARSE_POINT => deref symlinks NULL); if (hSource == INVALID_HANDLE_VALUE) - throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + getLastErrorFormatted()); Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hSource); (void)dummy; //silence warning "unused variable" @@ -825,7 +769,7 @@ void zen::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool &creationTime, //__out_opt LPFILETIME lpCreationTime, NULL, //__out_opt LPFILETIME lpLastAccessTime, &lastWriteTime)) //__out_opt LPFILETIME lpLastWriteTime - throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + getLastErrorFormatted()); } else { @@ -884,7 +828,7 @@ void zen::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool }); if (targetHandle.get() == INVALID_HANDLE_VALUE) - throw FileError(_("Error changing modification time:") + "\n\"" + targetObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + "\n\"" + targetObj + "\"" + "\n\n" + getLastErrorFormatted()); /* if (hTarget == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_SHARING_VIOLATION) @@ -898,10 +842,10 @@ void zen::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool &creationTime, NULL, &lastWriteTime)) - throw FileError(_("Error changing modification time:") + "\n\"" + targetObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + "\n\"" + targetObj + "\"" + "\n\n" + getLastErrorFormatted()); #ifndef NDEBUG //dst hack: verify data written - if (dst::isFatDrive(targetObj) && !zen::dirExists(targetObj)) //throw() + if (dst::isFatDrive(targetObj) && !dirExists(targetObj)) //throw() { WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {}; assert(::GetFileAttributesEx(applyLongPathPrefix(targetObj).c_str(), //__in LPCTSTR lpFileName, @@ -918,7 +862,7 @@ void zen::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool { struct stat objInfo = {}; if (::stat(sourceObj.c_str(), &objInfo) != 0) //read file attributes from source directory - throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + getLastErrorFormatted()); struct utimbuf newTimes = {}; newTimes.actime = objInfo.st_atime; @@ -926,13 +870,13 @@ void zen::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool //(try to) set new "last write time" if (::utime(targetObj.c_str(), &newTimes) != 0) //return value not evaluated! - throw FileError(_("Error changing modification time:") + "\n\"" + targetObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + "\n\"" + targetObj + "\"" + "\n\n" + getLastErrorFormatted()); } else { struct stat objInfo = {}; if (::lstat(sourceObj.c_str(), &objInfo) != 0) //read file attributes from source directory - throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading file attributes:") + "\n\"" + sourceObj + "\"" + "\n\n" + getLastErrorFormatted()); struct timeval newTimes[2] = {}; newTimes[0].tv_sec = objInfo.st_atime; /* seconds */ @@ -942,7 +886,7 @@ void zen::copyFileTimes(const Zstring& sourceObj, const Zstring& targetObj, bool newTimes[1].tv_usec = 0; /* microseconds */ if (::lutimes(targetObj.c_str(), newTimes) != 0) //return value not evaluated! - throw FileError(_("Error changing modification time:") + "\n\"" + targetObj + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error changing modification time:") + "\n\"" + targetObj + "\"" + "\n\n" + getLastErrorFormatted()); } #endif } @@ -954,7 +898,7 @@ namespace Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target path of symbolic link to a directory; throw (FileError) { //open handle to target of symbolic link - const HANDLE hDir = ::CreateFile(zen::applyLongPathPrefix(dirLinkName).c_str(), + const HANDLE hDir = ::CreateFile(applyLongPathPrefix(dirLinkName).c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, @@ -962,7 +906,7 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa FILE_FLAG_BACKUP_SEMANTICS, //needed to open a directory NULL); if (hDir == INVALID_HANDLE_VALUE) - throw FileError(_("Error resolving symbolic link:") + "\n\"" + dirLinkName + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error resolving symbolic link:") + "\n\"" + dirLinkName + "\"" + "\n\n" + getLastErrorFormatted()); Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hDir); (void)dummy; //silence warning "unused variable" @@ -976,7 +920,7 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa LPTSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags); - static const GetFinalPathNameByHandleWFunc getFinalPathNameByHandle = + const GetFinalPathNameByHandleWFunc getFinalPathNameByHandle = util::getDllFun<GetFinalPathNameByHandleWFunc>(L"kernel32.dll", "GetFinalPathNameByHandleW"); if (getFinalPathNameByHandle == NULL) @@ -991,7 +935,7 @@ Zstring resolveDirectorySymlink(const Zstring& dirLinkName) //get full target pa { std::wstring errorMessage = _("Error resolving symbolic link:") + "\n\"" + dirLinkName + "\""; if (rv == 0) - errorMessage += L"\n\n" + zen::getLastErrorFormatted(); + errorMessage += L"\n\n" + getLastErrorFormatted(); throw FileError(errorMessage); } @@ -1014,7 +958,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, bool dere errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem return; - throw FileError(_("Error reading security context:") + "\n\"" + source + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error reading security context:") + "\n\"" + source + "\"" + "\n\n" + getLastErrorFormatted()); } Loki::ScopeGuard dummy1 = Loki::MakeGuard(::freecon, contextSource); (void)dummy1; //silence warning "unused variable" @@ -1044,7 +988,7 @@ void copySecurityContext(const Zstring& source, const Zstring& target, bool dere ::setfilecon (target.c_str(), contextSource) : ::lsetfilecon(target.c_str(), contextSource); if (rv3 < 0) - throw FileError(_("Error writing security context:") + "\n\"" + target + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error writing security context:") + "\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted()); } #endif //HAVE_SELINUX @@ -1077,7 +1021,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, bool de PACL sacl = NULL; //http://msdn.microsoft.com/en-us/library/aa364399(v=VS.85).aspx - const HANDLE hSource = ::CreateFile(zen::applyLongPathPrefix(source).c_str(), + const HANDLE hSource = ::CreateFile(applyLongPathPrefix(source).c_str(), READ_CONTROL | ACCESS_SYSTEM_SECURITY, //ACCESS_SYSTEM_SECURITY required for SACL access FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, @@ -1085,7 +1029,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, bool de FILE_FLAG_BACKUP_SEMANTICS | (derefSymlinks ? 0 : FILE_FLAG_OPEN_REPARSE_POINT), //FILE_FLAG_BACKUP_SEMANTICS needed to open a directory NULL); if (hSource == INVALID_HANDLE_VALUE) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + zen::getLastErrorFormatted() + " (OR)"); + throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted() + " (OR)"); Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hSource); (void)dummy; //silence warning "unused variable" @@ -1100,7 +1044,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, bool de &sacl, //__out_opt PACL *ppSacl, &buffer); //__out_opt PSECURITY_DESCRIPTOR *ppSecurityDescriptor if (rc != ERROR_SUCCESS) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + zen::getLastErrorFormatted(rc) + " (R)"); + throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted(rc) + " (R)"); Loki::ScopeGuard dummy4 = Loki::MakeGuard(::LocalFree, buffer); (void)dummy4; //silence warning "unused variable" @@ -1119,7 +1063,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, bool de }); if (targetHandle.get() == INVALID_HANDLE_VALUE) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + zen::getLastErrorFormatted() + " (OW)"); + throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted() + " (OW)"); // rc = ::SetNamedSecurityInfo(const_cast<WCHAR*>(applyLongPathPrefix(target).c_str()), //__in LPTSTR pObjectName, -> does NOT dereference symlinks! rc = ::SetSecurityInfo( @@ -1132,7 +1076,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, bool de sacl); //__in_opt PACL pSacl if (rc != ERROR_SUCCESS) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + zen::getLastErrorFormatted(rc) + " (W)"); + throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted(rc) + " (W)"); #elif defined FFS_LINUX @@ -1146,7 +1090,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, bool de if (::stat (source.c_str(), &fileInfo) != 0 || ::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights! ::chmod(target.c_str(), fileInfo.st_mode) != 0) - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + zen::getLastErrorFormatted() + " (R)"); + throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted() + " (R)"); } else { @@ -1154,7 +1098,7 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, bool de if (::lstat (source.c_str(), &fileInfo) != 0 || ::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0 || // may require admin rights! (!symlinkExists(target) && ::chmod(target.c_str(), fileInfo.st_mode) != 0)) //setting access permissions doesn't make sense for symlinks on Linux: there is no lchmod() - throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + zen::getLastErrorFormatted() + " (W)"); + throw FileError(_("Error copying file permissions:") + "\n\"" + source + "\" ->\n\"" + target + "\"" + "\n\n" + getLastErrorFormatted() + " (W)"); } #endif } @@ -1163,8 +1107,8 @@ void copyObjectPermissions(const Zstring& source, const Zstring& target, bool de namespace { //provide uniform binary interface: -void removeDirSimple(const Zstring& directory) { zen::removeDirectory(directory); } //throw (FileError) -void removeFileSimple(const Zstring& filename) { zen::removeFile(filename); } //throw (FileError) +void removeDirSimple(const Zstring& directory) { removeDirectory(directory); } //throw (FileError) +void removeFileSimple(const Zstring& filename) { removeFile(filename); } //throw (FileError) } @@ -1172,7 +1116,7 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat { using namespace zen; - if (zen::dirExists(directory)) + if (dirExists(directory)) return; if (level == 100) //catch endless recursion @@ -1180,7 +1124,7 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat //try to create parent folders first const Zstring dirParent = directory.BeforeLast(FILE_NAME_SEPARATOR); - if (!dirParent.empty() && !zen::dirExists(dirParent)) + if (!dirParent.empty() && !dirExists(dirParent)) { //call function recursively const Zstring templateParent = templateDir.BeforeLast(FILE_NAME_SEPARATOR); @@ -1201,7 +1145,7 @@ void createDirectoryRecursively(const Zstring& directory, const Zstring& templat #endif { if (level != 0) return; - throw FileError(_("Error creating directory:") + "\n\"" + directory + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error creating directory:") + "\n\"" + directory + "\"" + "\n\n" + getLastErrorFormatted()); } if (!templateDir.empty()) @@ -1314,7 +1258,7 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, zen: #ifdef FFS_WIN //dynamically load windows API function typedef BOOLEAN (WINAPI *CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags); - static const CreateSymbolicLinkFunc createSymbolicLink = util::getDllFun<CreateSymbolicLinkFunc>(L"kernel32.dll", "CreateSymbolicLinkW"); + const CreateSymbolicLinkFunc createSymbolicLink = util::getDllFun<CreateSymbolicLinkFunc>(L"kernel32.dll", "CreateSymbolicLinkW"); if (createSymbolicLink == NULL) throw FileError(_("Error loading library function:") + "\n\"" + "CreateSymbolicLinkW" + "\""); @@ -1322,11 +1266,11 @@ void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, zen: targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, linkPath.c_str(), //__in LPTSTR lpTargetFileName, (type == SYMLINK_TYPE_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0))) //__in DWORD dwFlags - throw FileError(_("Error copying symbolic link:") + "\n\"" + sourceLink + "\" ->\n\"" + targetLink + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error copying symbolic link:") + "\n\"" + sourceLink + "\" ->\n\"" + targetLink + "\"" + "\n\n" + getLastErrorFormatted()); #elif defined FFS_LINUX if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0) - throw FileError(_("Error copying symbolic link:") + "\n\"" + sourceLink + "\" ->\n\"" + targetLink + "\"" + "\n\n" + zen::getLastErrorFormatted()); + throw FileError(_("Error copying symbolic link:") + "\n\"" + sourceLink + "\" ->\n\"" + targetLink + "\"" + "\n\n" + getLastErrorFormatted()); #endif //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist @@ -1351,7 +1295,7 @@ Zstring createTempName(const Zstring& filename) Zstring output = filename + zen::TEMP_FILE_ENDING; //ensure uniqueness - for (int i = 1; zen::somethingExists(output); ++i) + for (int i = 1; somethingExists(output); ++i) output = filename + Zchar('_') + Zstring::fromNumber(i) + zen::TEMP_FILE_ENDING; return output; @@ -1362,11 +1306,13 @@ struct CallbackData { CallbackData(CallbackCopyFile& cb) : callback(cb), + callNr(0), exceptionCaught(false) {} CallbackCopyFile& callback; + size_t callNr; bool exceptionCaught; - zen::UInt64 bytesTransferredOnException; + UInt64 bytesTransferredOnException; }; @@ -1381,14 +1327,13 @@ DWORD CALLBACK copyCallbackInternal( HANDLE hDestinationFile, LPVOID lpData) { - //small performance optimization: it seems this callback function is called for every 64 kB (depending on cluster size). - static size_t callNr = 0; - if (++callNr % 4 == 0) //executing callback every 256 kB should suffice + if (lpData) { - if (lpData) - { - CallbackData& cbd = *static_cast<CallbackData*>(lpData); + CallbackData& cbd = *static_cast<CallbackData*>(lpData); + //small performance optimization: it seems this callback function is called for every 64 kB (depending on cluster size). + if (++cbd.callNr % 4 == 0) //executing callback every 256 kB should suffice + { //some odd check for some possible(?) error condition if (totalBytesTransferred.QuadPart < 0) //let's see if someone answers the call... ::MessageBox(NULL, L"You've just discovered a bug in WIN32 API function \"CopyFileEx\"! \n\n\ @@ -1398,12 +1343,15 @@ DWORD CALLBACK copyCallbackInternal( NULL, 0); try { - cbd.callback.updateCopyStatus(zen::UInt64(totalBytesTransferred.QuadPart)); + cbd.callback.updateCopyStatus(UInt64(totalBytesTransferred.QuadPart)); } catch (...) { +#ifndef _MSC_VER +#warning migrate to std::exception_ptr when available +#endif cbd.exceptionCaught = true; - cbd.bytesTransferredOnException = zen::UInt64(totalBytesTransferred.QuadPart); + cbd.bytesTransferredOnException = UInt64(totalBytesTransferred.QuadPart); return PROGRESS_CANCEL; } } @@ -1413,23 +1361,6 @@ DWORD CALLBACK copyCallbackInternal( } -#ifndef COPY_FILE_COPY_SYMLINK -#define COPY_FILE_COPY_SYMLINK 0x00000800 -#endif - -bool supportForSymbolicLinks() -{ - OSVERSIONINFO osvi = {}; - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - //symbolic links are supported starting with Vista - if (::GetVersionEx(&osvi)) - return osvi.dwMajorVersion > 5; //XP has majorVersion == 5, minorVersion == 1; Vista majorVersion == 6, dwMinorVersion == 0 - //overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx - return false; -} - - #ifndef COPY_FILE_ALLOW_DECRYPTED_DESTINATION #define COPY_FILE_ALLOW_DECRYPTED_DESTINATION 0x00000008 #endif @@ -1456,12 +1387,12 @@ void rawCopyWinApi(const Zstring& sourceFile, DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS; - // static const bool symlinksSupported = supportForSymbolicLinks(); //only set "true" if supported by OS: else copying in Windows XP fails - // if (copyFileSymLinks && symlinksSupported) - // copyFlags |= COPY_FILE_COPY_SYMLINK; - //allow copying from encrypted to non-encrytped location - static const bool nonEncSupported = supportForNonEncryptedDestination(); + + static bool nonEncSupported = false; + static boost::once_flag once = BOOST_ONCE_INIT; //caveat: function scope static initialization is not thread-safe in VS 2010! + boost::call_once(once, []() { nonEncSupported = supportForNonEncryptedDestination(); }); + if (nonEncSupported) copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; @@ -1484,7 +1415,7 @@ void rawCopyWinApi(const Zstring& sourceFile, //assemble error message... std::wstring errorMessage = _("Error copying file:") + "\n\"" + sourceFile + "\" ->\n\"" + targetFile + "\"" + - "\n\n" + zen::getLastErrorFormatted(lastError); + "\n\n" + getLastErrorFormatted(lastError); //if file is locked (try to) use Windows Volume Shadow Copy Service if (lastError == ERROR_SHARING_VIOLATION || @@ -1508,7 +1439,7 @@ void rawCopyWinApi(const Zstring& sourceFile, //trying to copy > 4GB file to FAT/FAT32 volume gives obscure ERROR_INVALID_PARAMETER (FAT can indeed handle files up to 4 Gig, tested!) if (lastError == ERROR_INVALID_PARAMETER && dst::isFatDrive(targetFile) && - getFilesize(sourceFile) >= 4U * zen::UInt64(1024U * 1024 * 1024)) //throw (FileError) + getFilesize(sourceFile) >= 4U * UInt64(1024U * 1024 * 1024)) //throw (FileError) errorMessage += L"\nFAT volume cannot store files larger than 4 gigabyte!"; } catch(...) {} @@ -1555,7 +1486,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // */ // // //open sourceFile for reading -// HANDLE hFileIn = ::CreateFile(zen::applyLongPathPrefix(sourceFile).c_str(), +// HANDLE hFileIn = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), // GENERIC_READ, // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //all shared modes are required to read files that are open in other applications // NULL, @@ -1565,7 +1496,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // if (hFileIn == INVALID_HANDLE_VALUE) // { // const DWORD lastError = ::GetLastError(); -// const std::wstring& errorMessage = _("Error opening file:") + "\n\"" + sourceFile + "\"" + "\n\n" + zen::getLastErrorFormatted(lastError); +// const std::wstring& errorMessage = _("Error opening file:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted(lastError); // // //if file is locked (try to) use Windows Volume Shadow Copy Service // if (lastError == ERROR_SHARING_VIOLATION || @@ -1580,7 +1511,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // // BY_HANDLE_FILE_INFORMATION infoFileIn = {}; // if (!::GetFileInformationByHandle(hFileIn, &infoFileIn)) -// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + zen::getLastErrorFormatted()); +// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); // // //####################################### DST hack ########################################### // if (dst::isFatDrive(sourceFile)) //throw() @@ -1609,7 +1540,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // FILE_ATTRIBUTE_ENCRYPTED; // // //create targetFile and open it for writing -// HANDLE hFileOut = ::CreateFile(zen::applyLongPathPrefix(targetFile).c_str(), +// HANDLE hFileOut = ::CreateFile(applyLongPathPrefix(targetFile).c_str(), // GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION // FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // NULL, @@ -1620,7 +1551,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // { // const DWORD lastError = ::GetLastError(); // const std::wstring& errorMessage = _("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + zen::getLastErrorFormatted(lastError); +// "\n\n" + getLastErrorFormatted(lastError); // // if (lastError == ERROR_FILE_EXISTS) // throw ErrorTargetExisting(errorMessage); @@ -1648,7 +1579,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, // NULL, //__out LPTSTR lpFileSystemNameBuffer, // 0)) //__in DWORD nFileSystemNameSize -// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + zen::getLastErrorFormatted()); +// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); // // const bool sourceIsEncrypted = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; // const bool sourceIsCompressed = (infoFileIn.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; @@ -1675,7 +1606,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // &bytesReturned, //number of bytes returned // NULL)) //OVERLAPPED structure // throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + zen::getLastErrorFormatted() + +// "\n\n" + getLastErrorFormatted() + // "\nFailed to write NTFS compressed attribute!"); // } // @@ -1694,14 +1625,17 @@ void rawCopyWinApi(const Zstring& sourceFile, // &bytesReturned, //number of bytes returned // NULL)) //OVERLAPPED structure // throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + zen::getLastErrorFormatted() + +// "\n\n" + getLastErrorFormatted() + // "\nFailed to write NTFS sparse attribute!"); // } // } // // // const DWORD BUFFER_SIZE = 512 * 1024; //512 kb seems to be a reasonable buffer size -// static const boost::scoped_array<BYTE> memory(new BYTE[BUFFER_SIZE]); +// static boost::thread_specific_ptr<std::vector<char>> cpyBuf; +// if (!cpyBuf.get()) +// cpyBuf.reset(new std::vector<char>(BUFFER_SIZE)); //512 kb seems to be a reasonable buffer size +// std::vector<char>& buffer = *cpyBuf; // // struct ManageCtxt //manage context for BackupRead()/BackupWrite() // { @@ -1719,7 +1653,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // } context; // // //copy contents of sourceFile to targetFile -// zen::UInt64 totalBytesTransferred; +// UInt64 totalBytesTransferred; // // bool eof = false; // do @@ -1729,22 +1663,22 @@ void rawCopyWinApi(const Zstring& sourceFile, // if (useBackupFun) // { // if (!::BackupRead(hFileIn, //__in HANDLE hFile, -// memory.get(), //__out LPBYTE lpBuffer, +// &buffer[0], //__out LPBYTE lpBuffer, // BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, // &bytesRead, //__out LPDWORD lpNumberOfBytesRead, // false, //__in BOOL bAbort, // false, //__in BOOL bProcessSecurity, // &context.read)) //__out LPVOID *lpContext // throw FileError(_("Error reading file:") + "\n\"" + sourceFile + "\"" + -// "\n\n" + zen::getLastErrorFormatted()); +// "\n\n" + getLastErrorFormatted()); // } // else if (!::ReadFile(hFileIn, //__in HANDLE hFile, -// memory.get(), //__out LPVOID lpBuffer, +// &buffer[0], //__out LPVOID lpBuffer, // BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, // &bytesRead, //__out_opt LPDWORD lpNumberOfBytesRead, // NULL)) //__inout_opt LPOVERLAPPED lpOverlapped // throw FileError(_("Error reading file:") + "\n\"" + sourceFile + "\"" + -// "\n\n" + zen::getLastErrorFormatted()); +// "\n\n" + getLastErrorFormatted()); // // if (bytesRead > BUFFER_SIZE) // throw FileError(_("Error reading file:") + "\n\"" + sourceFile + "\"" + @@ -1758,22 +1692,22 @@ void rawCopyWinApi(const Zstring& sourceFile, // if (useBackupFun) // { // if (!::BackupWrite(hFileOut, //__in HANDLE hFile, -// memory.get(), //__in LPBYTE lpBuffer, +// &buffer[0], //__in LPBYTE lpBuffer, // bytesRead, //__in DWORD nNumberOfBytesToWrite, // &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten, // false, //__in BOOL bAbort, // false, //__in BOOL bProcessSecurity, // &context.write)) //__out LPVOID *lpContext // throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + zen::getLastErrorFormatted() + " (w)"); //w -> distinguish from fopen error message! +// "\n\n" + getLastErrorFormatted() + " (w)"); //w -> distinguish from fopen error message! // } // else if (!::WriteFile(hFileOut, //__in HANDLE hFile, -// memory.get(), //__out LPVOID lpBuffer, +// &buffer[0], //__out LPVOID lpBuffer, // bytesRead, //__in DWORD nNumberOfBytesToWrite, // &bytesWritten, //__out_opt LPDWORD lpNumberOfBytesWritten, // NULL)) //__inout_opt LPOVERLAPPED lpOverlapped // throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + -// "\n\n" + zen::getLastErrorFormatted() + " (w)"); //w -> distinguish from fopen error message! +// "\n\n" + getLastErrorFormatted() + " (w)"); //w -> distinguish from fopen error message! // // if (bytesWritten != bytesRead) // throw FileError(_("Error writing file:") + "\n\"" + targetFile + "\"" + "\n\n" + "incomplete write"); @@ -1803,7 +1737,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // { // LARGE_INTEGER inputSize = {}; // if (!::GetFileSizeEx(hFileIn, &inputSize)) -// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + zen::getLastErrorFormatted()); +// throw FileError(_("Error reading file attributes:") + "\n\"" + sourceFile + "\"" + "\n\n" + getLastErrorFormatted()); // // if (inputSize.QuadPart != 0) // throw FileError(_("Error reading file:") + "\n\"" + sourceFile + "\"" + "\n\n" + "unknown error"); @@ -1814,7 +1748,7 @@ void rawCopyWinApi(const Zstring& sourceFile, // &infoFileIn.ftCreationTime, // NULL, // &infoFileIn.ftLastWriteTime)) -// throw FileError(_("Error changing modification time:") + "\n\"" + targetFile + "\"" + "\n\n" + zen::getLastErrorFormatted()); +// throw FileError(_("Error changing modification time:") + "\n\"" + targetFile + "\"" + "\n\n" + getLastErrorFormatted()); // // //#ifndef NDEBUG //dst hack: verify data written @@ -1878,10 +1812,13 @@ void rawCopyStream(const Zstring& sourceFile, //create targetFile and open it for writing FileOutput fileOut(targetFile, FileOutput::ACC_CREATE_NEW); //throw (FileError: ErrorTargetPathMissing, ErrorTargetExisting) - static std::vector<wchar_t> buffer(512 * 1024); //512 kb seems to be a reasonable buffer size + static boost::thread_specific_ptr<std::vector<char>> cpyBuf; + if (!cpyBuf.get()) + cpyBuf.reset(new std::vector<char>(512 * 1024)); //512 kb seems to be a reasonable buffer size + std::vector<char>& buffer = *cpyBuf; //copy contents of sourceFile to targetFile - zen::UInt64 totalBytesTransferred; + UInt64 totalBytesTransferred; do { const size_t bytesRead = fileIn.read(&buffer[0], buffer.size()); //throw (FileError) @@ -1944,7 +1881,7 @@ void zen::copyFile(const Zstring& sourceFile, //throw (FileError: ErrorTargetPat CallbackCopyFile* callback) { Zstring temporary = targetFile + zen::TEMP_FILE_ENDING; //use temporary file until a correct date has been set - Loki::ScopeGuard guardTempFile = Loki::MakeGuard(&removeFile, boost::cref(temporary)); //transactional behavior: ensure cleanup (e.g. network drop) -> cref [!] + Loki::ScopeGuard guardTempFile = Loki::MakeGuard([&]() { removeFile(temporary); }); //transactional behavior: ensure cleanup (e.g. network drop) -> ref to temporary[!] //raw file copy try diff --git a/shared/file_handling.h b/shared/file_handling.h index 0ee4b1e9..113168f3 100644 --- a/shared/file_handling.h +++ b/shared/file_handling.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FILE_HANDLING_H_INCLUDED #define FILE_HANDLING_H_INCLUDED @@ -25,13 +25,15 @@ bool somethingExists(const Zstring& objname); //throw() check whether any //check whether two folders are located on the same (logical) volume //left and right directories NEED NOT yet exist! volume prefix is sufficient! path may end with PATH_SEPARATOR -enum ResponseSameVol +enum ResponseSame { - VOLUME_SAME, - VOLUME_DIFFERENT, - VOLUME_CANT_SAY + IS_SAME_YES, + IS_SAME_NO, + IS_SAME_CANT_SAY }; -ResponseSameVol onSameVolume(const Zstring& folderLeft, const Zstring& folderRight); //throw() +ResponseSame onSameVolume(const Zstring& folderLeft, const Zstring& folderRight); //throw() + +void checkthis(const Zstring& folderLeft); //throw() //copy file or directory create/last change date, void copyFileTimes(const Zstring& sourceDir, const Zstring& targetDir, bool derefSymlinks); //throw (FileError) diff --git a/shared/file_id.cpp b/shared/file_id.cpp index da8fd815..e6c016e7 100644 --- a/shared/file_id.cpp +++ b/shared/file_id.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "file_id.h" #ifdef FFS_WIN @@ -18,8 +18,7 @@ namespace { -template <class T> -inline +template <class T> inline std::string numberToBytes(T number) { const char* rawBegin = reinterpret_cast<const char*>(&number); diff --git a/shared/file_id.h b/shared/file_id.h index 2e94bf5a..eaf47e3c 100644 --- a/shared/file_id.h +++ b/shared/file_id.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FILEID_H_INCLUDED #define FILEID_H_INCLUDED diff --git a/shared/file_io.cpp b/shared/file_io.cpp index c19101aa..2614ac06 100644 --- a/shared/file_io.cpp +++ b/shared/file_io.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "file_io.h" #include "last_error.h" #include "i18n.h" diff --git a/shared/file_io.h b/shared/file_io.h index 79b521f1..6f87845b 100644 --- a/shared/file_io.h +++ b/shared/file_io.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FILEIO_H_INCLUDED #define FILEIO_H_INCLUDED diff --git a/shared/file_traverser.cpp b/shared/file_traverser.cpp index aced3487..595b2768 100644 --- a/shared/file_traverser.cpp +++ b/shared/file_traverser.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "file_traverser.h" #include <limits> #include "last_error.h" @@ -14,6 +14,7 @@ #include <wx/msw/wrapwin.h> //includes "windows.h" #include "long_path_prefix.h" #include "dst_hack.h" +#include "file_update_handle.h" #elif defined FFS_LINUX #include <sys/stat.h> @@ -57,7 +58,7 @@ inline zen::Int64 filetimeToTimeT(const FILETIME& lastWriteTime) { //convert UTC FILETIME to ANSI C format (number of seconds since Jan. 1st 1970 UTC) - zen::Int64 writeTimeLong = zen::to<zen::Int64>(zen::UInt64(lastWriteTime.dwLowDateTime, lastWriteTime.dwHighDateTime) / 10000000U); //reduce precision to 1 second (FILETIME has unit 10^-7 s) + zen::Int64 writeTimeLong = to<zen::Int64>(zen::UInt64(lastWriteTime.dwLowDateTime, lastWriteTime.dwHighDateTime) / 10000000U); //reduce precision to 1 second (FILETIME has unit 10^-7 s) writeTimeLong -= zen::Int64(3054539008UL, 2); //timeshift between ansi C time and FILETIME in seconds == 11644473600s return writeTimeLong; } @@ -126,14 +127,12 @@ public: } private: - DirTraverser(const DirTraverser&); - DirTraverser& operator=(const DirTraverser&); + DirTraverser(const DirTraverser&); + DirTraverser& operator=(const DirTraverser&); template <bool followSymlinks> void traverse(const Zstring& directory, zen::TraverseCallback& sink, int level) { - using namespace zen; - tryReportingError([&](std::wstring& errorMsg) -> bool { if (level == 100) //notify endless recursion @@ -146,19 +145,16 @@ private: #ifdef FFS_WIN - //ensure directoryFormatted ends with backslash - const Zstring& directoryFormatted = directory.EndsWith(FILE_NAME_SEPARATOR) ? - directory : - directory + FILE_NAME_SEPARATOR; - + //ensure directoryPf ends with backslash + const Zstring& directoryPf = directory.EndsWith(FILE_NAME_SEPARATOR) ? + directory : + directory + FILE_NAME_SEPARATOR; WIN32_FIND_DATA fileInfo = {}; HANDLE searchHandle = INVALID_HANDLE_VALUE; tryReportingError([&](std::wstring& errorMsg) -> bool { - searchHandle = ::FindFirstFile( - applyLongPathPrefix(directoryFormatted + Zchar('*')).c_str(), //__in LPCTSTR lpFileName - &fileInfo); //__out LPWIN32_FIND_DATA lpFindFileData + searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &fileInfo); //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH if (searchHandle == INVALID_HANDLE_VALUE) @@ -188,7 +184,7 @@ private: (shortName[1] == L'\0' || (shortName[1] == L'.' && shortName[2] == L'\0'))) continue; - const Zstring& fullName = directoryFormatted + shortName; + const Zstring& fullName = directoryPf + shortName; const bool isSymbolicLink = (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0; @@ -254,7 +250,7 @@ private: details.fileSize = zen::UInt64(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); } - sink.onFile(shortName, fullName, details); + sink.onFile(shortName, fullName, details); } } while ([&]() -> bool @@ -322,7 +318,7 @@ private: //don't return "." and ".." - const Zchar* const shortName = dirEntry->d_name; + const char* const shortName = dirEntry->d_name; if (shortName[0] == '.' && (shortName[1] == '\0' || (shortName[1] == '.' && shortName[2] == '\0'))) continue; @@ -421,23 +417,25 @@ private: const dst::RawTime encodedTime = dst::fatEncodeUtcTime(i->second); //throw (std::runtime_error) { - HANDLE hTarget = ::CreateFile(zen::applyLongPathPrefix(i->first).c_str(), - GENERIC_WRITE, //just FILE_WRITE_ATTRIBUTES may not be enough for some NAS shares! - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - NULL); - if (hTarget == INVALID_HANDLE_VALUE) + //may need to remove the readonly-attribute (e.g. FAT usb drives) + FileUpdateHandle updateHandle(i->first, [=]() + { + return ::CreateFile(zen::applyLongPathPrefix(i->first).c_str(), + GENERIC_WRITE, //just FILE_WRITE_ATTRIBUTES may not be enough for some NAS shares! + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + }); + if (updateHandle.get() == INVALID_HANDLE_VALUE) { ++failedAttempts; assert(false); //don't throw exceptions due to dst hack here continue; } - Loki::ScopeGuard dummy = Loki::MakeGuard(::CloseHandle, hTarget); - (void)dummy; //silence warning "unused variable" - if (!::SetFileTime(hTarget, + if (!::SetFileTime(updateHandle.get(), &encodedTime.createTimeRaw, NULL, &encodedTime.writeTimeRaw)) @@ -483,7 +481,7 @@ void zen::traverseFolder(const Zstring& directory, bool followSymlinks, Traverse #ifdef FFS_WIN try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail) { - zen::Privileges::getInstance().ensureActive(SE_BACKUP_NAME); //throw (FileError) + zen::Privileges::getInstance().ensureActive(SE_BACKUP_NAME); //throw FileError } catch (...) {} //don't cause issues in user mode #endif diff --git a/shared/file_traverser.h b/shared/file_traverser.h index 18d692df..aa9dffad 100644 --- a/shared/file_traverser.h +++ b/shared/file_traverser.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FILETRAVERSER_H_INCLUDED #define FILETRAVERSER_H_INCLUDED diff --git a/shared/file_update_handle.h b/shared/file_update_handle.h new file mode 100644 index 00000000..1ffb7000 --- /dev/null +++ b/shared/file_update_handle.h @@ -0,0 +1,67 @@ +#ifndef FILE_UPDATE_HANDLE_H_INCLUDED +#define FILE_UPDATE_HANDLE_H_INCLUDED + +#include <wx/msw/wrapwin.h> //includes "windows.h" +#include "long_path_prefix.h" + +namespace +{ +//manage file handle to update existing files (temporarily resetting read-only if necessary) +//CreateFileCmd: lambda directly returning non-owned file handle from ::CreateFile() +class FileUpdateHandle +{ +public: + template <class CreateFileCmd> + FileUpdateHandle(const Zstring& filename, CreateFileCmd cmd) : + filenameFmt(zen::applyLongPathPrefix(filename)), + hFile(INVALID_HANDLE_VALUE), + attr(INVALID_FILE_ATTRIBUTES) + { + hFile = cmd(); + if (hFile != INVALID_HANDLE_VALUE) + return; + + const DWORD lastError = ::GetLastError(); + if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only + { + Loki::ScopeGuard guardErrorCode = Loki::MakeGuard(::SetLastError, lastError); //transactional behavior: ensure cleanup (e.g. network drop) -> cref [!] + + //read-only file attribute may cause trouble: temporarily reset it + const DWORD tmpAttr = ::GetFileAttributes(filenameFmt.c_str()); + if (tmpAttr != INVALID_FILE_ATTRIBUTES && (tmpAttr & FILE_ATTRIBUTE_READONLY)) + { + if (::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL)) + { + guardErrorCode.Dismiss(); + attr = tmpAttr; //"create" guard on read-only attribute + + //now try again + hFile = cmd(); + } + } + } + } + + ~FileUpdateHandle() + { + if (hFile != INVALID_HANDLE_VALUE) + ::CloseHandle(hFile); + + if (attr != INVALID_FILE_ATTRIBUTES) + ::SetFileAttributes(filenameFmt.c_str(), attr); + } + + //may return INVALID_FILE_ATTRIBUTES, in which case ::GetLastError() may be called directly after FileUpdateHandle() + HANDLE get() const { return hFile; } + +private: + FileUpdateHandle(const FileUpdateHandle&); + FileUpdateHandle& operator=(const FileUpdateHandle&); + + Zstring filenameFmt; + HANDLE hFile; + DWORD attr; +}; +} + +#endif // FILE_UPDATE_HANDLE_H_INCLUDED diff --git a/shared/global_func.h b/shared/global_func.h index bf9f13dd..861e2081 100644 --- a/shared/global_func.h +++ b/shared/global_func.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef GLOBALFUNCTIONS_H_INCLUDED #define GLOBALFUNCTIONS_H_INCLUDED diff --git a/shared/guid.cpp b/shared/guid.cpp index 1580a62d..1cc304cb 100644 --- a/shared/guid.cpp +++ b/shared/guid.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "guid.h" #include <boost/uuid/uuid.hpp> diff --git a/shared/guid.h b/shared/guid.h index 166e9eb1..1986b101 100644 --- a/shared/guid.h +++ b/shared/guid.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef GUID_H_INCLUDED #define GUID_H_INCLUDED diff --git a/shared/help_provider.cpp b/shared/help_provider.cpp index 2c5d5aa9..7eb043cd 100644 --- a/shared/help_provider.cpp +++ b/shared/help_provider.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "help_provider.h" #include <wx/help.h> #include "standard_paths.h" diff --git a/shared/help_provider.h b/shared/help_provider.h index 01ac3054..dbe3ce26 100644 --- a/shared/help_provider.h +++ b/shared/help_provider.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef HELPPROVIDER_H_INCLUDED #define HELPPROVIDER_H_INCLUDED diff --git a/shared/i18n.h b/shared/i18n.h index cac6555c..8aa38f3c 100644 --- a/shared/i18n.h +++ b/shared/i18n.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef I18_N_HEADER_3843489325045 #define I18_N_HEADER_3843489325045 diff --git a/shared/int64.h b/shared/int64.h index 15900fde..61ef1716 100644 --- a/shared/int64.h +++ b/shared/int64.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FFS_LARGE_64_BIT_INTEGER_H_INCLUDED #define FFS_LARGE_64_BIT_INTEGER_H_INCLUDED diff --git a/shared/last_error.cpp b/shared/last_error.cpp index ea8345bc..57053c30 100644 --- a/shared/last_error.cpp +++ b/shared/last_error.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "last_error.h" #include "string_utf8.h" #include "i18n.h" diff --git a/shared/last_error.h b/shared/last_error.h index b617ebf5..3e90a1a0 100644 --- a/shared/last_error.h +++ b/shared/last_error.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SYSTEMFUNCTIONS_H_INCLUDED #define SYSTEMFUNCTIONS_H_INCLUDED diff --git a/shared/localization.cpp b/shared/localization.cpp index 919009d6..66b2d97b 100644 --- a/shared/localization.cpp +++ b/shared/localization.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "localization.h" #include <fstream> #include <map> diff --git a/shared/localization.h b/shared/localization.h index 07ac2a69..438ad445 100644 --- a/shared/localization.h +++ b/shared/localization.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef MISC_H_INCLUDED #define MISC_H_INCLUDED diff --git a/shared/long_path_prefix.h b/shared/long_path_prefix.h index b6da9168..45fde0fa 100644 --- a/shared/long_path_prefix.h +++ b/shared/long_path_prefix.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef LONGPATHPREFIX_H_INCLUDED #define LONGPATHPREFIX_H_INCLUDED diff --git a/shared/mouse_move_dlg.cpp b/shared/mouse_move_dlg.cpp index 95074b55..7981df49 100644 --- a/shared/mouse_move_dlg.cpp +++ b/shared/mouse_move_dlg.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "mouse_move_dlg.h" #include <vector> #include <wx/msw/wrapwin.h> //includes "windows.h" diff --git a/shared/mouse_move_dlg.h b/shared/mouse_move_dlg.h index 2e23c089..4412529c 100644 --- a/shared/mouse_move_dlg.h +++ b/shared/mouse_move_dlg.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef MOUSEMOVEWINDOW_H_INCLUDED #define MOUSEMOVEWINDOW_H_INCLUDED diff --git a/shared/notify_removal.cpp b/shared/notify_removal.cpp index 819604ad..3a9239db 100644 --- a/shared/notify_removal.cpp +++ b/shared/notify_removal.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "notify_removal.h" #include <set> #include "last_error.h" @@ -183,8 +183,8 @@ public: } private: - Pimpl(Pimpl&); - Pimpl& operator=(Pimpl&); + Pimpl(Pimpl&); + Pimpl& operator=(Pimpl&); virtual void onMessage(UINT message, WPARAM wParam, LPARAM lParam) //throw()! { diff --git a/shared/notify_removal.h b/shared/notify_removal.h index abdf460b..f9d1cbaf 100644 --- a/shared/notify_removal.h +++ b/shared/notify_removal.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef NOTIFY_H_INCLUDED #define NOTIFY_H_INCLUDED diff --git a/shared/parse_lng.h b/shared/parse_lng.h index fcf20d28..b943d222 100644 --- a/shared/parse_lng.h +++ b/shared/parse_lng.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef PARSE_LNG_HEADER_INCLUDED #define PARSE_LNG_HEADER_INCLUDED diff --git a/shared/parse_plural.h b/shared/parse_plural.h index 3837feba..1139ffb6 100644 --- a/shared/parse_plural.h +++ b/shared/parse_plural.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef PARSE_PLURAL_H_INCLUDED #define PARSE_PLURAL_H_INCLUDED diff --git a/shared/parse_txt.h b/shared/parse_txt.h index 56147425..ca155dec 100644 --- a/shared/parse_txt.h +++ b/shared/parse_txt.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef PARSE_TXT_H_INCLUDED #define PARSE_TXT_H_INCLUDED diff --git a/shared/pch.h b/shared/pch.h index 06d6b417..aae74c08 100644 --- a/shared/pch.h +++ b/shared/pch.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FFS_PRECOMPILED_HEADER #define FFS_PRECOMPILED_HEADER diff --git a/shared/perf.h b/shared/perf.h index f62f9cc0..9bcf7882 100644 --- a/shared/perf.h +++ b/shared/perf.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef DEBUG_PERF_HEADER #define DEBUG_PERF_HEADER diff --git a/shared/recycler.cpp b/shared/recycler.cpp index cc216d4f..92b7b222 100644 --- a/shared/recycler.cpp +++ b/shared/recycler.cpp @@ -3,13 +3,15 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "recycler.h" #include <stdexcept> #include <iterator> #include "i18n.h" +#include "file_handling.h" #ifdef FFS_WIN +#include <boost/thread/once.hpp> #include "dll_loader.h" #include <wx/msw/wrapwin.h> //includes "windows.h" #include "build_info.h" @@ -45,7 +47,6 @@ std::wstring getRecyclerDllName() bool vistaOrLater() { OSVERSIONINFO osvi = {}; - ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); //IFileOperation is supported with Vista and later @@ -72,12 +73,12 @@ Nevertheless, let's use IFileOperation for better error reporting! void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw (FileError) { - using zen::FileError; - if (filesToDelete.empty()) return; - static const bool useIFileOperation = vistaOrLater(); + static bool useIFileOperation = false; + static boost::once_flag once = BOOST_ONCE_INIT; //caveat: function scope static initialization is not thread-safe in VS 2010! + boost::call_once(once, []() { useIFileOperation = vistaOrLater(); }); if (useIFileOperation) //new recycle bin usage: available since Vista { @@ -86,14 +87,8 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw ( std::back_inserter(fileNames), std::mem_fun_ref(&Zstring::c_str)); using namespace fileop; - - static MoveToRecycleBinFct moveToRecycler = NULL; - if (!moveToRecycler) - moveToRecycler = util::getDllFun<MoveToRecycleBinFct>(getRecyclerDllName(), moveToRecycleBinFctName); - - static GetLastErrorFct getLastError = NULL; - if (!getLastError) - getLastError = util::getDllFun<GetLastErrorFct>(getRecyclerDllName(), getLastErrorFctName); + MoveToRecycleBinFct moveToRecycler = util::getDllFun<MoveToRecycleBinFct>(getRecyclerDllName(), moveToRecycleBinFctName); + GetLastErrorFct getLastError = util::getDllFun<GetLastErrorFct> (getRecyclerDllName(), getLastErrorFctName); if (moveToRecycler == NULL || getLastError == NULL) throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + fileNames[0] + "\"" + //report first file only... better than nothing @@ -122,7 +117,7 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw ( //filenameDoubleNull += removeLongPathPrefix(*i); //::SHFileOperation() can't handle \\?\-prefix! //You should use fully-qualified path names with this function. Using it with relative path names is not thread safe. filenameDoubleNull += *i; //::SHFileOperation() can't handle \\?\-prefix! - filenameDoubleNull += Zchar(0); + filenameDoubleNull += L'\0'; } SHFILEOPSTRUCT fileOp = {}; @@ -137,7 +132,7 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw ( if (::SHFileOperation(&fileOp) != 0 || fileOp.fAnyOperationsAborted) { - throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + filenameDoubleNull.c_str() + "\""); //report first file only... better than nothing + throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + filenameDoubleNull + "\""); //report first file only... better than nothing } } } @@ -145,41 +140,48 @@ void moveToWindowsRecycler(const std::vector<Zstring>& filesToDelete) //throw ( } -bool zen::moveToRecycleBin(const Zstring& fileToDelete) //throw (FileError) +bool zen::moveToRecycleBin(const Zstring& filename) //throw (FileError) { -#ifdef FFS_WIN - const Zstring filenameFmt = applyLongPathPrefix(fileToDelete); - - const DWORD attr = ::GetFileAttributes(filenameFmt.c_str()); - if (attr == INVALID_FILE_ATTRIBUTES) + if (!somethingExists(filename)) return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! - //::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL); - //both SHFileOperation and useIFileOperation are not able to delete a folder named "System Volume Information" with normal attributes but shamelessly report success +#ifdef FFS_WIN + //::SetFileAttributes(applyLongPathPrefix(filename).c_str(), FILE_ATTRIBUTE_NORMAL); + //both SHFileOperation and IFileOperation are not able to delete a folder named "System Volume Information" with normal attributes but shamelessly report success std::vector<Zstring> fileNames; - fileNames.push_back(fileToDelete); + fileNames.push_back(filename); ::moveToWindowsRecycler(fileNames); //throw (FileError) #elif defined FFS_LINUX - struct stat fileInfo = {}; - if (::lstat(fileToDelete.c_str(), &fileInfo) != 0) - return false; //neither file nor any other object with that name existing: no error situation, manual deletion relies on it! - - Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(fileToDelete.c_str()); //never fails + Glib::RefPtr<Gio::File> fileObj = Gio::File::create_for_path(filename.c_str()); //never fails try { if (!fileObj->trash()) - throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + fileToDelete + "\"" + + throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + filename + "\"" + "\n\n" + "(unknown error)"); } catch (const Glib::Error& errorObj) { + //implement same behavior as in Windows: if recycler is not existing, delete permanently + if (errorObj.code() == G_IO_ERROR_NOT_SUPPORTED) + { + struct stat fileInfo = {}; + if (::lstat(filename.c_str(), &fileInfo) != 0) + return false; + + if (S_ISLNK(fileInfo.st_mode) || S_ISREG(fileInfo.st_mode)) + removeFile(filename); //throw FileError + else if (S_ISDIR(fileInfo.st_mode)) + removeDirectory(filename); //throw FileError + return true; + } + //assemble error message - const std::wstring errorMessage = L"Glib Error Code " + toString<std::wstring>(errorObj.code()) + ", " + - g_quark_to_string(errorObj.domain()) + ": " + errorObj.what(); + const std::wstring errorMessage = L"Glib Error Code " + toString<std::wstring>(errorObj.code()) + /* ", " + + g_quark_to_string(errorObj.domain()) + */ ": " + errorObj.what(); - throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + fileToDelete + "\"" + + throw FileError(_("Error moving to Recycle Bin:") + "\n\"" + filename + "\"" + "\n\n" + "(" + errorMessage + ")"); } #endif @@ -188,15 +190,15 @@ bool zen::moveToRecycleBin(const Zstring& fileToDelete) //throw (FileError) #ifdef FFS_WIN -zen::StatusRecycler zen::recycleBinExists(const Zstring& pathName) +zen::StatusRecycler zen::recycleBinStatus(const Zstring& pathName) { std::vector<wchar_t> buffer(MAX_PATH + 1); if (::GetVolumePathName(applyLongPathPrefix(pathName).c_str(), //__in LPCTSTR lpszFileName, &buffer[0], //__out LPTSTR lpszVolumePathName, static_cast<DWORD>(buffer.size()))) //__in DWORD cchBufferLength { - Zstring rootPath =&buffer[0]; - if (!rootPath.EndsWith(FILE_NAME_SEPARATOR)) //a trailing backslash is required + Zstring rootPath = &buffer[0]; + if (!endsWith(rootPath, FILE_NAME_SEPARATOR)) //a trailing backslash is required rootPath += FILE_NAME_SEPARATOR; SHQUERYRBINFO recInfo = {}; @@ -207,4 +209,22 @@ zen::StatusRecycler zen::recycleBinExists(const Zstring& pathName) } return STATUS_REC_UNKNOWN; } +#elif defined FFS_LINUX +/* +We really need access to a similar function to check whether a directory supports trashing and emit a warning if it does not! + +The following function looks perfect, alas it is restricted to local files and to the implementation of GIO only: + + gboolean _g_local_file_has_trash_dir(const char* dirname, dev_t dir_dev); + See: http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.h + + Just checking for "G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH" is not correct, since we find in + http://www.netmite.com/android/mydroid/2.0/external/bluetooth/glib/gio/glocalfileinfo.c + + g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH, + writable && parent_info->has_trash_dir); + + => We're NOT interested in whether the specified folder can be trashed, but whether it supports thrashing its child elements! (Only support, not actual write access!) + This renders G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH useless for this purpose. +*/ #endif diff --git a/shared/recycler.h b/shared/recycler.h index da6e3123..718b64cc 100644 --- a/shared/recycler.h +++ b/shared/recycler.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef RECYCLER_H_INCLUDED #define RECYCLER_H_INCLUDED @@ -23,15 +23,12 @@ Recycler always available: during runtime either SHFileOperation or (since Vista Linux ----- -Include compilation flag: -`pkg-config --cflags gtkmm-2.4` - -Linker flag: -`pkg-config --libs gtkmm-2.4` +Compiler flag: `pkg-config --cflags gtkmm-2.4` +Linker flag: `pkg-config --libs gtkmm-2.4` */ -//move a file or folder to Recycle Bin -bool moveToRecycleBin(const Zstring& fileToDelete); //return "true" if file/dir was actually deleted; throw (FileError) +//move a file or folder to Recycle Bin (deletes permanently if recycle is not available) +bool moveToRecycleBin(const Zstring& filename); //return "true" if file/dir was actually deleted; throw (FileError) #ifdef FFS_WIN @@ -42,7 +39,7 @@ enum StatusRecycler STATUS_REC_UNKNOWN }; -StatusRecycler recycleBinExists(const Zstring& pathName); //test existence of Recycle Bin API for certain path +StatusRecycler recycleBinStatus(const Zstring& pathName); //test existence of Recycle Bin API for certain path #endif } diff --git a/shared/resolve_path.cpp b/shared/resolve_path.cpp index 19dc0011..a9d1e0e5 100644 --- a/shared/resolve_path.cpp +++ b/shared/resolve_path.cpp @@ -8,6 +8,9 @@ #include "dll_loader.h" #include <wx/msw/wrapwin.h> //includes "windows.h" #include "long_path_prefix.h" +#ifdef _MSC_VER +#pragma comment(lib, "Mpr.lib") +#endif #elif defined FFS_LINUX #include <map> @@ -21,20 +24,42 @@ using namespace zen; namespace { #ifdef FFS_WIN -Zstring resolveRelativePath(const Zstring& relativeName, DWORD proposedBufferSize = 1000) +Zstring resolveBrokenNetworkMap(const Zstring& dirname) //circumvent issue with disconnected network maps that could be activated by a simple explorer double click { - std::vector<Zchar> fullPath(proposedBufferSize); - - const DWORD rv = ::GetFullPathName( - applyLongPathPrefix(relativeName).c_str(), //__in LPCTSTR lpFileName, - proposedBufferSize, //__in DWORD nBufferLength, - &fullPath[0], //__out LPTSTR lpBuffer, - NULL); //__out LPTSTR *lpFilePart - if (rv == 0 || rv == proposedBufferSize) + if (dirname.size() >= 2 && iswalpha(dirname[0]) && dirname[1] == L':') + { + Zstring driveLetter(dirname.c_str(), 2); //e.g.: "Q:" + if (::GetFileAttributes((driveLetter + L'\\').c_str()) == INVALID_FILE_ATTRIBUTES) + { + DWORD bufferSize = 10000; + std::vector<wchar_t> remoteNameBuffer(bufferSize); + DWORD rv = ::WNetGetConnection(driveLetter.c_str(), //__in LPCTSTR lpLocalName in the form "<driveletter>:" + &remoteNameBuffer[0], //__out LPTSTR lpRemoteName, + &bufferSize); //__inout LPDWORD lpnLength + (void)rv; + //no error check here! remoteNameBuffer will be filled on ERROR_CONNECTION_UNAVAIL and maybe others? + Zstring networkShare = &remoteNameBuffer[0]; + if (!networkShare.empty()) + return networkShare + (dirname.c_str() + 2); //replace "Q:\subdir" by "\\server\share\subdir" + } + } + return dirname; +} + + +Zstring resolveRelativePath(Zstring relativeName) +{ + relativeName = resolveBrokenNetworkMap(relativeName); + + std::vector<Zchar> fullPath(10000); + + const DWORD rv = ::GetFullPathName(applyLongPathPrefix(relativeName).c_str(), //__in LPCTSTR lpFileName, + static_cast<DWORD>(fullPath.size()), //__in DWORD nBufferLength, + &fullPath[0], //__out LPTSTR lpBuffer, + NULL); //__out LPTSTR *lpFilePart + if (rv == 0 || rv >= fullPath.size()) //theoretically, rv can never be == fullPath.size() //ERROR! Don't do anything return relativeName; - if (rv > proposedBufferSize) - return resolveRelativePath(relativeName, rv); return &fullPath[0]; } diff --git a/shared/resolve_path.h b/shared/resolve_path.h index 4ee1a90f..f6ae58cb 100644 --- a/shared/resolve_path.h +++ b/shared/resolve_path.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef RESOLVE_PATH_H_INCLUDED #define RESOLVE_PATH_H_INCLUDED diff --git a/shared/serialize.cpp b/shared/serialize.cpp index c2da1202..26102820 100644 --- a/shared/serialize.cpp +++ b/shared/serialize.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "serialize.h" #include "i18n.h" diff --git a/shared/serialize.h b/shared/serialize.h index 769b8671..c8a94ad1 100644 --- a/shared/serialize.h +++ b/shared/serialize.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SERIALIZE_H_INCLUDED #define SERIALIZE_H_INCLUDED diff --git a/shared/shadow.cpp b/shared/shadow.cpp index 948050b6..f000a69b 100644 --- a/shared/shadow.cpp +++ b/shared/shadow.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "shadow.h" #include <wx/msw/wrapwin.h> //includes "windows.h" #include "i18n.h" @@ -14,8 +14,8 @@ #include "ShadowCopy\shadow.h" #include "Loki/ScopeGuard.h" -using shadow::ShadowCopy; using namespace zen; +using namespace shadow; namespace @@ -23,7 +23,6 @@ namespace bool newerThanXP() { OSVERSIONINFO osvi = {}; - ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (::GetVersionEx(&osvi)) @@ -43,7 +42,7 @@ bool runningWOW64() //test if process is running under WOW64 (reference http://m HANDLE hProcess, PBOOL Wow64Process); - static const IsWow64ProcessFun isWow64Process = + const IsWow64ProcessFun isWow64Process = util::getDllFun<IsWow64ProcessFun>(L"kernel32.dll", "IsWow64Process"); if (isWow64Process) @@ -87,13 +86,8 @@ public: ShadowVolume(const Zstring& volumeNameFormatted) : //throw(FileError) backupHandle(0) { - using namespace shadow; - - if (!createShadowCopy) - createShadowCopy = util::getDllFun<CreateShadowCopyFct>(getShadowDllName(), createShadowCopyFctName); - - if (!releaseShadowCopy) - releaseShadowCopy = util::getDllFun<ReleaseShadowCopyFct>(getShadowDllName(), releaseShadowCopyFctName); + createShadowCopy = util::getDllFun<CreateShadowCopyFct>(getShadowDllName(), createShadowCopyFctName); + releaseShadowCopy = util::getDllFun<ReleaseShadowCopyFct>(getShadowDllName(), releaseShadowCopyFctName); //check if shadow copy dll was loaded correctly if (createShadowCopy == NULL || @@ -103,8 +97,7 @@ public: //VSS does not support running under WOW64 except for Windows XP and Windows Server 2003 //(Reference: http://msdn.microsoft.com/en-us/library/aa384627(VS.85).aspx) - static const bool wow64Active = runningWOW64(); - if (wow64Active) + if (runningWOW64()) throw FileError(_("Error starting Volume Shadow Copy Service!") + "\n" + _("Making shadow copies on WOW64 is not supported. Please use FreeFileSync 64-bit version.")); @@ -140,24 +133,18 @@ private: ShadowVolume(const ShadowVolume&); ShadowVolume& operator=(const ShadowVolume&); - static shadow::CreateShadowCopyFct createShadowCopy; - static shadow::ReleaseShadowCopyFct releaseShadowCopy; + shadow::CreateShadowCopyFct createShadowCopy; + shadow::ReleaseShadowCopyFct releaseShadowCopy; Zstring shadowVol; ShadowHandle backupHandle; }; - - -shadow::CreateShadowCopyFct ShadowCopy::ShadowVolume::createShadowCopy; -shadow::ReleaseShadowCopyFct ShadowCopy::ShadowVolume::releaseShadowCopy; //############################################################################################################# Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile) { - using namespace zen; - wchar_t volumeNameRaw[1000]; if (!::GetVolumePathName(inputFile.c_str(), //__in LPCTSTR lpszFileName, diff --git a/shared/shadow.h b/shared/shadow.h index edea4377..aff5b437 100644 --- a/shared/shadow.h +++ b/shared/shadow.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SHADOW_H_INCLUDED #define SHADOW_H_INCLUDED diff --git a/shared/shell_execute.h b/shared/shell_execute.h index 2cabbed1..64797eff 100644 --- a/shared/shell_execute.h +++ b/shared/shell_execute.h @@ -3,20 +3,20 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef EXECUTE_HEADER_23482134578134134 #define EXECUTE_HEADER_23482134578134134 -#include <wx/string.h> #include <wx/msgdlg.h> #ifdef FFS_WIN -#include "string_tools.h" #include "last_error.h" +#include "string_tools.h" #include "i18n.h" #include <wx/msw/wrapwin.h> //includes "windows.h" #elif defined FFS_LINUX +#include <stdlib.h> #include <wx/utils.h> #endif @@ -69,7 +69,7 @@ void shellExecute(const wxString& command, ExecutionType type = EXEC_TYPE_ASYNC) if (!::ShellExecuteEx(&execInfo)) //__inout LPSHELLEXECUTEINFO lpExecInfo { - wxString errorMsg = _("Invalid commandline: \"%x\""); + wxString errorMsg = _("Invalid commandline: %x"); wxString cmdFmt = wxString(L"\nFile: ") + filename + L"\nArg: " + arguments; errorMsg.Replace(L"%x", cmdFmt); @@ -87,10 +87,26 @@ void shellExecute(const wxString& command, ExecutionType type = EXEC_TYPE_ASYNC) } #elif defined FFS_LINUX - //by default wxExecute uses a zero sized dummy window as a hack to keep focus which leaves a useless empty icon in ALT-TAB list - //=> use wxEXEC_NODISABLE and roll our own window disabler! (see comment in app.cpp: void *wxGUIAppTraits::BeforeChildWaitLoop()) - wxWindowDisabler dummy; //disables all top level windows - wxExecute(command, (type == EXEC_TYPE_ASYNC ? wxEXEC_ASYNC : wxEXEC_SYNC) | wxEXEC_NODISABLE); + if (type == EXEC_TYPE_SYNC) + { + int rv = ::system(utf8CvrtTo<std::string>(command).c_str()); //do NOT use std::system as its documentation says nothing about "WEXITSTATUS(rv)", ect... + if (rv == -1 || WEXITSTATUS(rv) == 127) //http://linux.die.net/man/3/system "In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127)" + { + wxString errorMsg = _("Invalid commandline: %x"); + replace(errorMsg, L"%x", L"\n" + command); + wxMessageBox(errorMsg); + return; + } + } + else + { + // ! unfortunately it seems there is no way on Linux to get a failure notification for calling an invalid commandline asynchronously ! + + //by default wxExecute uses a zero sized dummy window as a hack to keep focus which leaves a useless empty icon in ALT-TAB list + //=> use wxEXEC_NODISABLE and roll our own window disabler! (see comment in app.cpp: void *wxGUIAppTraits::BeforeChildWaitLoop()) + wxWindowDisabler dummy; //disables all top level windows + wxExecute(command, wxEXEC_ASYNC | wxEXEC_NODISABLE); + } #endif } } diff --git a/shared/standard_paths.cpp b/shared/standard_paths.cpp index 928dedcc..c2d79516 100644 --- a/shared/standard_paths.cpp +++ b/shared/standard_paths.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "standard_paths.h" #include <wx/stdpaths.h> #include "string_conv.h" diff --git a/shared/standard_paths.h b/shared/standard_paths.h index faafe263..708a377d 100644 --- a/shared/standard_paths.h +++ b/shared/standard_paths.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef STANDARDPATHS_H_INCLUDED #define STANDARDPATHS_H_INCLUDED diff --git a/shared/string_conv.h b/shared/string_conv.h index 477dda8c..976080fb 100644 --- a/shared/string_conv.h +++ b/shared/string_conv.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef STRINGCONV_H_INCLUDED #define STRINGCONV_H_INCLUDED diff --git a/shared/symlink_target.h b/shared/symlink_target.h index e3a75f81..ef7b2275 100644 --- a/shared/symlink_target.h +++ b/shared/symlink_target.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SYMLINK_WIN_H_INCLUDED #define SYMLINK_WIN_H_INCLUDED diff --git a/shared/taskbar.cpp b/shared/taskbar.cpp index 969f4bb0..005df705 100644 --- a/shared/taskbar.cpp +++ b/shared/taskbar.cpp @@ -5,16 +5,24 @@ // ************************************************************************** // #include "taskbar.h" + +#ifdef FFS_WIN #include "Taskbar_Seven/taskbar.h" #include "dll_loader.h" #include "build_info.h" #include "assert_static.h" #include <wx/msw/wrapwin.h> //includes "windows.h" +#elif defined HAVE_UBUNTU_UNITY +#include <unity/unity/unity.h> +#endif + using namespace util; -using namespace tbseven; +#ifdef FFS_WIN +using namespace tbseven; + namespace { bool windows7TaskbarAvailable() @@ -45,7 +53,7 @@ std::wstring getTaskBarDllName() //######################################################################################################## -class TaskbarProgress::Pimpl //throw (TaskbarNotAvailable) +class Taskbar::Pimpl //throw (TaskbarNotAvailable) { public: Pimpl(const wxTopLevelWindow& window) : @@ -60,29 +68,26 @@ public: throw TaskbarNotAvailable(); } - ~Pimpl() - { - setStatus(STATUS_NOPROGRESS); - } + ~Pimpl() { setStatus(STATUS_NOPROGRESS); } void setStatus(Status status) { TaskBarStatus tbSevenStatus = tbseven::STATUS_NORMAL; switch (status) { - case TaskbarProgress::STATUS_NOPROGRESS: + case Taskbar::STATUS_NOPROGRESS: tbSevenStatus = tbseven::STATUS_NOPROGRESS; break; - case TaskbarProgress::STATUS_INDETERMINATE: + case Taskbar::STATUS_INDETERMINATE: tbSevenStatus = tbseven::STATUS_INDETERMINATE; break; - case TaskbarProgress::STATUS_NORMAL: + case Taskbar::STATUS_NORMAL: tbSevenStatus = tbseven::STATUS_NORMAL; break; - case TaskbarProgress::STATUS_ERROR: + case Taskbar::STATUS_ERROR: tbSevenStatus = tbseven::STATUS_ERROR; break; - case TaskbarProgress::STATUS_PAUSED: + case Taskbar::STATUS_PAUSED: tbSevenStatus = tbseven::STATUS_PAUSED; break; } @@ -96,23 +101,80 @@ public: } private: - void* assocWindow; + void* assocWindow; //HWND const SetStatusFct setStatus_; const SetProgressFct setProgress_; }; -//######################################################################################################## +#elif defined HAVE_UBUNTU_UNITY //Ubuntu unity +namespace +{ +const char FFS_DESKTOP_FILE[] = "freefilesync.desktop"; +} -TaskbarProgress::TaskbarProgress(const wxTopLevelWindow& window) : pimpl_(new Pimpl(window)) {} +class Taskbar::Pimpl //throw (TaskbarNotAvailable) +{ +public: + Pimpl(const wxTopLevelWindow& window) : + tbEntry(unity_launcher_entry_get_for_desktop_id(FFS_DESKTOP_FILE)) + //tbEntry(unity_launcher_entry_get_for_app_uri("application://freefilesync.desktop")) + { + if (!tbEntry) + throw TaskbarNotAvailable(); + } -TaskbarProgress::~TaskbarProgress() {} //std::unique_ptr ... + ~Pimpl() { setStatus(STATUS_NOPROGRESS); } //it seems UnityLauncherEntry* does not need destruction -void TaskbarProgress::setStatus(Status status) -{ - pimpl_->setStatus(status); -} + void setStatus(Status status) + { + switch (status) + { + case Taskbar::STATUS_ERROR: + unity_launcher_entry_set_urgent(tbEntry, true); + break; -void TaskbarProgress::setProgress(size_t current, size_t total) + case Taskbar::STATUS_NOPROGRESS: + case Taskbar::STATUS_INDETERMINATE: + unity_launcher_entry_set_urgent(tbEntry, false); + unity_launcher_entry_set_progress_visible(tbEntry, false); + break; + + case Taskbar::STATUS_NORMAL: + unity_launcher_entry_set_urgent(tbEntry, false); + unity_launcher_entry_set_progress_visible(tbEntry, true); + break; + + case Taskbar::STATUS_PAUSED: + unity_launcher_entry_set_urgent (tbEntry, false); + break; + } + } + + void setProgress(size_t current, size_t total) + { + unity_launcher_entry_set_progress(tbEntry, total == 0 ? 0 : double(current) / total); + } + +private: + UnityLauncherEntry* tbEntry; +}; + + +#else //no taskbar support yet +class Taskbar::Pimpl { - pimpl_->setProgress(current, total); -} +public: + Pimpl(const wxTopLevelWindow& window) { throw TaskbarNotAvailable(); } + void setStatus(Status status) {} + void setProgress(size_t current, size_t total) {} + +}; +#endif + + +//######################################################################################################## +Taskbar::Taskbar(const wxTopLevelWindow& window) : pimpl_(new Pimpl(window)) {} //throw TaskbarNotAvailable +Taskbar::~Taskbar() {} //std::unique_ptr ... + +void Taskbar::setStatus(Status status) { pimpl_->setStatus(status); } +void Taskbar::setProgress(size_t current, size_t total) { pimpl_->setProgress(current, total); } diff --git a/shared/taskbar.h b/shared/taskbar.h index 90a76d13..54e0a431 100644 --- a/shared/taskbar.h +++ b/shared/taskbar.h @@ -3,28 +3,32 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef TASKBARPROGRESS_H_INCLUDED #define TASKBARPROGRESS_H_INCLUDED -#ifndef FFS_WIN -use in windows build only! -#endif - #include <wx/toplevel.h> #include <memory> +/* +Windows 7; show progress in windows superbar via ITaskbarList3 Interface (http://msdn.microsoft.com/en-us/library/dd391692(VS.85).aspx) + +Ubuntu: use Unity interface (optional) + +Define HAVE_UBUNTU_UNITY and set: + Compiler flag: `pkg-config --cflags unity` + Linker flag: `pkg-config --libs unity` +*/ namespace util { class TaskbarNotAvailable {}; -//show progress in windows superbar via ITaskbarList3 Interface (http://msdn.microsoft.com/en-us/library/dd391692(VS.85).aspx) -class TaskbarProgress +class Taskbar { public: - TaskbarProgress(const wxTopLevelWindow& window); //throw TaskbarNotAvailable() - ~TaskbarProgress(); + Taskbar(const wxTopLevelWindow& window); //throw TaskbarNotAvailable() + ~Taskbar(); enum Status { diff --git a/shared/toggle_button.cpp b/shared/toggle_button.cpp index 511822b1..24f74bc7 100644 --- a/shared/toggle_button.cpp +++ b/shared/toggle_button.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "toggle_button.h" void ToggleButton::init(const wxBitmap& activeBmp, diff --git a/shared/toggle_button.h b/shared/toggle_button.h index 6613fdd4..f333ca24 100644 --- a/shared/toggle_button.h +++ b/shared/toggle_button.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef TOGGLEBUTTON_H_INCLUDED #define TOGGLEBUTTON_H_INCLUDED diff --git a/shared/util.cpp b/shared/util.cpp index faa4074c..d780d683 100644 --- a/shared/util.cpp +++ b/shared/util.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "util.h" #include "zstring.h" #include "i18n.h" diff --git a/shared/util.h b/shared/util.h index 25762ce7..0e08280e 100644 --- a/shared/util.h +++ b/shared/util.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef UTIL_H_INCLUDED #define UTIL_H_INCLUDED diff --git a/shared/warn_static.h b/shared/warn_static.h new file mode 100644 index 00000000..bb4f4a6f --- /dev/null +++ b/shared/warn_static.h @@ -0,0 +1,35 @@ +// ************************************************************************** +// * This file is part of the zenXML project. It is distributed under the * +// * Boost Software License, Version 1.0. See accompanying file * +// * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt. * +// * Copyright (C) 2011 ZenJu (zhnmju123 AT gmx.de) * +// ************************************************************************** + +#ifndef WARN_STATIC_HEADER_08724567834560832745 +#define WARN_STATIC_HEADER_08724567834560832745 + +/* +Portable Compile-Time Warning +----------------------------- +Usage: + warn_static("my message") +*/ + +#ifdef _MSC_VER +#define MAKE_STRING_SUB(NUM) #NUM +#define MAKE_STRING(NUM) MAKE_STRING_SUB(NUM) + +#define warn_static(TXT) \ +__pragma(message (__FILE__ "(" MAKE_STRING(__LINE__) "): Warning: " ## TXT)) + +#elif defined __GNUC__ +#define LOKI_CONCAT( X, Y ) LOKI_CONCAT_SUB( X, Y ) +#define LOKI_CONCAT_SUB( X, Y ) X##Y + +#define warn_static(TXT) \ +typedef int STATIC_WARNING __attribute__ ((deprecated)); \ +enum { LOKI_CONCAT(warn_static_dummy_value, __LINE__) = sizeof(STATIC_WARNING) }; +#endif + + +#endif //WARN_STATIC_HEADER_08724567834560832745
\ No newline at end of file diff --git a/shared/wx_choice_enum.h b/shared/wx_choice_enum.h index 9f832394..e8fbeead 100644 --- a/shared/wx_choice_enum.h +++ b/shared/wx_choice_enum.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef WX_CHOICE_ENUM_H_INCLUDED #define WX_CHOICE_ENUM_H_INCLUDED diff --git a/shared/wx_timespan.h b/shared/wx_timespan.h index 108327bf..2e566927 100644 --- a/shared/wx_timespan.h +++ b/shared/wx_timespan.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef WX_TIMESPAN_CTRL_HEADER_INCLUDED #define WX_TIMESPAN_CTRL_HEADER_INCLUDED diff --git a/shared/xml_base.cpp b/shared/xml_base.cpp index cd9f58a6..c7c92401 100644 --- a/shared/xml_base.cpp +++ b/shared/xml_base.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "xml_base.h" #include <file_handling.h> #include <string_conv.h> @@ -13,12 +13,14 @@ using namespace zen; +//loadXmlDocument vs loadStream: +//1. better error reporting +//2. quick exit if (potentially large) input file is not an XML void xmlAccess::loadXmlDocument(const wxString& filename, XmlDoc& doc) //throw FfsXmlError() { std::string stream; try { - const zen::UInt64 fs = zen::getFilesize(toZ(filename)); //throw (FileError) { //quick test whether input is an XML: avoid loading large binary files up front! //doesn't correctly handle BOM! (but no issue yet...) @@ -31,6 +33,7 @@ void xmlAccess::loadXmlDocument(const wxString& filename, XmlDoc& doc) //throw F throw FfsXmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"")); } + const zen::UInt64 fs = zen::getFilesize(toZ(filename)); //throw (FileError) stream.resize(to<size_t>(fs)); FileInput inputFile(toZ(filename)); //throw (FileError); diff --git a/shared/xml_base.h b/shared/xml_base.h index 6a47fcf2..37d79863 100644 --- a/shared/xml_base.h +++ b/shared/xml_base.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef XMLBASE_H_INCLUDED #define XMLBASE_H_INCLUDED diff --git a/shared/xml_error.h b/shared/xml_error.h index ffd6af8b..6e17670a 100644 --- a/shared/xml_error.h +++ b/shared/xml_error.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef XMLERROR_H_INCLUDED #define XMLERROR_H_INCLUDED diff --git a/shared/zbase.h b/shared/zbase.h index 6d9ac578..55bc0d96 100644 --- a/shared/zbase.h +++ b/shared/zbase.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef Z_BASE_H_INCLUDED #define Z_BASE_H_INCLUDED @@ -12,6 +12,7 @@ #include <sstream> #include <algorithm> #include <string_tools.h> +#include <boost/detail/atomic_count.hpp> /* Allocator Policy: @@ -101,24 +102,17 @@ private: size_t capacity; //allocated size without null-termination }; - static Descriptor* descr(T* ptr) - { - return reinterpret_cast<Descriptor*>(ptr) - 1; - } - - static const Descriptor* descr(const T* ptr) - { - return reinterpret_cast<const Descriptor*>(ptr) - 1; - } + static Descriptor* descr( T* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } + static const Descriptor* descr(const T* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; } }; template <typename T, //Character Type class AP> //Allocator Policy -class StorageRefCount : public AP +class StorageRefCountThreadSafe : public AP { protected: - ~StorageRefCount() {} + ~StorageRefCountThreadSafe() {} static T* create(size_t size) { @@ -132,10 +126,7 @@ protected: assert(minCapacity >= size); Descriptor* const newDescr = static_cast<Descriptor*>(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(T))); - - newDescr->refCount = 1; - newDescr->length = size; - newDescr->capacity = newCapacity; + new (newDescr) Descriptor(1, size, newCapacity); return reinterpret_cast<T*>(newDescr + 1); } @@ -150,7 +141,10 @@ protected: static void destroy(T* ptr) { if (--descr(ptr)->refCount == 0) + { + descr(ptr)->~Descriptor(); AP::deallocate(descr(ptr)); + } } static bool canWrite(const T* ptr, size_t minCapacity) //needs to be checked before writing to "ptr" @@ -173,26 +167,23 @@ protected: private: struct Descriptor { - size_t refCount; + Descriptor(long rc, size_t len, size_t cap) : refCount(rc), length(len), capacity(cap) {} + + boost::detail::atomic_count refCount; //practically no perf loss: ~0.2%! (FFS comparison) size_t length; size_t capacity; //allocated size without null-termination }; - static Descriptor* descr(T* ptr) - { - return reinterpret_cast<Descriptor*>(ptr) - 1; - } - - static const Descriptor* descr(const T* ptr) - { - return reinterpret_cast<const Descriptor*>(ptr) - 1; - } + static Descriptor* descr( T* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } + static const Descriptor* descr(const T* ptr) { return reinterpret_cast<const Descriptor*>(ptr) - 1; } }; -template <class T, //Character Type - template <class, class> class SP = StorageRefCount, //Storage Policy - class AP = AllocatorOptimalSpeed> //Allocator Policy +//perf note: interstingly StorageDeepCopy and StorageRefCountThreadSafe show same performance in FFS comparison + +template <class T, //Character Type + template <class, class> class SP = StorageRefCountThreadSafe, //Storage Policy + class AP = AllocatorOptimalSpeed> //Allocator Policy class Zbase : public SP<T, AP> { public: @@ -200,6 +191,7 @@ public: Zbase(const T* source); //implicit conversion from a C-string Zbase(const T* source, size_t length); Zbase(const Zbase& source); + Zbase(Zbase&& tmp); explicit Zbase(T source); //dangerous if implicit: T buffer[]; Zbase name = buffer; ups... //allow explicit construction from different string type, prevent ambiguity via SFINAE template <class S> explicit Zbase(const S& other, typename S::value_type = 0); @@ -261,6 +253,7 @@ public: void push_back(T val); //STL access Zbase& operator=(const Zbase& source); + Zbase& operator=(Zbase&& tmp); Zbase& operator=(const T* source); Zbase& operator=(T source); Zbase& operator+=(const Zbase& other); @@ -380,6 +373,14 @@ Zbase<T, SP, AP>::Zbase(const Zbase<T, SP, AP>& source) template <class T, template <class, class> class SP, class AP> +inline +Zbase<T, SP, AP>::Zbase(Zbase<T, SP, AP>&& tmp) +{ + rawStr = this->clone(tmp.rawStr); //for a ref-counting string there probably isn't a faster way, even with r-value references +} + + +template <class T, template <class, class> class SP, class AP> template <class S> inline Zbase<T, SP, AP>::Zbase(const S& other, typename S::value_type) @@ -827,6 +828,15 @@ Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator=(const Zbase<T, SP, AP>& source) template <class T, template <class, class> class SP, class AP> inline +Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator=(Zbase<T, SP, AP>&& tmp) +{ + swap(tmp); + return *this; +} + + +template <class T, template <class, class> class SP, class AP> +inline Zbase<T, SP, AP>& Zbase<T, SP, AP>::operator=(const T* source) { return assign(source, zen::cStringLength(source)); diff --git a/shared/zstring.cpp b/shared/zstring.cpp index 2955ec3e..e1df17ee 100644 --- a/shared/zstring.cpp +++ b/shared/zstring.cpp @@ -3,9 +3,10 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "zstring.h" #include <stdexcept> +#include <boost/thread/once.hpp> #ifdef FFS_WIN #include <wx/msw/wrapwin.h> //includes "windows.h" @@ -52,6 +53,12 @@ LeakChecker& LeakChecker::instance() return inst; } +//caveat: function scope static initialization is not thread-safe in VS 2010! => make sure to call at app start! +namespace +{ +struct Dummy { Dummy() { LeakChecker::instance(); }} blah; +} + std::string LeakChecker::rawMemToString(const void* ptr, size_t size) { @@ -113,7 +120,10 @@ int z_impl::compareFilenamesWin(const wchar_t* a, const wchar_t* b, size_t sizeA LPCWSTR lpString2, int cchCount2, BOOL bIgnoreCase); - static const CompareStringOrdinalFunc ordinalCompare = util::getDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal"); + static CompareStringOrdinalFunc ordinalCompare = NULL; //caveat: function scope static initialization is not thread-safe in VS 2010! + static boost::once_flag once = BOOST_ONCE_INIT; + boost::call_once(once, []() { ordinalCompare = util::getDllFun<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal"); }); + if (ordinalCompare != NULL) //this additional test has no noticeable performance impact { diff --git a/shared/zstring.h b/shared/zstring.h index 841fb8a2..3b4837db 100644 --- a/shared/zstring.h +++ b/shared/zstring.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef ZSTRING_H_INCLUDED #define ZSTRING_H_INCLUDED @@ -11,8 +11,8 @@ #include <cstring> //strcmp() #ifndef NDEBUG +#include "boost_thread_wrap.h" //include <boost/thread.hpp> #include <map> -#include <wx/thread.h> #endif @@ -22,7 +22,7 @@ class LeakChecker //small test for memory leaks public: void insert(const void* ptr, size_t size) { - wxCriticalSectionLocker dummy(lockActStrings); + boost::lock_guard<boost::mutex> dummy(lockActStrings); if (activeStrings.find(ptr) != activeStrings.end()) reportProblem(std::string("Fatal Error: New memory points into occupied space: ") + rawMemToString(ptr, size)); @@ -31,8 +31,7 @@ public: void remove(const void* ptr) { - wxCriticalSectionLocker dummy(lockActStrings); - + boost::lock_guard<boost::mutex> dummy(lockActStrings); if (activeStrings.find(ptr) == activeStrings.end()) reportProblem(std::string("Fatal Error: No memory available for deallocation at this location!")); @@ -50,7 +49,7 @@ private: static std::string rawMemToString(const void* ptr, size_t size); void reportProblem(const std::string& message); //throw (std::logic_error) - wxCriticalSection lockActStrings; + boost::mutex lockActStrings; typedef std::map<const void*, size_t> VoidPtrSizeMap; VoidPtrSizeMap activeStrings; }; @@ -106,7 +105,6 @@ template <template <class, class> class SP, class AP> void MakeUpper(Zbase<wchar_t, SP, AP>& str); #endif - #ifdef FFS_WIN //Windows stores filenames in wide character format typedef wchar_t Zchar; #define Zstr(x) L ## x @@ -121,8 +119,8 @@ const Zchar FILE_NAME_SEPARATOR = '/'; #error define platform you are in: FFS_WIN or FFS_LINUX #endif -//"The reason for all the fuss above" (Loki/SmartPtr) -typedef Zbase<Zchar, StorageRefCount, AllocatorFreeStoreChecked> Zstring; //for use with file names +//"The reason for all the fuss above" - Loki/SmartPtr +typedef Zbase<Zchar, StorageRefCountThreadSafe, AllocatorFreeStoreChecked> Zstring; //for use with file names diff --git a/structures.cpp b/structures.cpp index 70613eff..0d16f735 100644 --- a/structures.cpp +++ b/structures.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "structures.h" #include "shared/i18n.h" #include <iterator> diff --git a/structures.h b/structures.h index 500cf784..6039066d 100644 --- a/structures.h +++ b/structures.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FREEFILESYNC_H_INCLUDED #define FREEFILESYNC_H_INCLUDED diff --git a/synchronization.cpp b/synchronization.cpp index d438f947..d641bafc 100644 --- a/synchronization.cpp +++ b/synchronization.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "synchronization.h" #include <stdexcept> #include <wx/msgdlg.h> @@ -19,9 +19,10 @@ #include <wx/file.h> #include <boost/bind.hpp> #include "shared/global_func.h" +#include "shared/disable_standby.h" #include <memory> #include "library/db_file.h" -#include "shared/disable_standby.h" +#include "library/dir_exist_async.h" #include "library/cmp_filetime.h" #include "shared/file_io.h" #include <deque> @@ -49,13 +50,6 @@ void SyncStatistics::init() } -SyncStatistics::SyncStatistics(const HierarchyObject& hierObj) -{ - init(); - getNumbersRecursively(hierObj); -} - - SyncStatistics::SyncStatistics(const FolderComparison& folderCmp) { init(); @@ -63,25 +57,10 @@ SyncStatistics::SyncStatistics(const FolderComparison& folderCmp) } -int SyncStatistics::getConflict() const -{ - return conflict; -} - -const SyncStatistics::ConflictTexts& SyncStatistics::getFirstConflicts() const //get first few sync conflicts -{ - return firstConflicts; -} - -zen::UInt64 SyncStatistics::getDataToProcess() const -{ - return dataToProcess; -} - - -size_t SyncStatistics::getRowCount() const +SyncStatistics::SyncStatistics(const HierarchyObject& hierObj) { - return rowsTotal; + init(); + getNumbersRecursively(hierObj); } @@ -443,7 +422,7 @@ DeletionHandling::DeletionHandling(DeletionPolicy handleDel, cleanedUp(false) { #ifdef FFS_WIN - if (deletionType == MOVE_TO_RECYCLE_BIN && recycleBinExists(baseDir) != STATUS_REC_EXISTS) + if (deletionType == MOVE_TO_RECYCLE_BIN && recycleBinStatus(baseDir) != STATUS_REC_EXISTS) deletionType = DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks) #endif @@ -476,11 +455,9 @@ DeletionHandling::DeletionHandling(DeletionPolicy handleDel, DeletionHandling::~DeletionHandling() { - try //always (try to) clean up, even if synchronization is aborted! - { - tryCleanup(); //make sure this stays non-blocking! - } - catch (...) {} + //always (try to) clean up, even if synchronization is aborted! + try { tryCleanup(); } + catch (...) {} //make sure this stays non-blocking! } @@ -546,7 +523,7 @@ void DeletionHandling::removeFile(const Zstring& relativeName) const try //rename file: no copying!!! { - if (!dirExists(targetDir)) + if (!dirExistsUpdating(targetDir, *procCallback_)) createDirectory(targetDir); //throw FileError -> may legitimately fail on Linux if permissions are missing //performance optimization!! Instead of moving each object into recycle bin separately, we rename them ony by one into a @@ -567,7 +544,7 @@ void DeletionHandling::removeFile(const Zstring& relativeName) const const Zstring targetFile = sessionDelDir + relativeName; //altDeletionDir ends with path separator const Zstring targetDir = targetFile.BeforeLast(FILE_NAME_SEPARATOR); - if (!dirExists(targetDir)) + if (!dirExistsUpdating(targetDir, *procCallback_)) createDirectory(targetDir); //throw (FileError) CallbackMoveFileImpl callBack(*procCallback_); //if file needs to be copied we need callback functionality to update screen and offer abort @@ -592,14 +569,14 @@ void DeletionHandling::removeFolder(const Zstring& relativeName) const break; case MOVE_TO_RECYCLE_BIN: - if (dirExists(fullName)) + if (!dirExistsUpdating(fullName, *procCallback_)) { const Zstring targetDir = sessionDelDir + relativeName; const Zstring targetSuperDir = targetDir.BeforeLast(FILE_NAME_SEPARATOR); try //rename directory: no copying!!! { - if (!dirExists(targetSuperDir)) + if (!dirExistsUpdating(targetSuperDir, *procCallback_)) createDirectory(targetSuperDir); //throw FileError -> may legitimately fail on Linux if permissions are missing //performance optimization!! Instead of moving each object into recycle bin separately, we rename them ony by one into a @@ -616,12 +593,12 @@ void DeletionHandling::removeFolder(const Zstring& relativeName) const break; case MOVE_TO_CUSTOM_DIRECTORY: - if (dirExists(fullName)) + if (!dirExistsUpdating(fullName, *procCallback_)) { const Zstring targetDir = sessionDelDir + relativeName; const Zstring targetSuperDir = targetDir.BeforeLast(FILE_NAME_SEPARATOR); - if (!dirExists(targetSuperDir)) + if (!dirExistsUpdating(targetSuperDir, *procCallback_)) createDirectory(targetSuperDir); //throw (FileError) CallbackMoveFileImpl callBack(*procCallback_); //if files need to be copied, we need callback functionality to update screen and offer abort @@ -643,11 +620,11 @@ bool DeletionHandling::deletionFreesSpace() const case MOVE_TO_CUSTOM_DIRECTORY: switch (zen::onSameVolume(baseDir_, sessionDelDir)) { - case VOLUME_SAME: + case IS_SAME_YES: return false; - case VOLUME_DIFFERENT: + case IS_SAME_NO: return true; //but other volume (sessionDelDir) may become full... - case VOLUME_CANT_SAY: + case IS_SAME_CANT_SAY: return true; //a rough guess! } } @@ -1178,7 +1155,7 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText)); //some check to catch the error that directory on source has been deleted externally after "compare"... - if (!zen::dirExists(dirObj.getFullName<RIGHT_SIDE>())) + if (!dirExistsUpdating(dirObj.getFullName<RIGHT_SIDE>(), procCallback_)) throw FileError(_("Source directory does not exist anymore:") + "\n\"" + dirObj.getFullName<RIGHT_SIDE>() + "\""); createDirectory(target, dirObj.getFullName<RIGHT_SIDE>(), copyFilePermissions_); //no symlink copying! break; @@ -1191,7 +1168,7 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const procCallback_.reportInfo(utf8CvrtTo<wxString>(statusText)); //some check to catch the error that directory on source has been deleted externally after "compare"... - if (!zen::dirExists(dirObj.getFullName<LEFT_SIDE>())) + if (!dirExistsUpdating(dirObj.getFullName<LEFT_SIDE>(), procCallback_)) throw FileError(_("Source directory does not exist anymore:") + "\n\"" + dirObj.getFullName<LEFT_SIDE>() + "\""); createDirectory(target, dirObj.getFullName<LEFT_SIDE>(), copyFilePermissions_); //no symlink copying! break; @@ -1274,11 +1251,11 @@ void SynchronizeFolderPair::synchronizeFolder(DirMapping& dirObj) const //avoid data loss when source directory doesn't (temporarily?) exist anymore AND user chose to ignore errors (else we wouldn't arrive here) -bool dataLossPossible(const Zstring& dirName, const SyncStatistics& folderPairStat) +bool dataLossPossible(const Zstring& dirName, const SyncStatistics& folderPairStat, ProcessCallback& procCallback) { return folderPairStat.getCreate() + folderPairStat.getOverwrite() + folderPairStat.getConflict() == 0 && folderPairStat.getDelete() > 0 && //deletions only... (respect filtered items!) - !dirName.empty() && !zen::dirExists(dirName); + !dirName.empty() && !dirExistsUpdating(dirName, procCallback); } @@ -1485,13 +1462,13 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf } //avoid data loss when source directory doesn't (temporarily?) exist anymore AND user chose to ignore errors(else we wouldn't arrive here) - if (dataLossPossible(j->getBaseDir<LEFT_SIDE>(), statisticsFolderPair)) + if (dataLossPossible(j->getBaseDir<LEFT_SIDE>(), statisticsFolderPair, procCallback)) { procCallback.reportFatalError(_("Source directory does not exist anymore:") + "\n\"" + j->getBaseDir<LEFT_SIDE>() + "\""); skipFolderPair[folderIndex] = true; continue; } - if (dataLossPossible(j->getBaseDir<RIGHT_SIDE>(), statisticsFolderPair)) + if (dataLossPossible(j->getBaseDir<RIGHT_SIDE>(), statisticsFolderPair, procCallback)) { procCallback.reportFatalError(_("Source directory does not exist anymore:") + "\n\"" + j->getBaseDir<RIGHT_SIDE>() + "\""); skipFolderPair[folderIndex] = true; @@ -1531,13 +1508,13 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf if (statisticsFolderPair.getOverwrite<LEFT_SIDE>() + statisticsFolderPair.getDelete <LEFT_SIDE>() > 0 && - recycleBinExists(j->getBaseDir<LEFT_SIDE>()) != STATUS_REC_EXISTS) + recycleBinStatus(j->getBaseDir<LEFT_SIDE>()) != STATUS_REC_EXISTS) recyclMissing.insert(j->getBaseDir<LEFT_SIDE>()); if (statisticsFolderPair.getOverwrite<RIGHT_SIDE>() + statisticsFolderPair.getDelete <RIGHT_SIDE>() > 0 && - recycleBinExists(j->getBaseDir<RIGHT_SIDE>()) != STATUS_REC_EXISTS) + recycleBinStatus(j->getBaseDir<RIGHT_SIDE>()) != STATUS_REC_EXISTS) recyclMissing.insert(j->getBaseDir<RIGHT_SIDE>()); } #endif @@ -1591,8 +1568,8 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf for (auto i = diskSpaceMissing.begin(); i != diskSpaceMissing.end(); ++i) warningMessage += wxString(wxT("\n\n")) + "\"" + i->first + "\"\n" + - _("Free disk space required:") + wxT(" ") + formatFilesizeToShortString(to<zen::UInt64>(i->second.first)) + wxT("\n") + - _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(to<zen::UInt64>(i->second.second)); + _("Free disk space required:") + wxT(" ") + formatFilesizeToShortString(to<UInt64>(i->second.first)) + wxT("\n") + + _("Free disk space available:") + wxT(" ") + formatFilesizeToShortString(to<UInt64>(i->second.second)); procCallback.reportWarning(warningMessage, m_warnings.warningNotEnoughDiskSpace); } @@ -1671,15 +1648,15 @@ void SyncProcess::startSynchronizationProcess(const std::vector<FolderPairSyncCf //create base directories first (if not yet existing) -> no symlink or attribute copying! -> single error message instead of one per file (e.g. unplugged network drive) const Zstring dirnameLeft = j->getBaseDir<LEFT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR); - if (!dirnameLeft.empty() && !zen::dirExists(dirnameLeft)) + if (!dirnameLeft.empty() && !dirExistsUpdating(dirnameLeft, procCallback)) { - if (!tryReportingError(procCallback, boost::bind(zen::createDirectory, boost::cref(dirnameLeft)))) //may throw in error-callback! + if (!tryReportingError(procCallback, boost::bind(createDirectory, boost::cref(dirnameLeft)))) //may throw in error-callback! continue; //skip this folder pair } const Zstring dirnameRight = j->getBaseDir<RIGHT_SIDE>().BeforeLast(FILE_NAME_SEPARATOR); - if (!dirnameRight.empty() && !zen::dirExists(dirnameRight)) + if (!dirnameRight.empty() && !dirExistsUpdating(dirnameRight, procCallback)) { - if (!tryReportingError(procCallback, boost::bind(zen::createDirectory, boost::cref(dirnameRight)))) //may throw in error-callback! + if (!tryReportingError(procCallback, boost::bind(createDirectory, boost::cref(dirnameRight)))) //may throw in error-callback! continue; //skip this folder pair } //------------------------------------------------------------------------------------------ @@ -1778,10 +1755,10 @@ void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstrin { WhileCopying<DelTargetCommand> callback(bytesReported, procCallback_, cmd); - zen::copyFile(source, //type File implicitly means symlinks need to be dereferenced! - target, - copyFilePermissions_, - &callback); //throw (FileError, ErrorFileLocked); + copyFile(source, //type File implicitly means symlinks need to be dereferenced! + target, + copyFilePermissions_, + &callback); //throw (FileError, ErrorFileLocked); //inform about the (remaining) processed amount of data procCallback_.updateProcessedData(0, to<Int64>(totalBytesToCpy) - to<Int64>(bytesReported)); @@ -1823,7 +1800,7 @@ void SynchronizeFolderPair::copyFileUpdating(const Zstring& source, const Zstrin if (!targetDir.empty()) { - zen::createDirectory(targetDir, templateDir, copyFilePermissions_); //throw (FileError) + createDirectory(targetDir, templateDir, copyFilePermissions_); //throw (FileError) /*symbolic link handling: if "not traversing symlinks": fullName == c:\syncdir<symlinks>\some\dirs\leaf<symlink> => setting irrelevant @@ -1869,9 +1846,9 @@ void SynchronizeFolderPair::copySymlink(const Zstring& source, const Zstring& ta const Zstring targetDir = target.BeforeLast(FILE_NAME_SEPARATOR); const Zstring templateDir = source.BeforeLast(FILE_NAME_SEPARATOR); - if (!targetDir.empty() && !zen::dirExists(targetDir)) + if (!targetDir.empty() && !dirExistsUpdating(targetDir, procCallback_)) { - zen::createDirectory(targetDir, templateDir, copyFilePermissions_); //throw (FileError) + createDirectory(targetDir, templateDir, copyFilePermissions_); //throw (FileError) /*symbolic link handling: if "not traversing symlinks": fullName == c:\syncdir<symlinks>\some\dirs\leaf<symlink> => setting irrelevant @@ -1921,7 +1898,7 @@ void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& c static std::vector<char> memory2(1024 * 1024); #ifdef FFS_WIN - wxFile file1(zen::applyLongPathPrefix(source).c_str(), wxFile::read); //don't use buffered file input for verification! + wxFile file1(applyLongPathPrefix(source).c_str(), wxFile::read); //don't use buffered file input for verification! #elif defined FFS_LINUX wxFile file1(::open(source.c_str(), O_RDONLY)); //utilize UTF-8 filename #endif @@ -1929,7 +1906,7 @@ void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& c throw FileError(_("Error opening file:") + " \"" + source + "\""); #ifdef FFS_WIN - wxFile file2(zen::applyLongPathPrefix(target).c_str(), wxFile::read); //don't use buffered file input for verification! + wxFile file2(applyLongPathPrefix(target).c_str(), wxFile::read); //don't use buffered file input for verification! #elif defined FFS_LINUX wxFile file2(::open(target.c_str(), O_RDONLY)); //utilize UTF-8 filename #endif diff --git a/synchronization.h b/synchronization.h index 62a55d99..0f1931cb 100644 --- a/synchronization.h +++ b/synchronization.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SYNCHRONIZATION_H_INCLUDED #define SYNCHRONIZATION_H_INCLUDED @@ -30,13 +30,13 @@ public: int getDelete() const; template <SelectedSide side> int getDelete() const; - int getConflict() const; + int getConflict() const { return conflict; } typedef std::vector<std::pair<Zstring, wxString> > ConflictTexts; // Pair(filename/conflict text) - const ConflictTexts& getFirstConflicts() const; //get first few sync conflicts + const ConflictTexts& getFirstConflicts() const { return firstConflicts; } - zen::UInt64 getDataToProcess() const; - size_t getRowCount() const; + zen::UInt64 getDataToProcess() const { return dataToProcess; } + size_t getRowCount() const { return rowsTotal; } private: void init(); diff --git a/ui/batch_config.cpp b/ui/batch_config.cpp index c6aa4df7..043281e9 100644 --- a/ui/batch_config.cpp +++ b/ui/batch_config.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "batch_config.h" #include "../shared/dir_picker_i18n.h" #include "folder_pair.h" diff --git a/ui/batch_config.h b/ui/batch_config.h index 574fef66..9a272261 100644 --- a/ui/batch_config.h +++ b/ui/batch_config.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef BATCHCONFIG_H_INCLUDED #define BATCHCONFIG_H_INCLUDED diff --git a/ui/batch_status_handler.cpp b/ui/batch_status_handler.cpp index 2cfd43cb..772feaf4 100644 --- a/ui/batch_status_handler.cpp +++ b/ui/batch_status_handler.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "batch_status_handler.h" #include "msg_popup.h" #include <wx/ffile.h> diff --git a/ui/batch_status_handler.h b/ui/batch_status_handler.h index 1803019e..61e0f46e 100644 --- a/ui/batch_status_handler.h +++ b/ui/batch_status_handler.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef BATCHSTATUSHANDLER_H_INCLUDED #define BATCHSTATUSHANDLER_H_INCLUDED diff --git a/ui/check_version.cpp b/ui/check_version.cpp index 03f7e486..491cf3c2 100644 --- a/ui/check_version.cpp +++ b/ui/check_version.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "check_version.h" #include <wx/msgdlg.h> #include <wx/protocol/http.h> diff --git a/ui/check_version.h b/ui/check_version.h index b9606987..c1331f7f 100644 --- a/ui/check_version.h +++ b/ui/check_version.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef UPDATEVERSION_H_INCLUDED #define UPDATEVERSION_H_INCLUDED diff --git a/ui/folder_pair.h b/ui/folder_pair.h index 893acdc7..5ca8d92f 100644 --- a/ui/folder_pair.h +++ b/ui/folder_pair.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef FOLDERPAIR_H_INCLUDED #define FOLDERPAIR_H_INCLUDED diff --git a/ui/grid_view.cpp b/ui/grid_view.cpp index 59bfb767..ba1738f6 100644 --- a/ui/grid_view.cpp +++ b/ui/grid_view.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "grid_view.h" #include "sorting.h" #include "../synchronization.h" diff --git a/ui/grid_view.h b/ui/grid_view.h index edb54ab7..87d5c38d 100644 --- a/ui/grid_view.h +++ b/ui/grid_view.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef GRIDVIEW_H_INCLUDED #define GRIDVIEW_H_INCLUDED diff --git a/ui/gui_generated.cpp b/ui/gui_generated.cpp index d3c0bb8e..b15689ec 100644 --- a/ui/gui_generated.cpp +++ b/ui/gui_generated.cpp @@ -171,7 +171,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const fgSizer12->Add( m_bpButtonSyncConfig, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 3 ); - m_buttonStartSync = new wxButtonWithImage( m_panelTopButtons, wxID_ANY, _("Synchronize..."), wxDefaultPosition, wxSize( -1,42 ), 0 ); + m_buttonStartSync = new wxButtonWithImage( m_panelTopButtons, wxID_ANY, _("Synchronize..."), wxDefaultPosition, wxSize( 180,42 ), 0 ); m_buttonStartSync->SetFont( wxFont( 14, 74, 90, 92, false, wxEmptyString ) ); m_buttonStartSync->SetToolTip( _("Start synchronization") ); @@ -491,7 +491,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const fgSizer5->Add( m_bitmapCreate, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_textCtrlCreate = new wxTextCtrl( m_panelStatistics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlCreate = new wxTextCtrl( m_panelStatistics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlCreate->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlCreate->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlCreate->SetToolTip( _("Number of files and directories that will be created") ); @@ -503,7 +503,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const fgSizer5->Add( m_bitmapUpdate, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_textCtrlUpdate = new wxTextCtrl( m_panelStatistics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlUpdate = new wxTextCtrl( m_panelStatistics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlUpdate->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlUpdate->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlUpdate->SetToolTip( _("Number of files that will be overwritten") ); @@ -522,7 +522,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const fgSizer6->Add( m_bitmapDelete, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_textCtrlDelete = new wxTextCtrl( m_panelStatistics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlDelete = new wxTextCtrl( m_panelStatistics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlDelete->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlDelete->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlDelete->SetToolTip( _("Number of files and directories that will be deleted") ); @@ -534,7 +534,7 @@ MainDialogGenerated::MainDialogGenerated( wxWindow* parent, wxWindowID id, const fgSizer6->Add( m_bitmapData, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_textCtrlData = new wxTextCtrl( m_panelStatistics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlData = new wxTextCtrl( m_panelStatistics, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlData->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlData->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlData->SetToolTip( _("Total amount of data that will be transferred") ); @@ -3686,7 +3686,7 @@ SyncPreviewDlgGenerated::SyncPreviewDlgGenerated( wxWindow* parent, wxWindowID i fgSizer5->Add( m_bitmapCreate, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_textCtrlCreateL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlCreateL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlCreateL->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlCreateL->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlCreateL->SetToolTip( _("Number of files and directories that will be created") ); @@ -3698,7 +3698,7 @@ SyncPreviewDlgGenerated::SyncPreviewDlgGenerated( wxWindow* parent, wxWindowID i fgSizer5->Add( m_bitmapUpdate, 0, wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL, 5 ); - m_textCtrlUpdateL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlUpdateL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlUpdateL->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlUpdateL->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlUpdateL->SetToolTip( _("Number of files that will be overwritten") ); @@ -3710,7 +3710,7 @@ SyncPreviewDlgGenerated::SyncPreviewDlgGenerated( wxWindow* parent, wxWindowID i fgSizer5->Add( m_bitmapDelete, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_textCtrlDeleteL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlDeleteL = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlDeleteL->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlDeleteL->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlDeleteL->SetToolTip( _("Number of files and directories that will be deleted") ); @@ -3730,21 +3730,21 @@ SyncPreviewDlgGenerated::SyncPreviewDlgGenerated( wxWindow* parent, wxWindowID i fgSizer51->Add( m_staticText95, 0, wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 5 ); - m_textCtrlCreateR = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlCreateR = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlCreateR->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlCreateR->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlCreateR->SetToolTip( _("Number of files and directories that will be created") ); fgSizer51->Add( m_textCtrlCreateR, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_textCtrlUpdateR = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlUpdateR = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlUpdateR->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlUpdateR->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlUpdateR->SetToolTip( _("Number of files that will be overwritten") ); fgSizer51->Add( m_textCtrlUpdateR, 0, wxALIGN_CENTER_VERTICAL, 5 ); - m_textCtrlDeleteR = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlDeleteR = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY|wxTE_RIGHT ); m_textCtrlDeleteR->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlDeleteR->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlDeleteR->SetToolTip( _("Number of files and directories that will be deleted") ); @@ -3769,7 +3769,7 @@ SyncPreviewDlgGenerated::SyncPreviewDlgGenerated( wxWindow* parent, wxWindowID i bSizer156->Add( 0, 0, 1, wxEXPAND, 5 ); - m_textCtrlData = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_READONLY ); + m_textCtrlData = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 80,-1 ), wxTE_CENTRE|wxTE_READONLY ); m_textCtrlData->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) ); m_textCtrlData->SetBackgroundColour( wxColour( 208, 208, 208 ) ); m_textCtrlData->SetToolTip( _("Total amount of data that will be transferred") ); diff --git a/ui/gui_status_handler.cpp b/ui/gui_status_handler.cpp index 43293829..c940c76e 100644 --- a/ui/gui_status_handler.cpp +++ b/ui/gui_status_handler.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "gui_status_handler.h" #include "small_dlgs.h" #include "msg_popup.h" diff --git a/ui/gui_status_handler.h b/ui/gui_status_handler.h index 45d429b1..b7fbd9be 100644 --- a/ui/gui_status_handler.h +++ b/ui/gui_status_handler.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef GUISTATUSHANDLER_H_INCLUDED #define GUISTATUSHANDLER_H_INCLUDED diff --git a/ui/main_dlg.cpp b/ui/main_dlg.cpp index 2256329a..197784bd 100644 --- a/ui/main_dlg.cpp +++ b/ui/main_dlg.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "main_dlg.h" #include <iterator> #include <stdexcept> @@ -52,9 +52,11 @@ #include "../shared/check_exist.h" #include "../library/lock_holder.h" #include "../shared/shell_execute.h" +#include "../shared/localization.h" using namespace zen; + namespace { struct wxClientDataString : public wxClientData //we need a wxClientData derived class to tell wxWidgets to take object ownership! @@ -346,7 +348,7 @@ MainDialog::MainDialog(const wxString& cfgFileName, xmlAccess::XmlGlobalSettings if (filenames.empty()) { - if (fileExists(toZ(lastRunConfigName()))) //3. try to load auto-save config + if (zen::fileExists(zen::toZ(lastRunConfigName()))) //3. try to load auto-save config filenames.push_back(lastRunConfigName()); } } @@ -542,18 +544,19 @@ void MainDialog::init(const xmlAccess::XmlGuiConfig guiCfg, #endif //create language selection menu - for (std::vector<ExistingTranslations::Entry>::const_iterator i = ExistingTranslations::get().begin(); i != ExistingTranslations::get().end(); ++i) + std::for_each(zen::ExistingTranslations::get().begin(), zen::ExistingTranslations::get().end(), + [&](const zen::ExistingTranslations::Entry& entry) { - wxMenuItem* newItem = new wxMenuItem(m_menuLanguages, wxID_ANY, i->languageName, wxEmptyString, wxITEM_NORMAL ); - newItem->SetBitmap(GlobalResources::instance().getImage(i->languageFlag)); + wxMenuItem* newItem = new wxMenuItem(m_menuLanguages, wxID_ANY, entry.languageName, wxEmptyString, wxITEM_NORMAL ); + newItem->SetBitmap(GlobalResources::instance().getImage(entry.languageFlag)); //map menu item IDs with language IDs: evaluated when processing event handler - languageMenuItemMap.insert(std::map<MenuItemID, LanguageID>::value_type(newItem->GetId(), i->languageID)); + languageMenuItemMap.insert(std::map<MenuItemID, LanguageID>::value_type(newItem->GetId(), entry.languageID)); //connect event - Connect(newItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnMenuLanguageSwitch)); + this->Connect(newItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnMenuLanguageSwitch)); m_menuLanguages->Append(newItem); - } + }); //support for CTRL + C and DEL on grids m_gridLeft ->Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(MainDialog::onGridLeftButtonEvent), NULL, this); @@ -1030,43 +1033,6 @@ void MainDialog::deleteSelectedFiles(const std::set<size_t>& viewSelectionLeft, } -template <SelectedSide side> -void exstractNames(const FileSystemObject& fsObj, wxString& name, wxString& dir) -{ - if (!fsObj.isEmpty<side>()) - { - struct GetNames : public FSObjectVisitor - { - GetNames(wxString& nameIn, wxString& dirIn) : name_(nameIn), dir_(dirIn) {} - virtual void visit(const FileMapping& fileObj) - { - name_ = toWx(fileObj.getFullName<side>()); - dir_ = toWx(fileObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); - } - virtual void visit(const SymLinkMapping& linkObj) - { - name_ = toWx(linkObj.getFullName<side>()); - dir_ = toWx(linkObj.getFullName<side>().BeforeLast(FILE_NAME_SEPARATOR)); - } - virtual void visit(const DirMapping& dirObj) - { - dir_ = name_ = toWx(dirObj.getFullName<side>()); - } - - wxString& name_; - wxString& dir_; - ; - } getNames(name, dir); - fsObj.accept(getNames); - } - else - { - name.clear(); - dir.clear(); - } -} - - void MainDialog::openExternalApplication(const wxString& commandline) { if (m_gridLeft->isLeadGrid() || m_gridRight->isLeadGrid()) @@ -1082,66 +1048,85 @@ void MainDialog::openExternalApplication(const wxString& commandline) } +template <SelectedSide side> +wxString extractLastValidDir(const FileSystemObject& fsObj) +{ + Zstring fullname = fsObj.getBaseDirPf<side>() + fsObj.getObjRelativeName(); //full name even if FileSystemObject::isEmpty<side>() == true + + while (!fullname.empty() && !dirExists(fullname)) //bad algorithm: this one should better retrieve the status from fsObj + fullname = beforeLast(fullname, FILE_NAME_SEPARATOR); + + return toWx(fullname); +} + +bool tryReplace(const wxString& phrase, const wxString& replacement, wxString& command) //return false on error +{ + if (command.find(phrase) != wxString::npos) + { + if (replacement.empty()) + return false; + replace(command, phrase, replacement); + } + return true; +} + + void MainDialog::openExternalApplication(size_t rowNumber, bool leftSide, const wxString& commandline) { if (commandline.empty()) return; - wxString command = commandline; - wxString name; - wxString dir; wxString nameCo; + wxString dir; wxString dirCo; - const FileSystemObject* fsObj = gridDataView->getObject(rowNumber); - if (fsObj) { - if (leftSide) - { - exstractNames<LEFT_SIDE>( *fsObj, name, dir); - exstractNames<RIGHT_SIDE>(*fsObj, nameCo, dirCo); - } - else - { - exstractNames<RIGHT_SIDE>(*fsObj, name, dir); - exstractNames<LEFT_SIDE>( *fsObj, nameCo, dirCo); - } -#ifdef FFS_WIN - if (name.empty()) + const FileSystemObject* fsObj = gridDataView->getObject(rowNumber); + if (fsObj) { - if (leftSide) - zen::shellExecute(wxString(L"\"") + fsObj->getBaseDirPf<LEFT_SIDE>() + "\""); - //zen::shellExecute(wxString(wxT("explorer ")) + L"\"" + zToWx(fsObj->getBaseDirPf<LEFT_SIDE>()) + L"\""); - else - zen::shellExecute(wxString(L"\"") + fsObj->getBaseDirPf<RIGHT_SIDE>() + "\""); - //zen::shellExecute(wxString(wxT("explorer ")) + L"\"" + zToWx(fsObj->getBaseDirPf<RIGHT_SIDE>()) + L"\""); - return; + name = toWx(fsObj->getFullName<LEFT_SIDE>()); //empty if obj not existing + dir = toWx(beforeLast(fsObj->getFullName<LEFT_SIDE>(), FILE_NAME_SEPARATOR)); //small issue: if obj does not exist but parent exists, this one erronously returns empty + + nameCo = toWx(fsObj->getFullName<RIGHT_SIDE>()); + dirCo = toWx(beforeLast(fsObj->getFullName<RIGHT_SIDE>(), FILE_NAME_SEPARATOR)); } -#endif } - else + + if (!leftSide) { - //fallback - dir = toWx(zen::getFormattedDirectoryName(toZ(firstFolderPair->getLeftDir()))); - dirCo = toWx(zen::getFormattedDirectoryName(toZ(firstFolderPair->getRightDir()))); + std::swap(name, nameCo); + std::swap(dir, dirCo); + } - if (!leftSide) - std::swap(dir, dirCo); + wxString command = commandline; + if (tryReplace(L"%nameCo", nameCo, command) && //attention: replace %nameCo, %dirCo BEFORE %name, %dir to handle dependency + tryReplace(L"%dirCo", dirCo, command) && + tryReplace(L"%name", name, command) && + tryReplace(L"%dir", dir, command)) + zen::shellExecute(command); + else //fallback + { + wxString fallbackDir; + const FileSystemObject* fsObj = gridDataView->getObject(rowNumber); + if (fsObj) + { + fallbackDir = leftSide ? + extractLastValidDir<LEFT_SIDE>(*fsObj) : + extractLastValidDir<RIGHT_SIDE>(*fsObj); + } + + if (fallbackDir.empty()) + fallbackDir = leftSide ? + toWx(zen::getFormattedDirectoryName(toZ(firstFolderPair->getLeftDir()))) : + toWx(zen::getFormattedDirectoryName(toZ(firstFolderPair->getRightDir()))); #ifdef FFS_WIN - zen::shellExecute(wxString(L"\"") + dir + L"\""); //default - //zen::shellExecute(wxString(wxT("explorer ")) + L"\"" + dir + L"\""); //default - return; + zen::shellExecute(wxString(L"\"") + fallbackDir + L"\""); //default +#elif defined FFS_LINUX + zen::shellExecute(wxString(L"xdg-open \"") + fallbackDir + L"\""); //default #endif } - - command.Replace(wxT("%nameCo"), nameCo, true); //attention: replace %nameCo, %dirCo BEFORE %name, %dir to handle dependency - command.Replace(wxT("%dirCo"), dirCo, true); - command.Replace(wxT("%name"), name, true); - command.Replace(wxT("%dir"), dir, true); - - zen::shellExecute(command); } @@ -1157,7 +1142,7 @@ void MainDialog::pushStatusInformation(const wxString& text) void MainDialog::clearStatusBar() { - while (stackObjects.size() > 0) + while (!stackObjects.empty()) stackObjects.pop(); m_staticTextStatusMiddle->SetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)); //reset color @@ -1188,7 +1173,6 @@ void MainDialog::disableAllElements(bool enableAbort) if (enableAbort) { - //show abort button m_buttonAbort->Enable(); m_buttonAbort->Show(); @@ -1907,8 +1891,8 @@ void MainDialog::OnContextExcludeExtension(wxCommandEvent& event) updateFilterButtons(); //do not fully apply filter, just exclude new items - std::for_each(gridDataView->getDataTentative().begin(), gridDataView->getDataTentative().end(), - [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); + std::for_each(gridDataView->getDataTentative().begin(), gridDataView->getDataTentative().end(), + [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); //applyFiltering(getConfig().mainCfg, gridDataView->getDataTentative()); updateGuiGrid(); @@ -1950,8 +1934,8 @@ void MainDialog::OnContextExcludeObject(wxCommandEvent& event) updateFilterButtons(); //do not fully apply filter, just exclude new items - std::for_each(gridDataView->getDataTentative().begin(), gridDataView->getDataTentative().end(), - [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); + std::for_each(gridDataView->getDataTentative().begin(), gridDataView->getDataTentative().end(), + [&](BaseDirMapping& baseMap) { addHardFiltering(baseMap, newExclude); }); //applyFiltering(getConfig().mainCfg, gridDataView->getDataTentative()); updateGuiGrid(); @@ -2555,11 +2539,12 @@ bool MainDialog::saveOldConfig() //return false on user abort &dontShowAgain)) { case ReturnQuestionDlg::BUTTON_YES: - if (!trySaveConfig()) - return false; - break; + return trySaveConfig(); case ReturnQuestionDlg::BUTTON_NO: globalSettings->optDialogs.popupOnConfigChange = !dontShowAgain; + //by choosing "no" user actively discards current config selection + //this ensures next app start will load <last session> instead of the original non-modified config selection + setLastUsedConfig(std::vector<wxString>(), getConfig()); break; case ReturnQuestionDlg::BUTTON_CANCEL: return false; @@ -3258,7 +3243,6 @@ void MainDialog::updateStatistics() void MainDialog::OnSwitchView(wxCommandEvent& event) { - //toggle view syncPreview->enablePreview(!syncPreview->previewIsEnabled()); } diff --git a/ui/main_dlg.h b/ui/main_dlg.h index 47adf4a9..44d7ef62 100644 --- a/ui/main_dlg.h +++ b/ui/main_dlg.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef MAINDIALOG_H #define MAINDIALOG_H @@ -254,27 +254,27 @@ private: xmlAccess::XmlGlobalSettings* globalSettings; //always bound //UI view of FolderComparison structure - std::auto_ptr<zen::GridView> gridDataView; + std::unique_ptr<zen::GridView> gridDataView; //------------------------------------- //functional configuration xmlAccess::XmlGuiConfig currentCfg; //folder pairs: - std::auto_ptr<DirectoryPairFirst> firstFolderPair; //always bound!!! + std::unique_ptr<DirectoryPairFirst> firstFolderPair; //always bound!!! std::vector<DirectoryPair*> additionalFolderPairs; //additional pairs to the first pair //------------------------------------- //*********************************************** - std::auto_ptr<wxMenu> contextMenu; + std::unique_ptr<wxMenu> contextMenu; //status information wxLongLong lastStatusChange; std::stack<wxString> stackObjects; //compare status panel (hidden on start, shown when comparing) - std::auto_ptr<CompareStatus> compareStatus; //always bound + std::unique_ptr<CompareStatus> compareStatus; //always bound bool cleanedUp; @@ -283,7 +283,7 @@ private: const wxGrid* lastSortGrid; //update icons periodically: one updater instance for both left and right grids - std::auto_ptr<IconUpdater> updateFileIcons; + std::unique_ptr<IconUpdater> updateFileIcons; bool processingGlobalKeyEvent; //indicator to notify recursion in OnGlobalKeyEvent() @@ -304,7 +304,7 @@ private: bool syncPreviewEnabled; //toggle to display configuration preview instead of comparison result bool synchronizationEnabled; //determines whether synchronization should be allowed }; - std::auto_ptr<SyncPreview> syncPreview; //always bound + std::unique_ptr<SyncPreview> syncPreview; //always bound wxAuiManager auiMgr; //implement dockable GUI design diff --git a/ui/msg_popup.cpp b/ui/msg_popup.cpp index 7e35e19b..900ab683 100644 --- a/ui/msg_popup.cpp +++ b/ui/msg_popup.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "msg_popup.h" #include "../library/resources.h" #include "../shared/mouse_move_dlg.h" diff --git a/ui/msg_popup.h b/ui/msg_popup.h index f6c2e5d2..b6e1a1a7 100644 --- a/ui/msg_popup.h +++ b/ui/msg_popup.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef MESSAGEPOPUP_H_INCLUDED #define MESSAGEPOPUP_H_INCLUDED diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp index e244877a..3cf78fbb 100644 --- a/ui/progress_indicator.cpp +++ b/ui/progress_indicator.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "progress_indicator.h" #include <memory> #include "gui_generated.h" @@ -19,10 +19,7 @@ #include "../shared/mouse_move_dlg.h" #include "../library/error_log.h" #include "../shared/toggle_button.h" - -#ifdef FFS_WIN #include "../shared/taskbar.h" -#endif using namespace zen; @@ -97,9 +94,7 @@ private: CurrentStatus status; -#ifdef FFS_WIN - std::auto_ptr<util::TaskbarProgress> taskbar_; -#endif + std::unique_ptr<util::Taskbar> taskbar_; //remaining time std::auto_ptr<Statistics> statistics; @@ -179,13 +174,11 @@ void CompareStatus::CompareStatusImpl::init() { titleTextBackup = parentWindow_.GetTitle(); -#ifdef FFS_WIN - try //try to get access to Windows 7 Taskbar + try //try to get access to Windows 7/Ubuntu taskbar { - taskbar_.reset(new util::TaskbarProgress(parentWindow_)); + taskbar_.reset(new util::Taskbar(parentWindow_)); } catch (const util::TaskbarNotAvailable&) {} -#endif status = SCANNING; @@ -223,10 +216,7 @@ void CompareStatus::CompareStatusImpl::init() void CompareStatus::CompareStatusImpl::finalize() //hide again { -#ifdef FFS_WIN taskbar_.reset(); -#endif - parentWindow_.SetTitle(titleTextBackup); } @@ -288,7 +278,6 @@ void CompareStatus::CompareStatusImpl::showProgressExternally(const wxString& pr parentWindow_.SetTitle(progressText); //show progress on Windows 7 taskbar -#ifdef FFS_WIN using namespace util; if (taskbar_.get()) @@ -298,15 +287,14 @@ void CompareStatus::CompareStatusImpl::showProgressExternally(const wxString& pr switch (status) { case SCANNING: - taskbar_->setStatus(TaskbarProgress::STATUS_INDETERMINATE); + taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); break; case COMPARING_CONTENT: - taskbar_->setStatus(TaskbarProgress::STATUS_NORMAL); + taskbar_->setStatus(Taskbar::STATUS_NORMAL); taskbar_->setProgress(current, total); break; } } -#endif } @@ -489,7 +477,7 @@ public: void incProgressIndicator_NoUpdate(int objectsProcessed, zen::Int64 dataProcessed); void incScannedObjects_NoUpdate(int number); void setStatusText_NoUpdate(const wxString& text); - void updateStatusDialogNow(); + void updateStatusDialogNow(bool allowYield = true); void setCurrentStatus(SyncStatus::SyncStatusID id); void processHasFinished(SyncStatus::SyncStatusID id, const ErrorLogging& log); //essential to call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater @@ -505,6 +493,8 @@ private: virtual void OnIconize(wxIconizeEvent& event); void resumeFromSystray(); + void OnResumeFromTray(wxCommandEvent& event); + bool currentProcessIsRunning(); void showProgressExternally(const wxString& progressText, float percent = 0); //percent may already be included in progressText @@ -528,9 +518,7 @@ private: bool processPaused; SyncStatus::SyncStatusID currentStatus; -#ifdef FFS_WIN - std::unique_ptr<util::TaskbarProgress> taskbar_; -#endif + std::unique_ptr<util::Taskbar> taskbar_; //remaining time std::unique_ptr<Statistics> statistics; @@ -539,11 +527,7 @@ private: wxString titelTextBackup; - //save last used systray icon description - wxString progressTextLast; - float progressPercentLast; - - std::shared_ptr<MinimizeToTray> minimizedToSysTray; //optional: if filled, hides all visible windows, shows again if destroyed + std::unique_ptr<FfsTrayIcon> trayIcon; //optional: if filled all other windows should be hidden and conversely }; @@ -560,7 +544,7 @@ SyncStatus::SyncStatus(AbortCallback& abortCb, else { pimpl->Show(); - pimpl->updateStatusDialogNow(); //update visual statistics to get rid of "dummy" texts + pimpl->updateStatusDialogNow(false); //update visual statistics to get rid of "dummy" texts } } @@ -639,8 +623,7 @@ SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, processPaused(false), currentStatus(SyncStatus::ABORTED), lastStatCallSpeed(-1000000), //some big number - lastStatCallRemTime(-1000000), - progressPercentLast(0) + lastStatCallRemTime(-1000000) { #ifdef FFS_WIN new zen::MouseMoveWindow(*this); //allow moving main dialog by clicking (nearly) anywhere...; ownership passed to "this" @@ -670,13 +653,11 @@ SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, timeElapsed.Start(); //measure total time -#ifdef FFS_WIN - try //try to get access to Windows 7 Taskbar + try //try to get access to Windows 7/Ubuntu taskbar { - taskbar_.reset(new util::TaskbarProgress(mainDialog != NULL ? *static_cast<wxTopLevelWindow*>(mainDialog) : *this)); + taskbar_.reset(new util::Taskbar(mainDialog != NULL ? *static_cast<wxTopLevelWindow*>(mainDialog) : *this)); } catch (const util::TaskbarNotAvailable&) {} -#endif //hide "processed" statistics until end of process bSizerObjectsProcessed->Show(false); @@ -703,9 +684,6 @@ SyncStatus::SyncStatusImpl::~SyncStatusImpl() mainDialog->Raise(); mainDialog->SetFocus(); } - - if (minimizedToSysTray.get()) - minimizedToSysTray->keepHidden(); //prevent window from flashing shortly before it is destroyed } @@ -782,13 +760,8 @@ void SyncStatus::SyncStatusImpl::setStatusText_NoUpdate(const wxString& text) void SyncStatus::SyncStatusImpl::showProgressExternally(const wxString& progressText, float percent) { //write status information to systray, if window is minimized - if (minimizedToSysTray.get()) - minimizedToSysTray->setToolTip(progressText, percent); - //minimizedToSysTray may be a zombie... so set title text anyway - - //save progress text for later use (e.g. set systray icon tooltip in paused mode) - progressTextLast = progressText; - progressPercentLast = percent; + if (trayIcon.get()) + trayIcon->setToolTip(progressText, percent); wxString progressTextFmt = progressText; progressTextFmt.Replace(wxT("\n"), wxT(" - ")); @@ -804,7 +777,7 @@ void SyncStatus::SyncStatusImpl::showProgressExternally(const wxString& progress this->SetTitle(progressTextFmt); } -#ifdef FFS_WIN + using namespace util; //show progress on Windows 7 taskbar @@ -816,30 +789,29 @@ void SyncStatus::SyncStatusImpl::showProgressExternally(const wxString& progress switch (currentStatus) { case SyncStatus::SCANNING: - taskbar_->setStatus(TaskbarProgress::STATUS_INDETERMINATE); + taskbar_->setStatus(Taskbar::STATUS_INDETERMINATE); break; case SyncStatus::FINISHED_WITH_SUCCESS: case SyncStatus::COMPARING_CONTENT: case SyncStatus::SYNCHRONIZING: - taskbar_->setStatus(TaskbarProgress::STATUS_NORMAL); + taskbar_->setStatus(Taskbar::STATUS_NORMAL); taskbar_->setProgress(current, total); break; case SyncStatus::PAUSE: - taskbar_->setStatus(TaskbarProgress::STATUS_PAUSED); + taskbar_->setStatus(Taskbar::STATUS_PAUSED); taskbar_->setProgress(current, total); break; case SyncStatus::ABORTED: case SyncStatus::FINISHED_WITH_ERROR: - taskbar_->setStatus(TaskbarProgress::STATUS_ERROR); + taskbar_->setStatus(Taskbar::STATUS_ERROR); taskbar_->setProgress(current, total); break; } } -#endif } -void SyncStatus::SyncStatusImpl::updateStatusDialogNow() +void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) { //static RetrieveStatistics statistic; //statistic.writeEntry(currentData.ToDouble(), currentObjects); @@ -940,27 +912,29 @@ void SyncStatus::SyncStatusImpl::updateStatusDialogNow() } } - - //support for pause button - if(processPaused) + if (allowYield) { - if (statistics.get()) statistics->pauseTimer(); - - while (processPaused && currentProcessIsRunning()) + //support for pause button + if(processPaused) { - wxMilliSleep(UI_UPDATE_INTERVAL); - updateUiNow(); + if (statistics.get()) statistics->pauseTimer(); + + while (processPaused && currentProcessIsRunning()) + { + wxMilliSleep(UI_UPDATE_INTERVAL); + updateUiNow(); + } + + if (statistics.get()) statistics->resumeTimer(); } - if (statistics.get()) statistics->resumeTimer(); + /* + /|\ + | keep this order to ensure one full statistics update before entering pause mode + \|/ + */ + updateUiNow(); } - - /* - /|\ - | keep this order to ensure one full statistics update before entering pause mode - \|/ - */ - updateUiNow(); } @@ -1057,7 +1031,7 @@ void SyncStatus::SyncStatusImpl::processHasFinished(SyncStatus::SyncStatusID id, m_staticTextDataProcessed->SetLabel(zen::formatFilesizeToShortString(to<zen::UInt64>(currentData))); } - updateStatusDialogNow(); //keep this sequence to avoid display distortion, if e.g. only 1 item is sync'ed + updateStatusDialogNow(false); //keep this sequence to avoid display distortion, if e.g. only 1 item is sync'ed //hide progress text control and show log control instead m_textCtrlInfo->Hide(); @@ -1140,17 +1114,51 @@ void SyncStatus::SyncStatusImpl::OnIconize(wxIconizeEvent& event) { if (event.IsIconized()) //ATTENTION: iconize event is also triggered on "Restore"! (at least under Linux) minimizeToTray(); + else + resumeFromSystray(); //may be initiated by "show desktop" although all windows are hidden! +} + + +void SyncStatus::SyncStatusImpl::OnResumeFromTray(wxCommandEvent& event) +{ + resumeFromSystray(); } void SyncStatus::SyncStatusImpl::minimizeToTray() { - minimizedToSysTray.reset(new MinimizeToTray(this, mainDialog)); - minimizedToSysTray->setToolTip(progressTextLast, progressPercentLast); //set tooltip: in pause mode there is no statistics update, so this is the only chance + if (!trayIcon.get()) + { + trayIcon.reset(new FfsTrayIcon); + trayIcon->Connect(FFS_REQUEST_RESUME_TRAY_EVENT, wxCommandEventHandler(SyncStatus::SyncStatusImpl::OnResumeFromTray), NULL, this); + //tray icon has shorter lifetime than this => no need to disconnect event later + } + + updateStatusDialogNow(false); //set tooltip: in pause mode there is no statistics update, so this is the only chance + + Hide(); + if (mainDialog) + mainDialog->Hide(); } void SyncStatus::SyncStatusImpl::resumeFromSystray() { - minimizedToSysTray.reset(); + trayIcon.reset(); + + if (mainDialog) + { + if (mainDialog->IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! + mainDialog->Iconize(false); + mainDialog->Show(); + mainDialog->Raise(); + } + + if (IsIconized()) //caveat: if window is maximized calling Iconize(false) will erroneously un-maximize! + Iconize(false); + Show(); + Raise(); + SetFocus(); + + updateStatusDialogNow(false); //restore Windows 7 task bar status (e.g. required in pause mode) } diff --git a/ui/progress_indicator.h b/ui/progress_indicator.h index 9bb0eed0..71a975f6 100644 --- a/ui/progress_indicator.h +++ b/ui/progress_indicator.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef PROGRESSINDICATOR_H_INCLUDED #define PROGRESSINDICATOR_H_INCLUDED diff --git a/ui/search.cpp b/ui/search.cpp index 7fdaec03..d0b24299 100644 --- a/ui/search.cpp +++ b/ui/search.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "search.h" #include "gui_generated.h" #include <wx/msgdlg.h> diff --git a/ui/search.h b/ui/search.h index ce7e479a..93c3159f 100644 --- a/ui/search.h +++ b/ui/search.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED diff --git a/ui/small_dlgs.cpp b/ui/small_dlgs.cpp index ed28a166..ee82bbcc 100644 --- a/ui/small_dlgs.cpp +++ b/ui/small_dlgs.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "gui_generated.h" #include "small_dlgs.h" #include "msg_popup.h" diff --git a/ui/small_dlgs.h b/ui/small_dlgs.h index 31fd4548..93c90445 100644 --- a/ui/small_dlgs.h +++ b/ui/small_dlgs.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SMALLDIALOGS_H_INCLUDED #define SMALLDIALOGS_H_INCLUDED diff --git a/ui/sorting.h b/ui/sorting.h index d2bafd1d..fa4e9973 100644 --- a/ui/sorting.h +++ b/ui/sorting.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SORTING_H_INCLUDED #define SORTING_H_INCLUDED diff --git a/ui/switch_to_gui.cpp b/ui/switch_to_gui.cpp index 4bfdfd1f..7bd28c59 100644 --- a/ui/switch_to_gui.cpp +++ b/ui/switch_to_gui.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "switch_to_gui.h" #include "main_dlg.h" diff --git a/ui/switch_to_gui.h b/ui/switch_to_gui.h index 03e18324..cbbff20c 100644 --- a/ui/switch_to_gui.h +++ b/ui/switch_to_gui.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SWITCHTOGUI_H_INCLUDED #define SWITCHTOGUI_H_INCLUDED diff --git a/ui/sync_cfg.cpp b/ui/sync_cfg.cpp index 59d4d689..5e78e97a 100644 --- a/ui/sync_cfg.cpp +++ b/ui/sync_cfg.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "sync_cfg.h" #include "../library/resources.h" #include "../shared/dir_name.h" diff --git a/ui/sync_cfg.h b/ui/sync_cfg.h index bd52984b..7612d29b 100644 --- a/ui/sync_cfg.h +++ b/ui/sync_cfg.h @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef SYNCCONFIG_H_INCLUDED #define SYNCCONFIG_H_INCLUDED diff --git a/ui/tray_icon.cpp b/ui/tray_icon.cpp index 278f1888..90893c9f 100644 --- a/ui/tray_icon.cpp +++ b/ui/tray_icon.cpp @@ -3,7 +3,7 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #include "tray_icon.h" #include "../library/resources.h" #include "small_dlgs.h" @@ -14,6 +14,10 @@ #include <wx/menu.h> #include <wx/icon.h> //req. by Linux + +const wxEventType FFS_REQUEST_RESUME_TRAY_EVENT = wxNewEventType(); + + namespace { inline @@ -22,7 +26,6 @@ int roundNum(double d) //little rounding function return static_cast<int>(d < 0 ? d - .5 : d + .5); } - void fillRange(wxImage& img, int pixelFirst, int pixelLast, const wxColor& col) { const int pixelCount = img.GetWidth() >= 0 ? img.GetWidth() * img.GetHeight() : -1; @@ -147,15 +150,13 @@ enum Selection }; -class MinimizeToTray::TaskBarImpl : public wxTaskBarIcon +class FfsTrayIcon::TaskBarImpl : public wxTaskBarIcon { public: - TaskBarImpl(MinimizeToTray* parent) : parent_(parent) {} + TaskBarImpl(FfsTrayIcon& parent) : parent_(&parent) {} + + void parentHasDied() { parent_ = NULL; } - void parentHasDied() - { - parent_ = NULL; - } private: virtual wxMenu* CreatePopupMenu() { @@ -167,96 +168,67 @@ private: contextMenu->AppendSeparator(); contextMenu->Append(CONTEXT_RESTORE, _("&Restore")); //event handling - contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MinimizeToTray::OnContextMenuSelection), NULL, parent_); + contextMenu->Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(FfsTrayIcon::OnContextMenuSelection), NULL, parent_); return contextMenu; //ownership transferred to caller } - MinimizeToTray* parent_; + FfsTrayIcon* parent_; }; -MinimizeToTray::MinimizeToTray(wxTopLevelWindow* callerWnd, wxTopLevelWindow* secondWnd) : - callerWnd_(callerWnd), - secondWnd_(secondWnd), - trayIcon(new TaskBarImpl(this)) +FfsTrayIcon::FfsTrayIcon() : + trayIcon(new TaskBarImpl(*this)) { trayIcon->SetIcon(generateIcon(0), wxT("FreeFileSync")); - trayIcon->Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(MinimizeToTray::OnDoubleClick), NULL, this); //register double-click - - if (callerWnd_) - callerWnd_->Hide(); - if (secondWnd_) - secondWnd_->Hide(); + trayIcon->Connect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(FfsTrayIcon::OnDoubleClick), NULL, this); //register double-click } -MinimizeToTray::~MinimizeToTray() +FfsTrayIcon::~FfsTrayIcon() { - resumeFromTray(); -} + trayIcon->RemoveIcon(); //hide icon until final deletion takes place + trayIcon->Disconnect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(FfsTrayIcon::OnDoubleClick), NULL, this); + trayIcon->parentHasDied(); //TaskBarImpl (potentially) has longer lifetime than FfsTrayIcon: avoid callback! - -void MinimizeToTray::resumeFromTray() //remove trayIcon and restore windows: MinimizeToTray is now a zombie object... -{ - if (trayIcon) - { - if (secondWnd_) - { - secondWnd_->Iconize(false); - secondWnd_->Show(); - } - - if (callerWnd_) //usecase: avoid dialog flashing in batch silent mode - { - callerWnd_->Iconize(false); - callerWnd_->Show(); - callerWnd_->Raise(); - callerWnd_->SetFocus(); - } - trayIcon->RemoveIcon(); //hide icon until final deletion takes place - trayIcon->Disconnect(wxEVT_TASKBAR_LEFT_DCLICK, wxCommandEventHandler(MinimizeToTray::OnDoubleClick), NULL, this); - trayIcon->parentHasDied(); //TaskBarImpl (potentially) has longer lifetime than MinimizeToTray: avoid callback! - - //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking) - if (!wxPendingDelete.Member(trayIcon)) - wxPendingDelete.Append(trayIcon); - - trayIcon = NULL; //avoid reentrance - } -} - - -void MinimizeToTray::setToolTip(const wxString& toolTipText, double percent) -{ - if (trayIcon) - trayIcon->SetIcon(generateIcon(percent), toolTipText); + //use wxWidgets delayed destruction: delete during next idle loop iteration (handle late window messages, e.g. when double-clicking) + if (!wxPendingDelete.Member(trayIcon)) + wxPendingDelete.Append(trayIcon); } -void MinimizeToTray::keepHidden() +void FfsTrayIcon::setToolTip(const wxString& toolTipText, double percent) { - callerWnd_ = NULL; - secondWnd_ = NULL; + trayIcon->SetIcon(generateIcon(percent), toolTipText); } -void MinimizeToTray::OnContextMenuSelection(wxCommandEvent& event) +void FfsTrayIcon::OnContextMenuSelection(wxCommandEvent& event) { const Selection eventId = static_cast<Selection>(event.GetId()); switch (eventId) { case CONTEXT_ABOUT: + { + //ATTENTION: the modal dialog below does NOT disable all GUI input, e.g. user may still double-click on tray icon + //which will implicitly destroy the tray icon while still showing the modal dialog + trayIcon->SetEvtHandlerEnabled(false); zen::showAboutDialog(); - break; + trayIcon->SetEvtHandlerEnabled(true); + } + break; case CONTEXT_RESTORE: - resumeFromTray(); + { + wxCommandEvent dummy(FFS_REQUEST_RESUME_TRAY_EVENT); + ProcessEvent(dummy); + } } } -void MinimizeToTray::OnDoubleClick(wxCommandEvent& event) +void FfsTrayIcon::OnDoubleClick(wxCommandEvent& event) { - resumeFromTray(); + wxCommandEvent dummy(FFS_REQUEST_RESUME_TRAY_EVENT); + ProcessEvent(dummy); } diff --git a/ui/tray_icon.h b/ui/tray_icon.h index 8b5b5851..a615c091 100644 --- a/ui/tray_icon.h +++ b/ui/tray_icon.h @@ -3,30 +3,31 @@ // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** -// + #ifndef TRAYICON_H_INCLUDED #define TRAYICON_H_INCLUDED #include <wx/event.h> -#include <wx/toplevel.h> +//show tray icon with progress during lifetime of this instance +//emits the following wxCommandEvent in case user double-clicks on tray icon or selects corresponding context menu item: +extern const wxEventType FFS_REQUEST_RESUME_TRAY_EVENT; -class MinimizeToTray : private wxEvtHandler +class FfsTrayIcon : public wxEvtHandler { public: - MinimizeToTray(wxTopLevelWindow* callerWnd, wxTopLevelWindow* secondWnd = NULL); //ensure both windows have longer lifetime than this instance! - ~MinimizeToTray(); //show windows again + FfsTrayIcon(); + ~FfsTrayIcon(); void setToolTip(const wxString& toolTipText, double percent = 0); //percent (optional), number between [0, 100], for small progress indicator - void keepHidden(); //do not show windows again: avoid window flashing shortly before it is destroyed private: + FfsTrayIcon(const FfsTrayIcon&); + FfsTrayIcon& operator=(const FfsTrayIcon&); + void OnContextMenuSelection(wxCommandEvent& event); void OnDoubleClick(wxCommandEvent& event); - void resumeFromTray(); - wxTopLevelWindow* callerWnd_; - wxTopLevelWindow* secondWnd_; class TaskBarImpl; TaskBarImpl* trayIcon; //actual tray icon (don't use inheritance to enable delayed deletion) }; diff --git a/version/version.h b/version/version.h index 16eeb18d..cf4dbbb6 100644 --- a/version/version.h +++ b/version/version.h @@ -2,5 +2,5 @@ namespace zen { -const wxString currentVersion = wxT("3.19"); //internal linkage! + const wxString currentVersion = wxT("3.20"); //internal linkage! } diff --git a/version/version.rc b/version/version.rc index fd3faf30..bb4c0b9a 100644 --- a/version/version.rc +++ b/version/version.rc @@ -1,2 +1,2 @@ -#define VER_FREEFILESYNC 3,19,0,0 -#define VER_FREEFILESYNC_STR "3.19\0" +#define VER_FREEFILESYNC 3,20,0,0 +#define VER_FREEFILESYNC_STR "3.20\0" |