summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:17:25 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:17:25 +0200
commit94e1d2e78b6ff92d5a1c971c277cb6ac792d1c70 (patch)
tree338d2ab72f79901f5d32c96d63cec36f30bcf44e
parent4.4 (diff)
downloadFreeFileSync-94e1d2e78b6ff92d5a1c971c277cb6ac792d1c70.tar.gz
FreeFileSync-94e1d2e78b6ff92d5a1c971c277cb6ac792d1c70.tar.bz2
FreeFileSync-94e1d2e78b6ff92d5a1c971c277cb6ac792d1c70.zip
4.5
-rw-r--r--BUILD/Changelog.txt8
-rw-r--r--BUILD/FreeFileSync.chmbin670693 -> 670693 bytes
-rw-r--r--BUILD/Languages/dutch.lng131
-rw-r--r--BUILD/Languages/finnish.lng9
-rw-r--r--BUILD/Resources.zipbin272855 -> 272946 bytes
-rw-r--r--file_hierarchy.cpp10
-rw-r--r--file_hierarchy.h4
-rw-r--r--lib/folder_history_box.cpp7
-rw-r--r--lib/folder_history_box.h6
-rw-r--r--lib/resolve_path.cpp13
-rw-r--r--ui/small_dlgs.cpp1
-rw-r--r--version/version.h2
-rw-r--r--version/version.rc4
-rw-r--r--zen/FindFilePlus/find_file_plus.cpp141
-rw-r--r--zen/FindFilePlus/find_file_plus.h2
-rw-r--r--zen/FindFilePlus/load_dll.h1
-rw-r--r--zen/file_handling.cpp10
-rw-r--r--zen/file_id_def.h10
-rw-r--r--zen/file_traverser.cpp135
19 files changed, 305 insertions, 189 deletions
diff --git a/BUILD/Changelog.txt b/BUILD/Changelog.txt
index 5e5d9053..7c9b654f 100644
--- a/BUILD/Changelog.txt
+++ b/BUILD/Changelog.txt
@@ -2,6 +2,14 @@
|FreeFileSync|
--------------
+Changelog v4.5
+--------------
+Fixed "Windows Error Code 50: The request is not supported"
+Fixed "Windows Error Code 124: The system call level is not correct"
+Fixed config load performance problem if network drive is not reachable
+Support traversing truly empty directories (no ., ..) (Windows)
+
+
Changelog v4.4
--------------
Fixed error copying files containing alternate data streams (Windows)
diff --git a/BUILD/FreeFileSync.chm b/BUILD/FreeFileSync.chm
index 04ae52a9..89f04679 100644
--- a/BUILD/FreeFileSync.chm
+++ b/BUILD/FreeFileSync.chm
Binary files differ
diff --git a/BUILD/Languages/dutch.lng b/BUILD/Languages/dutch.lng
index 8c9d8a33..ad09e3f5 100644
--- a/BUILD/Languages/dutch.lng
+++ b/BUILD/Languages/dutch.lng
@@ -1,6 +1,6 @@
<header>
<language name>Nederlands</language name>
- <translator>Rogier Wijker</translator>
+ <translator>Edwin Dierssen</translator>
<locale>nl_NL</locale>
<flag file>holland.png</flag file>
<plural forms>2</plural forms>
@@ -8,7 +8,7 @@
</header>
<source>Searching for directory %x...</source>
-<target></target>
+<target>Zoeken naar map %x...</target>
<source>Show in Explorer</source>
<target>Toon in de verkenner</target>
@@ -23,7 +23,7 @@
<target>RealtimeSync - Geautomatiseerde Synchronisatie</target>
<source>Select alternate comparison settings</source>
-<target></target>
+<target>Selecteer alternatieve vergelijkings instellingen</target>
<source>Select alternate synchronization settings</source>
<target>Selecteer alternatieve synchronisatie instellingen</target>
@@ -80,13 +80,13 @@
<target>Vind</target>
<source>Select time span</source>
-<target></target>
+<target>Selecteer tijdsspanne</target>
<source>Show pop-up</source>
-<target></target>
+<target>Laat pop-up zien</target>
<source>Show pop-up on errors or warnings</source>
-<target></target>
+<target>Laat pop-up zien bij foutmeldingen of waarschuwingen</target>
<source>Ignore errors</source>
<target>Negeer foutmeldingen</target>
@@ -110,7 +110,7 @@
<target>Fout tijdens schrijven naar synchronisatie-database:</target>
<source>Invalid command line: %x</source>
-<target></target>
+<target>Ongeldige opdrachtregel: %x</target>
<source>Windows Error Code %x:</source>
<target>Windows Fout Code %x:</target>
@@ -119,7 +119,7 @@
<target>Linux Fout Code %x:</target>
<source>Error resolving symbolic link:</source>
-<target>Fout tijdens opzoeken van symbolische koppeling:</target>
+<target>Fout tijdens opzoeken van snelkoppeling:</target>
<source>%x MB</source>
<target>%x MB</target>
@@ -179,7 +179,7 @@
<target>Opmaak van synchronisatie-database komt niet overeen:</target>
<source>Database files do not share a common synchronization session:</source>
-<target></target>
+<target>Database bestanden delen geen gezamelijke synchronisatie sessie:</target>
<source>An exception occurred!</source>
<target>Er heeft een uitzondering plaatsgevonden!</target>
@@ -224,7 +224,10 @@
<pluralform>[1 Thread]</pluralform>
<pluralform>[%x Threads]</pluralform>
</source>
-<target></target>
+<target>
+<pluralform>[1 Thread]</pluralform>
+<pluralform>[%x Threads]</pluralform>
+</target>
<source>Invalid FreeFileSync config file!</source>
<target>Foutief FreeFileSync configuratiebestand!</target>
@@ -239,7 +242,7 @@
<target>Kon een benodigde DLL niet laden:</target>
<source>Error accessing Volume Shadow Copy Service!</source>
-<target></target>
+<target>Fout bij toegang tot Volume Schaduwkopie Service!</target>
<source>Making shadow copies on WOW64 is not supported. Please use FreeFileSync 64-bit version.</source>
<target>Het aanmaken van schaduwkopieën op WOW64 wordt niet ondersteund. Gebruik alstublieft de 64-bit versie van FreeFileSync.</target>
@@ -517,7 +520,7 @@ De opdrachtregel wordt telkens uitgevoerd indien:
<target>Taaklijst</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 <ffs_batch file>. This can also be scheduled in your operating system's task planner.</source>
-<target></target>
+<target>Maak een batch bestand voor automatische synchronisatie. Om te starten in batch mode kunt u simpel dubbelklikken op het bestand of uitvoeren via de opdrachtregel: FreeFileSync.exe <ffs_batchbestand>. Dit kan ook worden gepland in de taakplanner van uw besturingssysteem.</target>
<source>Help</source>
<target>Help</target>
@@ -541,7 +544,7 @@ De opdrachtregel wordt telkens uitgevoerd indien:
<target>Status terugkoppeling</target>
<source>Run minimized</source>
-<target></target>
+<target>Geminimaliseerd uitvoeren</target>
<source>Maximum number of logfiles:</source>
<target>Maximale aantal van logbestanden:</target>
@@ -550,7 +553,7 @@ De opdrachtregel wordt telkens uitgevoerd indien:
<target>Selecteer een map voor het logbestand:</target>
<source>Batch settings</source>
-<target></target>
+<target>Batch instellingen</target>
<source>&Save</source>
<target>&Opslaan</target>
@@ -568,19 +571,19 @@ De opdrachtregel wordt telkens uitgevoerd indien:
<target><Automatisch></target>
<source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source>
-<target></target>
+<target>Identificeer en verspreid veranderingen aan beide kanten met behulp van een database. Verwijderingen, hernoemingen en conflicten worden automatisch gedetecteerd.</target>
<source>Mirror ->></source>
<target>Spiegelen ->></target>
<source>Mirror backup of left folder. Right folder is modified to exactly match left folder after synchronization.</source>
-<target>Spiegel backup van linker map. Rechter map is bewerk om precies hetzelfde te hebben na synchronisatie.</target>
+<target>Spiegel backup van linker map. Rechter map is bewerkt om na synchronisatie een exacte kopie te zijn van de linker map.</target>
<source>Update -></source>
<target>Bijwerken -></target>
<source>Copy new or updated files to right folder.</source>
-<target>Kopieer nieuwe of geupdate bestanden naar de rechter map.</target>
+<target>Kopiëer nieuwe of geupdate bestanden naar de rechter map.</target>
<source>Custom</source>
<target>Aangepast</target>
@@ -630,10 +633,15 @@ Files are found equal if
- file size
are the same
</source>
-<target></target>
+<target>
+Bestanden worden als gelijk bevonden indien,
+ - de laatste schrijf tijd en datum
+ - de bestandsgrootte
+gelijk zijn
+</target>
<source>File time and size</source>
-<target></target>
+<target>Bestands tijd-en grootte</target>
<source>
Files are found equal if
@@ -650,7 +658,7 @@ overeenkomt
<target>Bestandsinhoud</target>
<source>Symbolic Link handling</source>
-<target>Symbolische Koppeling afhandeling</target>
+<target>Afhandeling van snelkoppelingen</target>
<source>Synchronizing...</source>
<target>Synchroniseert...</target>
@@ -767,10 +775,10 @@ Uitsluiten: \stuff\temp\*
<target>Uitsluiten</target>
<source>Minimum file size</source>
-<target></target>
+<target>Minimale bestandsgrootte</target>
<source>Maximum file size</source>
-<target></target>
+<target>Maximale bestandsgrootte</target>
<source>&Default</source>
<target>&Standaard</target>
@@ -782,19 +790,19 @@ Uitsluiten: \stuff\temp\*
<target>Verplaats kolom naar beneden</target>
<source>Transactional file copy</source>
-<target></target>
+<target>Transactionele bestands kopie</target>
<source>Write to a temporary file (*.ffs_tmp) first then rename it. This guarantees a consistent state even in case of fatal error.</source>
-<target></target>
+<target>Schrijf eerst naar een tijdelijk bestand (*.ffs_tmp) en hernoem dan. Dit garandeert een consistente toestand, zelfs in het geval van een fatale fout.</target>
<source>Copy locked files</source>
-<target>Kopieer vergrendelde bestanden</target>
+<target>Kopiëer vergrendelde bestanden</target>
<source>Copy shared or locked files using Volume Shadow Copy Service (Requires Administrator rights)</source>
-<target>Kopieer gedeelde of vergrendelde bestanden met Volume Shadow Copy Service (Vereist Administrator rechten)</target>
+<target>Kopiëer gedeelde of vergrendelde bestanden met Volume Shadow Copy Service (Vereist Administrator rechten)</target>
<source>Copy file access permissions</source>
-<target></target>
+<target>Kopiëer toegangsrechten van bestand.</target>
<source>Transfer file and directory permissions (Requires Administrator rights)</source>
<target>Zet bestand en map permissies over (Vereist Administrator rechten)</target>
@@ -866,22 +874,22 @@ Uitsluiten: \stuff\temp\*
<target>Aanpassen...</target>
<source>Select time span...</source>
-<target></target>
+<target>Selecteer tijdsspanne...</target>
<source>Auto-adjust columns</source>
<target>Kolommen automatisch aanpassen</target>
<source>Icon size:</source>
-<target></target>
+<target>Icoon grootte:</target>
<source>Small</source>
-<target></target>
+<target>Klein</target>
<source>Medium</source>
-<target></target>
+<target>Middel</target>
<source>Large</source>
-<target></target>
+<target>Groot</target>
<source>Include all rows</source>
<target>Alle rijen opnemen</target>
@@ -989,10 +997,10 @@ Uitsluiten: \stuff\temp\*
<target>Toon bestanden die aan de rechterzijde overschreven zullen worden</target>
<source>Hide files that won't be copied</source>
-<target>Verberg bestanden die niet zullen worden gekopieerd</target>
+<target>Verberg bestanden die niet zullen worden gekopiëerd</target>
<source>Show files that won't be copied</source>
-<target>Toon bestanden die niet gekopieerd zullen worden</target>
+<target>Toon bestanden die niet gekopiëerd zullen worden</target>
<source>All directories in sync!</source>
<target>Alle mappen zijn gesynchroniseerd!</target>
@@ -1081,20 +1089,17 @@ Uitsluiten: \stuff\temp\*
<source>Inactive</source>
<target>Niet actief</target>
-<source>Last x hours</source>
-<target></target>
-
<source>Today</source>
-<target></target>
+<target>Vandaag</target>
<source>This week</source>
-<target></target>
+<target>Deze week</target>
<source>This month</source>
-<target></target>
+<target>Deze maand</target>
<source>This year</source>
-<target></target>
+<target>Dit jaar</target>
<source>Byte</source>
<target>Byte</target>
@@ -1118,7 +1123,7 @@ Uitsluiten: \stuff\temp\*
<target>Volg</target>
<source>Copy NTFS permissions</source>
-<target></target>
+<target>Kopiëer NTFS permissies</target>
<source>Integrate external applications into context menu. The following macros are available:</source>
<target>Integreer externe applicaties in het context menu. De volgende macros zijn beschikbaar:</target>
@@ -1256,7 +1261,7 @@ Uitsluiten: \stuff\temp\*
<target>Fout tijdens het aanmaken van map:</target>
<source>Error copying symbolic link:</source>
-<target>Fout tijdens kopiëren van symbolische koppeling:</target>
+<target>Fout tijdens kopiëren van snelkoppeling:</target>
<source>Error copying file:</source>
<target>Fout tijdens kopiëren van bestand:</target>
@@ -1316,7 +1321,7 @@ Uitsluiten: \stuff\temp\*
<target>Bestanden %x hebben dezelfde datums maar een afwijkende grootte!</target>
<source>Symlinks %x have the same date but a different target!</source>
-<target>Symbolische koppeling %x heeft dezelfde datum maar een ander doel!</target>
+<target>Snelkoppeling %x heeft dezelfde datum maar een ander doel!</target>
<source>Comparing content of files %x</source>
<target>De inhoud van %x bestanden wordt vergeleken</target>
@@ -1334,10 +1339,10 @@ Uitsluiten: \stuff\temp\*
<target>Bestanden/Mappen verschillen alleen in attributen</target>
<source>Copy new file/folder to left</source>
-<target>Kopieer nieuw bestand/map naar links</target>
+<target>Kopiëer nieuw bestand/map naar links</target>
<source>Copy new file/folder to right</source>
-<target>Kopieer nieuw bestand/map naar rechts</target>
+<target>Kopiëer nieuw bestand/map naar rechts</target>
<source>Delete left file/folder</source>
<target>Verwijder linker bestand/map</target>
@@ -1346,10 +1351,10 @@ Uitsluiten: \stuff\temp\*
<target>Verwijder rechter bestand/map</target>
<source>Move file on left</source>
-<target></target>
+<target>Verplaats bestand aan de linkerkant</target>
<source>Move file on right</source>
-<target></target>
+<target>Verplaats bestand aan de rechterkant</target>
<source>Overwrite left file/folder with right one</source>
<target>Overschrijf linker bestand/map met de rechter</target>
@@ -1361,10 +1366,10 @@ Uitsluiten: \stuff\temp\*
<target>Geen actie ondernemen</target>
<source>Copy file attributes only to left</source>
-<target>Kopieer bestandsattributen alleen naar links</target>
+<target>Kopiëer bestandsattributen alleen naar links</target>
<source>Copy file attributes only to right</source>
-<target>Kopieer bestandsattributen allen naar rechts</target>
+<target>Kopiëer bestandsattributen alleen naar rechts</target>
<source>Multiple...</source>
<target>Meerdere...</target>
@@ -1376,40 +1381,40 @@ Uitsluiten: \stuff\temp\*
<target>Verwijderen van map %x</target>
<source>Deleting symbolic link %x</source>
-<target></target>
+<target>Verwijderen van snelkoppeling %x</target>
<source>Moving file %x to recycle bin</source>
-<target></target>
+<target>Bezig met verplaatsen van bestand %x naar prullenbak</target>
<source>Moving folder %x to recycle bin</source>
-<target></target>
+<target>Bezig met verplaatsen van map %x naar prullenbak</target>
<source>Moving symbolic link %x to recycle bin</source>
-<target></target>
+<target>Bezig met verplaatsen van snelkoppeling %x naar prullenbak</target>
<source>Moving file %x to %y</source>
-<target></target>
+<target>Bezig met verplaatsen van bestand %x naar %y</target>
<source>Moving folder %x to %y</source>
-<target></target>
+<target>Bezig met verplaatsen van map %x naar %y</target>
<source>Moving symbolic link %x to %y</source>
-<target></target>
+<target>Bezig met verplaatsen van snelkoppeling %x naar %y</target>
<source>Creating file %x</source>
-<target></target>
+<target>Bestand %x wordt aangemaakt</target>
<source>Creating symbolic link %x</source>
-<target></target>
+<target>Snelkoppeling %x wordt aangemaakt</target>
<source>Creating folder %x</source>
<target>Map %x wordt aangemaakt</target>
<source>Overwriting file %x</source>
-<target></target>
+<target>Bezig met overschrijven van bestand %x</target>
<source>Overwriting symbolic link %x</source>
-<target></target>
+<target>Bezig met overschrijven van snelkoppeling %x</target>
<source>Verifying file %x</source>
<target>Verifieert bestand %x</target>
@@ -1436,7 +1441,7 @@ Uitsluiten: \stuff\temp\*
<target>Significant verschil gedetecteerd:</target>
<source>More than 50% of the total number of files will be copied or deleted!</source>
-<target>Meer dan 50% van alle bestanden zal gekopieerd of verwijderd worden!</target>
+<target>Meer dan 50% van alle bestanden zal gekopiëerd of verwijderd worden!</target>
<source>Not enough free disk space available in:</source>
<target>Niet genoeg vrije schijfruimte beschikbaar op:</target>
@@ -1448,7 +1453,7 @@ Uitsluiten: \stuff\temp\*
<target>Beschikbare vrije schijfruimte :</target>
<source>Recycle Bin is not available for the following paths! Files will be deleted permanently instead:</source>
-<target></target>
+<target>Prullenbak is niet beschikbaar voor de volgende locaties! De bestanden worden permanent verwijderd:</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>
diff --git a/BUILD/Languages/finnish.lng b/BUILD/Languages/finnish.lng
index 942fbc33..44299263 100644
--- a/BUILD/Languages/finnish.lng
+++ b/BUILD/Languages/finnish.lng
@@ -571,7 +571,7 @@ Komento suoritetaan kun:
<target><- Automaattinen -></target>
<source>Identify and propagate changes on both sides using a database. Deletions, renaming and conflicts are detected automatically.</source>
-<target></target>
+<target>Tunnista ja monista muutokset tietokannalla molemmille puolille. Poisto/Poikkeama/Uudelleen nimeäminan tunnistetaan automaattisesti.</target>
<source>Mirror ->></source>
<target>Peilaava ->></target>
@@ -1089,9 +1089,6 @@ Sulje pois: \stuff\temp\*
<source>Inactive</source>
<target>Lepotilassa</target>
-<source>Last x hours</source>
-<target>Viimeiset x tuntia</target>
-
<source>Today</source>
<target>Tänään</target>
@@ -1354,10 +1351,10 @@ Sulje pois: \stuff\temp\*
<target>Poista tiedosto/hakemisto oikealta</target>
<source>Move file on left</source>
-<target></target>
+<target>Siirä vasen tiedosto</target>
<source>Move file on right</source>
-<target></target>
+<target>Siirrä oikea tiedosto</target>
<source>Overwrite left file/folder with right one</source>
<target>Ylikirjoita oikea tiedosto/hakemista vasemanpuolisella</target>
diff --git a/BUILD/Resources.zip b/BUILD/Resources.zip
index c4749b68..41b492ba 100644
--- a/BUILD/Resources.zip
+++ b/BUILD/Resources.zip
Binary files differ
diff --git a/file_hierarchy.cpp b/file_hierarchy.cpp
index f5d2d28c..5bd7c5bb 100644
--- a/file_hierarchy.cpp
+++ b/file_hierarchy.cpp
@@ -117,9 +117,9 @@ bool hasDirectChild(const HierarchyObject& hierObj, Predicate p)
}
-SyncOperation FileSystemObject::testSyncOperation(SyncDirection testSyncDir, bool enabled) const
+SyncOperation FileSystemObject::testSyncOperation(SyncDirection testSyncDir, bool active) const
{
- return proposedSyncOperation(getCategory(), enabled, testSyncDir, syncDirConflict);
+ return proposedSyncOperation(getCategory(), active, testSyncDir, syncDirConflict);
}
@@ -205,15 +205,15 @@ SyncOperation DirMapping::getSyncOperation() const
}
-SyncOperation FileMapping::testSyncOperation(SyncDirection testSyncDir, bool enabled) const
+SyncOperation FileMapping::testSyncOperation(SyncDirection testSyncDir, bool active) const
{
- SyncOperation op = FileSystemObject::testSyncOperation(testSyncDir, enabled);
+ SyncOperation op = FileSystemObject::testSyncOperation(testSyncDir, active);
/*
check whether we can optimize "create + delete" via "move":
note: as long as we consider "create + delete" cases only, detection of renamed files, should be fine even for "binary" comparison variant!
*/
- if (const FileMapping* refFile = dynamic_cast<const FileMapping*>(FileSystemObject::retrieve(moveFileRef)))
+ if (const FileSystemObject* refFile = dynamic_cast<const FileMapping*>(FileSystemObject::retrieve(moveFileRef))) //we expect a "FileMapping", but only need a "FileSystemObject"
{
SyncOperation opRef = refFile->FileSystemObject::getSyncOperation(); //do *not* make a virtual call!
diff --git a/file_hierarchy.h b/file_hierarchy.h
index ac7ed87d..c0db4be9 100644
--- a/file_hierarchy.h
+++ b/file_hierarchy.h
@@ -348,7 +348,7 @@ public:
virtual CompareFilesResult getCategory() const = 0;
virtual std::wstring getCatConflict() const = 0; //only filled if getCategory() == FILE_CONFLICT
//sync operation
- virtual SyncOperation testSyncOperation(SyncDirection testSyncDir, bool enabled) const;
+ virtual SyncOperation testSyncOperation(SyncDirection testSyncDir, bool active) const;
virtual SyncOperation getSyncOperation() const;
std::wstring getSyncOpConflict() const; //return conflict when determining sync direction or during categorization
@@ -491,7 +491,7 @@ public:
virtual CompareFilesResult getCategory() const;
virtual std::wstring getCatConflict() const;
- virtual SyncOperation testSyncOperation(SyncDirection testSyncDir, bool enabled) const;
+ virtual SyncOperation testSyncOperation(SyncDirection testSyncDir, bool active) const;
virtual SyncOperation getSyncOperation() const;
template <SelectedSide side> void syncTo(const FileDescriptor& descrTarget, const FileDescriptor* descrSource = NULL); //copy + update file attributes (optional)
diff --git a/lib/folder_history_box.cpp b/lib/folder_history_box.cpp
index b395cbf4..c8bd042a 100644
--- a/lib/folder_history_box.cpp
+++ b/lib/folder_history_box.cpp
@@ -60,8 +60,8 @@ void FolderHistoryBox::OnHideDropDown(wxCommandEvent& event)
}
#endif
-
-void FolderHistoryBox::update(const wxString& dirname)
+//set value and update list are technically entangled: see potential bug description below
+void FolderHistoryBox::setValueAndUpdateList(const wxString& dirname)
{
//it may be a little lame to update the list on each mouse-button click, but it should be working and we dont't have to manipulate wxComboBox internals
@@ -69,8 +69,7 @@ void FolderHistoryBox::update(const wxString& dirname)
//add some aliases to allow user changing to volume name and back, if possible
#ifdef FFS_WIN
- const Zstring activePath = toZ(GetValue());
- std::vector<Zstring> aliases = getDirectoryAliases(activePath);
+ std::vector<Zstring> aliases = getDirectoryAliases(toZ(dirname));
dirList.insert(dirList.end(), aliases.begin(), aliases.end());
#endif
diff --git a/lib/folder_history_box.h b/lib/folder_history_box.h
index 28b85c90..859234cc 100644
--- a/lib/folder_history_box.h
+++ b/lib/folder_history_box.h
@@ -80,7 +80,7 @@ public:
void setValue(const wxString& dirname)
{
- update(dirname); //required for setting value correctly + Linux to ensure the dropdown is shown as being populated
+ setValueAndUpdateList(dirname); //required for setting value correctly + Linux to ensure the dropdown is shown as being populated
}
// GetValue
@@ -88,9 +88,9 @@ public:
private:
void OnKeyEvent(wxKeyEvent& event);
void OnSelection(wxCommandEvent& event);
- void OnUpdateList(wxEvent& event) { update(GetValue()); event.Skip(); }
+ void OnUpdateList(wxEvent& event) { setValueAndUpdateList(GetValue()); event.Skip(); }
- void update(const wxString& dirname);
+ void setValueAndUpdateList(const wxString& dirname);
#if wxCHECK_VERSION(2, 9, 1)
void OnShowDropDown(wxCommandEvent& event);
diff --git a/lib/resolve_path.cpp b/lib/resolve_path.cpp
index feeb98e3..df65b98b 100644
--- a/lib/resolve_path.cpp
+++ b/lib/resolve_path.cpp
@@ -5,6 +5,7 @@
#include <map>
#include <set>
#include <zen/scope_guard.h>
+#include <zen/thread.h>
#ifdef FFS_WIN
#include <zen/dll.h>
@@ -354,6 +355,7 @@ Zstring volumenNameToPath(const Zstring& volumeName) //return empty string on er
#ifdef FFS_WIN
+//attention: this call may seriously block if network volume is not available!!!
Zstring volumePathToName(const Zstring& volumePath) //return empty string on error
{
const DWORD bufferSize = MAX_PATH + 1;
@@ -440,9 +442,14 @@ void getDirectoryAliasesRecursive(const Zstring& dirname, std::set<Zstring, Less
dirname[1] == L':' &&
dirname[2] == L'\\')
{
- Zstring volname = volumePathToName(Zstring(dirname.c_str(), 3));
- if (!volname.empty())
- output.insert(L"[" + volname + L"]" + Zstring(dirname.c_str() + 2));
+ //attention: "volumePathToName()" will seriously block if network volume is not available!!!
+ boost::unique_future<Zstring> futVolName = zen::async([=] { return volumePathToName(Zstring(dirname.c_str(), 3)); });
+ if (futVolName.timed_wait(boost::posix_time::seconds(1)))
+ {
+ Zstring volname = futVolName.get();
+ if (!volname.empty())
+ output.insert(L"[" + volname + L"]" + Zstring(dirname.c_str() + 2));
+ }
}
//2. replace volume name by volume path: [SYSTEM]\dirname -> c:\dirname
diff --git a/ui/small_dlgs.cpp b/ui/small_dlgs.cpp
index cf323279..63d3f566 100644
--- a/ui/small_dlgs.cpp
+++ b/ui/small_dlgs.cpp
@@ -160,7 +160,6 @@ FilterDlg::FilterDlg(wxWindow* parent,
enumTimeDescr.
add(UTIME_NONE, _("Inactive")).
- //add(UTIME_LAST_X_HOURS, _("Last x hours")). //better: "Last %x hour" ?
add(UTIME_TODAY, _("Today")).
add(UTIME_THIS_WEEK, _("This week")).
add(UTIME_THIS_MONTH, _("This month")).
diff --git a/version/version.h b/version/version.h
index 50936457..03ad1777 100644
--- a/version/version.h
+++ b/version/version.h
@@ -3,7 +3,7 @@
namespace zen
{
-const wchar_t currentVersion[] = L"4.4"; //internal linkage!
+const wchar_t currentVersion[] = L"4.5"; //internal linkage!
}
#endif
diff --git a/version/version.rc b/version/version.rc
index c5f61a95..2b486f25 100644
--- a/version/version.rc
+++ b/version/version.rc
@@ -1,2 +1,2 @@
-#define VER_FREEFILESYNC 4,4,0,0
-#define VER_FREEFILESYNC_STR "4.4\0"
+#define VER_FREEFILESYNC 4,5,0,0
+#define VER_FREEFILESYNC_STR "4.5\0"
diff --git a/zen/FindFilePlus/find_file_plus.cpp b/zen/FindFilePlus/find_file_plus.cpp
index becfe553..e613177b 100644
--- a/zen/FindFilePlus/find_file_plus.cpp
+++ b/zen/FindFilePlus/find_file_plus.cpp
@@ -10,16 +10,18 @@
#include "load_dll.h"
#include <zen/scope_guard.h>
+#include <cstdio>
+
using namespace dll;
using namespace findplus;
namespace
{
-struct FileError
+struct NtFileError //exception class
{
- FileError(ULONG errorCode) : win32Error(errorCode) {}
- ULONG win32Error;
+ NtFileError(NTSTATUS errorCode) : ntError(errorCode) {}
+ NTSTATUS ntError;
};
@@ -91,8 +93,8 @@ bool findplus::initDllBinding() //evaluate in ::DllMain() when attaching process
//http://msdn.microsoft.com/en-us/library/ff563638(v=VS.85).aspx
//verify dynamic dll binding
- return ntOpenFile &&
- ntClose &&
+ return ntOpenFile &&
+ ntClose &&
ntQueryDirectoryFile &&
rtlNtStatusToDosError &&
rtlFreeUnicodeString &&
@@ -108,9 +110,15 @@ public:
FileSearcher(const wchar_t* dirname); //throw FileError
~FileSearcher();
- void readdir(FileInformation& output); //throw FileError
+ void readDir(FileInformation& output) { (this->*readDirFun)(output); } //throw FileError
+
+ bool tryFallbackToDefaultQuery(); //call if readDir() throws STATUS_NOT_SUPPORTED or similar
private:
+ template <class QueryPolicy> void readDirImpl(FileInformation& output); //throw FileError
+ //handle fallback, if retrieving file id is not supported by file system layer
+ void (FileSearcher::*readDirFun)(FileInformation& output);
+
UNICODE_STRING ntPathName; //it seems hDir implicitly keeps a reference to this, at least ::FindFirstFile() does no cleanup before ::FindClose()!
HANDLE hDir;
@@ -124,9 +132,43 @@ private:
};
+namespace
+{
+/*
+Common C-style policy handling directory traversal:
+
+struct QueryPolicy
+{
+ typedef ... RawFileInfo;
+ static const FILE_INFORMATION_CLASS fileInformationClass = ...;
+ static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo);
+};
+*/
+
+struct DirQueryDefault
+{
+ typedef FILE_BOTH_DIR_INFORMATION RawFileInfo;
+ static const FILE_INFORMATION_CLASS fileInformationClass = FileBothDirectoryInformation;
+ static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo) { fileInfo.fileId.QuadPart = 0; }
+};
+
+struct DirQueryFileId
+{
+ typedef FILE_ID_BOTH_DIR_INFORMATION RawFileInfo;
+ static const FILE_INFORMATION_CLASS fileInformationClass = FileIdBothDirectoryInformation;
+ static void extractFileId(const RawFileInfo& rawInfo, FileInformation& fileInfo)
+ {
+ fileInfo.fileId.QuadPart = rawInfo.FileId.QuadPart; //may be 0 even in this context, e.g. for mapped FTP drive!
+ static_assert(sizeof(fileInfo.fileId) == sizeof(rawInfo.FileId), "dang!");
+ }
+};
+}
+
+
FileSearcher::FileSearcher(const wchar_t* dirname) :
hDir(NULL),
- nextEntryOffset(0)
+ nextEntryOffset(0),
+ readDirFun(&FileSearcher::readDirImpl<DirQueryFileId>) //start optimistically
{
ntPathName.Buffer = NULL;
ntPathName.Length = 0;
@@ -136,7 +178,8 @@ FileSearcher::FileSearcher(const wchar_t* dirname) :
//--------------------------------------------------------------------------------------------------------------
//convert dosFileName, e.g. C:\Users or \\?\C:\Users to ntFileName \??\C:\Users
- //in contrast to ::FindFirstFile() we don't evaluate the relativeName, however tests indicate ntFileName is *always* filled with an absolute name, even if dosFileName is relative
+ //in contrast to ::FindFirstFile() implementation we don't evaluate the relativeName,
+ //however tests indicate ntFileName is *always* filled with an absolute name, even if dosFileName is relative
//NOTE: RtlDosPathNameToNtPathName_U may be used on all XP/Win7/Win8 for compatibility
// RtlDosPathNameToNtPathName_U: used by Windows XP available with OS version 3.51 (Windows NT) and higher
@@ -145,7 +188,7 @@ FileSearcher::FileSearcher(const wchar_t* dirname) :
&ntPathName, //__out ntFileName,
NULL, //__out_optFilePart,
NULL)) //__out_opt relativeName - empty if dosFileName is absolute
- throw FileError(rtlNtStatusToDosError(STATUS_OBJECT_PATH_NOT_FOUND)); //translates to ERROR_PATH_NOT_FOUND, same behavior like ::FindFirstFileEx()
+ throw NtFileError(STATUS_OBJECT_PATH_NOT_FOUND); //translates to ERROR_PATH_NOT_FOUND, same behavior like ::FindFirstFileEx()
OBJECT_ATTRIBUTES objAttr = {};
InitializeObjectAttributes(&objAttr, //[out] POBJECT_ATTRIBUTES initializedAttributes,
@@ -163,7 +206,7 @@ FileSearcher::FileSearcher(const wchar_t* dirname) :
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //__in ULONG shareAccess, - 7 on Win7/Win8, 3 on XP
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT); //__in ULONG openOptions - 4021 used on all XP/Win7/Win8
if (!NT_SUCCESS(rv))
- throw FileError(rtlNtStatusToDosError(rv));
+ throw NtFileError(rv);
}
guardConstructor.dismiss();
@@ -179,16 +222,33 @@ FileSearcher::~FileSearcher()
if (ntPathName.Buffer)
rtlFreeUnicodeString(&ntPathName); //cleanup identical to ::FindFirstFile()
- //note that most if this function seems inlined by the linker, so that its assembly looks equivalent to "RtlFreeHeap(GetProcessHeap(), 0, ntPathName.Buffer)"
+ //note that most of this function seems inlined by the linker, so that its assembly looks equivalent to "RtlFreeHeap(GetProcessHeap(), 0, ntPathName.Buffer)"
+}
+
+
+inline
+bool FileSearcher::tryFallbackToDefaultQuery()
+{
+ if (readDirFun == &FileSearcher::readDirImpl<DirQueryDefault>)
+ return false; //already default
+
+ //Note: NtQueryDirectoryFile() may not respect "restartScan" for some weird Win2000 file system drivers, so we won't bother
+ //Samba before v3.0.22 (Mar 30, 2006) seems to have a bug which sucessfully returns 128 elements via NtQueryDirectoryFile()
+ //and FileIdBothDirectoryInformation, then fails with STATUS_INVALID_LEVEL.
+ //Although traversal is NOT finished yet, it will further return STATUS_NO_MORE_FILES, even if falling back to FileBothDirectoryInformation!!!
+
+ readDirFun = &FileSearcher::readDirImpl<DirQueryDefault>;
+ return true;
}
-void FileSearcher::readdir(FileInformation& output)
+template <class QueryPolicy>
+void FileSearcher::readDirImpl(FileInformation& output) //throw FileError
{
//although FILE_ID_FULL_DIR_INFORMATION should suffice for our purposes, there are problems on Windows XP for certain directories, e.g. "\\Vboxsvr\build"
//making NtQueryDirectoryFile() return with STATUS_INVALID_PARAMETER while other directories, e.g. "C:\" work fine for some reason
//FILE_ID_BOTH_DIR_INFORMATION on the other hand works on XP/Win7/Win8
- //performance: there is no noticable difference between FILE_ID_BOTH_DIR_INFORMATION and FILE_ID_FULL_DIR_INFORMATION
+ //performance: there is no noticeable difference between FILE_ID_BOTH_DIR_INFORMATION and FILE_ID_FULL_DIR_INFORMATION
/* corresponding first access in ::FindFirstFileW()
@@ -216,18 +276,26 @@ void FileSearcher::readdir(FileInformation& output)
&status, //__out PIO_STATUS_BLOCK ioStatusBlock,
&buffer, //__out_bcount(Length) PVOID fileInformation,
BUFFER_SIZE, //__in ULONG length, ::FindNextFileW() on all XP/Win7/Win8 uses sizeof(FILE_BOTH_DIR_INFORMATION) + sizeof(TCHAR) * 2000 == 0x1000
- FileIdBothDirectoryInformation, //__in FILE_INFORMATION_CLASS fileInformationClass - all XP/Win7/Win8 use "FileBothDirectoryInformation"
+ QueryPolicy::fileInformationClass, //__in FILE_INFORMATION_CLASS fileInformationClass - all XP/Win7/Win8 use "FileBothDirectoryInformation"
false, //__in BOOLEAN returnSingleEntry,
NULL, //__in_opt PUNICODE_STRING mask,
false); //__in BOOLEAN restartScan
if (!NT_SUCCESS(rv))
- throw FileError(rtlNtStatusToDosError(rv)); //throws STATUS_NO_MORE_FILES when finished
+ {
+ if (rv == STATUS_NO_SUCH_FILE) //harmonize ntQueryDirectoryFile() error handling! failure to find a file on first call returns STATUS_NO_SUCH_FILE,
+ rv = STATUS_NO_MORE_FILES; //while on subsequent accesses return STATUS_NO_MORE_FILES
+ //note: not all directories contain "., .." entries! E.g. a drive's root directory or NetDrive + ftp.gnu.org\CRYPTO.README"
+ //-> addon: this is NOT a directory, it looks like on in NetDrive, but it's a file in Opera
+
+ throw NtFileError(rv); //throws STATUS_NO_MORE_FILES when finished
+ }
- if (status.Information == 0) //except for the first call to call ::NtQueryDirectoryFile():
- throw FileError(rtlNtStatusToDosError(STATUS_BUFFER_OVERFLOW)); //if buffer size is too small, return value is STATUS_SUCCESS and Information == 0 -> we don't expect this!
+ if (status.Information == 0) //except for the first call to call ::NtQueryDirectoryFile():
+ throw NtFileError(STATUS_BUFFER_OVERFLOW); //if buffer size is too small, return value is STATUS_SUCCESS and Information == 0 -> we don't expect this!
}
- const FILE_ID_BOTH_DIR_INFORMATION& dirInfo = *reinterpret_cast<FILE_ID_BOTH_DIR_INFORMATION*>(reinterpret_cast<char*>(buffer) + nextEntryOffset);
+ typedef typename QueryPolicy::RawFileInfo RawFileInfo;
+ const RawFileInfo& dirInfo = *reinterpret_cast<RawFileInfo*>(reinterpret_cast<char*>(buffer) + nextEntryOffset);
if (dirInfo.NextEntryOffset == 0)
nextEntryOffset = 0; //our offset is relative to the beginning of the buffer
@@ -241,15 +309,16 @@ void FileSearcher::readdir(FileInformation& output)
return tmp;
};
+ QueryPolicy::extractFileId(dirInfo, output);
+
output.creationTime = toFileTime(dirInfo.CreationTime);
output.lastWriteTime = toFileTime(dirInfo.LastWriteTime);
output.fileSize.QuadPart = dirInfo.EndOfFile.QuadPart;
- output.fileId.QuadPart = dirInfo.FileId.QuadPart;
output.fileAttributes = dirInfo.FileAttributes;
output.shortNameLength = dirInfo.FileNameLength / sizeof(TCHAR); //FileNameLength is in bytes!
if (dirInfo.FileNameLength + sizeof(TCHAR) > sizeof(output.shortName)) //this may actually happen if ::NtQueryDirectoryFile() decides to return a
- throw FileError(rtlNtStatusToDosError(STATUS_BUFFER_OVERFLOW)); //short name of length MAX_PATH + 1, 0-termination is not required!
+ throw NtFileError(STATUS_BUFFER_OVERFLOW); //short name of length MAX_PATH + 1, 0-termination is not required!
::memcpy(output.shortName, dirInfo.FileName, dirInfo.FileNameLength);
output.shortName[output.shortNameLength] = 0; //NOTE: FILE_ID_BOTH_DIR_INFORMATION::FileName in general is NOT 0-terminated! It is on XP/Win7, but NOT on Win8!
@@ -257,7 +326,6 @@ void FileSearcher::readdir(FileInformation& output)
static_assert(sizeof(output.creationTime) == sizeof(dirInfo.CreationTime), "dang!");
static_assert(sizeof(output.lastWriteTime) == sizeof(dirInfo.LastWriteTime), "dang!");
static_assert(sizeof(output.fileSize) == sizeof(dirInfo.EndOfFile), "dang!");
- static_assert(sizeof(output.fileId) == sizeof(dirInfo.FileId), "dang!");
static_assert(sizeof(output.fileAttributes) == sizeof(dirInfo.FileAttributes), "dang!");
}
@@ -268,10 +336,10 @@ FindHandle findplus::openDir(const wchar_t* dirname)
{
return new FileSearcher(dirname); //throw FileError
}
- catch (const FileError& err)
+ catch (const NtFileError& e)
{
- setWin32Error(err.win32Error);
- return NULL;
+ setWin32Error(rtlNtStatusToDosError(e.ntError));
+ return nullptr;
}
}
@@ -280,12 +348,28 @@ bool findplus::readDir(FindHandle hnd, FileInformation& output)
{
try
{
- hnd->readdir(output); //throw FileError
+ hnd->readDir(output); //throw FileError
return true;
}
- catch (const FileError& err)
+ catch (const NtFileError& e)
{
- setWin32Error(err.win32Error);
+ /*
+ fallback to default directory query method, if FileIdBothDirectoryInformation is not properly implemented
+ this is required for NetDrive mounted Webdav, e.g. www.box.net and NT4, 2000 remote drives, et al.
+ */
+ if (e.ntError != STATUS_NO_MORE_FILES)
+ if (e.ntError == STATUS_INVALID_LEVEL ||
+ e.ntError == STATUS_NOT_SUPPORTED ||
+ e.ntError == STATUS_INVALID_PARAMETER ||
+ e.ntError == STATUS_INVALID_NETWORK_RESPONSE ||
+ e.ntError == STATUS_INVALID_INFO_CLASS ||
+ e.ntError == STATUS_ACCESS_VIOLATION) //FileIdBothDirectoryInformation on XP accessing UDF
+ {
+ if (hnd->tryFallbackToDefaultQuery())
+ return readDir(hnd, output); //implementation of tryFallbackToDefaultQuery() promises, that we don't land in an endless recursion here!
+ }
+
+ setWin32Error(rtlNtStatusToDosError(e.ntError));
return false;
}
}
@@ -293,6 +377,5 @@ bool findplus::readDir(FindHandle hnd, FileInformation& output)
void findplus::closeDir(FindHandle hnd)
{
- if (hnd) //play a little "nice"
- delete hnd;
+ delete hnd;
}
diff --git a/zen/FindFilePlus/find_file_plus.h b/zen/FindFilePlus/find_file_plus.h
index 72b76dbb..33e9a178 100644
--- a/zen/FindFilePlus/find_file_plus.h
+++ b/zen/FindFilePlus/find_file_plus.h
@@ -34,7 +34,7 @@ struct FileInformation
FILETIME creationTime;
FILETIME lastWriteTime;
ULARGE_INTEGER fileSize;
- ULARGE_INTEGER fileId;
+ ULARGE_INTEGER fileId; //optional: may be 0 if not supported
DWORD fileAttributes;
DWORD shortNameLength;
WCHAR shortName[MAX_PATH + 1]; //shortName is 0-terminated
diff --git a/zen/FindFilePlus/load_dll.h b/zen/FindFilePlus/load_dll.h
index 350de9f8..24ce7174 100644
--- a/zen/FindFilePlus/load_dll.h
+++ b/zen/FindFilePlus/load_dll.h
@@ -39,7 +39,6 @@ private:
-
void* /*FARPROC*/ loadSymbol(const wchar_t* libraryName, const char* functionName);
}
diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp
index 1e44afb7..d6804e34 100644
--- a/zen/file_handling.cpp
+++ b/zen/file_handling.cpp
@@ -2008,12 +2008,12 @@ void rawCopyStream(const Zstring& sourceFile,
FileOutput fileOut(targetFile, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting
std::vector<char>& buffer = []() -> std::vector<char>&
- {
- 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
+ {
+ 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
return *cpyBuf;
- }();
+ }();
//copy contents of sourceFile to targetFile
UInt64 totalBytesTransferred;
diff --git a/zen/file_id_def.h b/zen/file_id_def.h
index b2029879..7e729eb1 100644
--- a/zen/file_id_def.h
+++ b/zen/file_id_def.h
@@ -29,13 +29,16 @@ FileId extractFileID(const BY_HANDLE_FILE_INFORMATION& fileInfo)
ULARGE_INTEGER uint = {};
uint.HighPart = fileInfo.nFileIndexHigh;
uint.LowPart = fileInfo.nFileIndexLow;
- return std::make_pair(fileInfo.dwVolumeSerialNumber, uint.QuadPart);
+
+ return fileInfo.dwVolumeSerialNumber != 0 && uint.QuadPart != 0 ?
+ FileId(fileInfo.dwVolumeSerialNumber, uint.QuadPart) : FileId();
}
inline
FileId extractFileID(DWORD dwVolumeSerialNumber, ULARGE_INTEGER fileId)
{
- return std::make_pair(dwVolumeSerialNumber, fileId.QuadPart);
+ return dwVolumeSerialNumber != 0 && fileId.QuadPart != 0 ?
+ FileId(dwVolumeSerialNumber, fileId.QuadPart) : FileId();
}
namespace impl
@@ -57,7 +60,8 @@ typedef std::pair<decltype(impl::StatDummy::st_dev), decltype(impl::StatDummy::s
inline
FileId extractFileID(const struct stat& fileInfo)
{
- return std::make_pair(fileInfo.st_dev, fileInfo.st_ino);
+ return fileInfo.st_dev != 0 && fileInfo.st_ino != 0 ?
+ FileId(fileInfo.st_dev, fileInfo.st_ino) : FileId();
}
#endif
}
diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp
index 8a8a8b6f..236581a3 100644
--- a/zen/file_traverser.cpp
+++ b/zen/file_traverser.cpp
@@ -31,13 +31,13 @@ namespace
//implement "retry" in a generic way:
template <class Command> inline //function object expecting to throw FileError if operation fails
-void tryReportingError(Command cmd, zen::TraverseCallback& callback)
+bool tryReportingError(Command cmd, zen::TraverseCallback& callback) //return "true" on success, "false" if error was ignored
{
for (;;)
try
{
cmd();
- return;
+ return true;
}
catch (const FileError& e)
{
@@ -46,7 +46,7 @@ void tryReportingError(Command cmd, zen::TraverseCallback& callback)
case TraverseCallback::TRAV_ERROR_RETRY:
break;
case TraverseCallback::TRAV_ERROR_IGNORE:
- return;
+ return false;
default:
assert(false);
break;
@@ -119,18 +119,17 @@ const DllFun<findplus::OpenDirFunc> openDir (findplus::getDllName(), findplus::
const DllFun<findplus::ReadDirFunc> readDir (findplus::getDllName(), findplus::readDirFuncName ); //load at startup: avoid pre C++11 static initialization MT issues
const DllFun<findplus::CloseDirFunc> closeDir(findplus::getDllName(), findplus::closeDirFuncName); //
-
/*
Common C-style interface for Win32 FindFirstFile(), FindNextFile() and FileFilePlus openDir(), closeDir():
-struct X //see "policy based design"
+struct TraverserPolicy //see "policy based design"
{
-typedef ... Handle;
+typedef ... DirHandle;
typedef ... FindData;
-static Handle create(const Zstring& directoryPf, FindData& fileInfo); //throw FileError - concession to FindFirstFile(): implement two operations: 1. open handle, 2. retrieve first data set
-static void destroy(Handle hnd); //throw()
-static bool next(Handle hnd, const Zstring& directory, WIN32_FIND_DATA& fileInfo) //throw FileError
+static void create(const Zstring& directory, DirHandle& hnd); //throw FileError - *no* concession to FindFirstFile(): open handle only, *no* return of data!
+static void destroy(DirHandle hnd); //throw()
+static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
-//helper routines
+//FindData "member" functions
static void extractFileInfo (const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output);
static Int64 getModTime (const FindData& fileInfo);
static const FILETIME& getModTimeRaw (const FindData& fileInfo); //yet another concession to DST hack
@@ -140,35 +139,54 @@ static bool isDirectory (const FindData& fileInfo);
static bool isSymlink (const FindData& fileInfo);
}
-Note: Win32 FindFirstFile(), FindNextFile() is a weaker abstraction than FileFilePlus openDir(), readDir(), closeDir() and Unix opendir(), closedir(), stat(),
- so unfortunately we have to use former as a greatest common divisor
+Note: Win32 FindFirstFile(), FindNextFile() is a weaker abstraction than FileFilePlus openDir(), readDir(), closeDir() and Unix opendir(), closedir(), stat()
*/
struct Win32Traverser
{
- typedef HANDLE Handle;
+ struct DirHandle
+ {
+ DirHandle() : searchHandle(NULL), firstRead(true) {}
+
+ HANDLE searchHandle;
+ bool firstRead;
+ WIN32_FIND_DATA firstData;
+ };
+
typedef WIN32_FIND_DATA FindData;
- static Handle create(const Zstring& directory, FindData& fileInfo) //throw FileError
+ static void create(const Zstring& directory, DirHandle& hnd) //throw FileError
{
const Zstring& directoryPf = endsWith(directory, FILE_NAME_SEPARATOR) ?
directory :
directory + FILE_NAME_SEPARATOR;
- HANDLE output = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &fileInfo);
+ hnd.searchHandle = ::FindFirstFile(applyLongPathPrefix(directoryPf + L'*').c_str(), &hnd.firstData);
//no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH
- if (output == INVALID_HANDLE_VALUE)
+ if (hnd.searchHandle == INVALID_HANDLE_VALUE)
throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
- //::GetLastError() == ERROR_FILE_NOT_FOUND -> actually NOT okay, even for an empty directory this should not occur (., ..)
- return output;
+
+ //::GetLastError() == ERROR_FILE_NOT_FOUND -> *usually* NOT okay:
+ //however it is unclear whether this indicates a missing directory or a completely empty directory
+ // note: not all directories contain "., .." entries! E.g. a drive's root directory or NetDrive + ftp.gnu.org\CRYPTO.README"
+ // -> addon: this is NOT a directory, it looks like one in NetDrive, but it's a file in Opera!
+ //we have to guess it's former and let the error propagate
+ // -> FindFirstFile() is a nice example of violation of API design principle of single responsibility and its consequences
}
- static void destroy(Handle hnd) { ::FindClose(hnd); } //throw()
+ static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw()
- static bool next(Handle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+ static bool getEntry(DirHandle& hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
{
- if (!::FindNextFile(hnd, &fileInfo))
+ if (hnd.firstRead)
+ {
+ hnd.firstRead = false;
+ ::memcpy(&fileInfo, &hnd.firstData, sizeof(fileInfo));
+ return true;
+ }
+
+ if (!::FindNextFile(hnd.searchHandle, &fileInfo))
{
if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
return false;
@@ -178,7 +196,6 @@ struct Win32Traverser
return true;
}
- //helper routines
template <class FindData>
static void extractFileInfo(const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output)
{
@@ -208,27 +225,27 @@ struct Win32Traverser
struct FilePlusTraverser
{
- typedef findplus::FindHandle Handle;
+ struct DirHandle
+ {
+ DirHandle() : searchHandle(NULL) {}
+
+ findplus::FindHandle searchHandle;
+ };
+
typedef findplus::FileInformation FindData;
- static Handle create(const Zstring& directory, FindData& fileInfo) //throw FileError
+ static void create(const Zstring& directory, DirHandle& hnd) //throw FileError
{
- Handle output = ::openDir(applyLongPathPrefix(directory).c_str());
- if (output == NULL)
+ hnd.searchHandle = ::openDir(applyLongPathPrefix(directory).c_str());
+ if (hnd.searchHandle == NULL)
throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted());
-
- bool rv = next(output, directory, fileInfo); //throw FileError
- if (!rv) //we expect at least two successful reads, even for an empty directory: ., ..
- throw FileError(_("Error traversing directory:") + L"\n\"" + directory + L"\"" + L"\n\n" + zen::getLastErrorFormatted(ERROR_NO_MORE_FILES));
-
- return output;
}
- static void destroy(Handle hnd) { ::closeDir(hnd); } //throw()
+ static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw()
- static bool next(Handle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
+ static bool getEntry(DirHandle hnd, const Zstring& directory, FindData& fileInfo) //throw FileError
{
- if (!::readDir(hnd, fileInfo))
+ if (!::readDir(hnd.searchHandle, fileInfo))
{
if (::GetLastError() == ERROR_NO_MORE_FILES) //not an error situation
return false;
@@ -238,7 +255,6 @@ struct FilePlusTraverser
return true;
}
- //helper routines
template <class FindData>
static void extractFileInfo(const FindData& fileInfo, const DWORD* volumeSerial, TraverseCallback::FileInfo& output)
{
@@ -305,31 +321,43 @@ private:
throw FileError(_("Endless loop when traversing directory:") + L"\n\"" + directory + L"\"");
}, sink);
- typename Trav::FindData fileInfo = {};
-
- typename Trav::Handle searchHandle = 0;
+ typename Trav::DirHandle searchHandle;
- tryReportingError([&]
+ const bool openSuccess = tryReportingError([&]
{
typedef Trav Trav; //f u VS!
- searchHandle = Trav::create(directory, fileInfo); //throw FileError
+ Trav::create(directory, searchHandle); //throw FileError
}, sink);
- if (searchHandle == 0)
+ if (!openSuccess)
return; //ignored error
ZEN_ON_BLOCK_EXIT(typedef Trav Trav; Trav::destroy(searchHandle));
- do
+ typename Trav::FindData fileInfo = {};
+
+ while ([&]() -> bool
+ {
+ bool moreData = false;
+
+ typedef Trav Trav1; //f u VS!
+ tryReportingError([&]
{
- //don't return "." and ".."
+ typedef Trav1 Trav; //f u VS!
+ moreData = Trav::getEntry(searchHandle, directory, fileInfo); //throw FileError
+ }, sink);
+
+ return moreData;
+ }())
+ {
+ //skip "." and ".."
const Zchar* const shortName = Trav::getShortName(fileInfo);
if (shortName[0] == L'.' &&
- (shortName[1] == L'\0' || (shortName[1] == L'.' && shortName[2] == L'\0')))
+ (shortName[1] == L'\0' || (shortName[1] == L'.' && shortName[2] == L'\0')))
continue;
const Zstring& fullName = endsWith(directory, FILE_NAME_SEPARATOR) ?
- directory + shortName :
- directory + FILE_NAME_SEPARATOR + shortName;
+ directory + shortName :
+ directory + FILE_NAME_SEPARATOR + shortName;
if (Trav::isSymlink(fileInfo) && !followSymlinks_) //evaluate symlink directly
{
@@ -383,19 +411,6 @@ private:
sink.onFile(shortName, fullName, details);
}
}
- while ([&]() -> bool
- {
- bool moreData = false;
-
- typedef Trav Trav1; //f u VS!
- tryReportingError([&]
- {
- typedef Trav1 Trav; //f u VS!
- moreData = Trav::next(searchHandle, directory, fileInfo); //throw FileError
- }, sink);
-
- return moreData;
- }());
}
bgstack15