From 94db751716dd2851f99b5c4c2981da1d1f4780f8 Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 2 Oct 2015 14:53:20 +0200 Subject: 6.11 --- FreeFileSync/Build/Changelog.txt | 26 +- FreeFileSync/Build/Help/FreeFileSync.hhc | 5 - FreeFileSync/Build/Help/FreeFileSync.hhp | 1 - FreeFileSync/Build/Help/html/Command Line.html | 4 +- FreeFileSync/Build/Help/html/FreeFileSync.html | 25 + FreeFileSync/Build/Help/html/Links.html | 29 - FreeFileSync/Build/Help/html/RealtimeSync.html | 7 +- FreeFileSync/Build/Help/img/donate.png | Bin 0 -> 3979 bytes FreeFileSync/Build/Help/img/filter.png | Bin 43018 -> 43329 bytes FreeFileSync/Build/Languages/dutch.lng | 1576 +++++++++++++ FreeFileSync/Build/Languages/german.lng | 61 +- FreeFileSync/Build/Languages/norwegian.lng | 72 +- FreeFileSync/Build/Languages/outdated/dutch.lng | 1573 ------------- FreeFileSync/Source/FreeFileSync.vcxproj | 2 +- FreeFileSync/Source/Makefile | 2 +- FreeFileSync/Source/RealtimeSync/Makefile | 2 +- .../Source/RealtimeSync/RealtimeSync.vcxproj | 2 +- FreeFileSync/Source/RealtimeSync/application.cpp | 4 +- FreeFileSync/Source/RealtimeSync/application.h | 10 +- FreeFileSync/Source/RealtimeSync/main_dlg.cpp | 7 +- FreeFileSync/Source/RealtimeSync/main_dlg.h | 22 +- FreeFileSync/Source/RealtimeSync/monitor.cpp | 6 +- FreeFileSync/Source/RealtimeSync/monitor.h | 4 +- FreeFileSync/Source/RealtimeSync/tray_menu.cpp | 14 +- FreeFileSync/Source/RealtimeSync/xml_proc.cpp | 2 +- FreeFileSync/Source/algorithm.cpp | 29 +- FreeFileSync/Source/application.cpp | 70 +- FreeFileSync/Source/application.h | 8 +- FreeFileSync/Source/comparison.cpp | 23 +- FreeFileSync/Source/file_hierarchy.h | 108 +- FreeFileSync/Source/lib/db_file.cpp | 41 +- FreeFileSync/Source/lib/db_file.h | 7 +- FreeFileSync/Source/lib/dir_exist_async.h | 6 +- FreeFileSync/Source/lib/dir_lock.cpp | 11 +- FreeFileSync/Source/lib/error_log.h | 2 +- FreeFileSync/Source/lib/ffs_paths.cpp | 4 +- FreeFileSync/Source/lib/generate_logfile.h | 58 +- FreeFileSync/Source/lib/hard_filter.cpp | 37 +- FreeFileSync/Source/lib/hard_filter.h | 36 +- FreeFileSync/Source/lib/icon_buffer.cpp | 16 +- FreeFileSync/Source/lib/localization.cpp | 31 +- FreeFileSync/Source/lib/lock_holder.h | 6 +- FreeFileSync/Source/lib/osx_file_icon.h | 36 - FreeFileSync/Source/lib/osx_file_icon.mm | 179 -- FreeFileSync/Source/lib/parallel_scan.cpp | 60 +- FreeFileSync/Source/lib/parse_lng.h | 48 +- FreeFileSync/Source/lib/parse_plural.h | 38 +- FreeFileSync/Source/lib/process_xml.cpp | 6 +- FreeFileSync/Source/lib/process_xml.h | 12 +- FreeFileSync/Source/lib/resolve_path.cpp | 28 +- FreeFileSync/Source/lib/shadow.cpp | 128 -- FreeFileSync/Source/lib/shadow.h | 36 - FreeFileSync/Source/lib/status_handler.h | 28 +- FreeFileSync/Source/lib/versioning.cpp | 33 +- FreeFileSync/Source/structures.cpp | 8 +- FreeFileSync/Source/structures.h | 1 - FreeFileSync/Source/synchronization.cpp | 217 +- FreeFileSync/Source/synchronization.h | 7 +- FreeFileSync/Source/ui/batch_config.cpp | 20 +- FreeFileSync/Source/ui/batch_status_handler.cpp | 55 +- FreeFileSync/Source/ui/batch_status_handler.h | 16 +- FreeFileSync/Source/ui/check_version.cpp | 44 +- FreeFileSync/Source/ui/column_attr.h | 34 +- FreeFileSync/Source/ui/custom_grid.cpp | 116 +- FreeFileSync/Source/ui/custom_grid.h | 8 +- FreeFileSync/Source/ui/dir_name.cpp | 2 +- FreeFileSync/Source/ui/folder_history_box.h | 2 +- FreeFileSync/Source/ui/grid_view.cpp | 16 +- FreeFileSync/Source/ui/grid_view.h | 5 +- FreeFileSync/Source/ui/gui_generated.cpp | 26 +- FreeFileSync/Source/ui/gui_generated.h | 3 +- FreeFileSync/Source/ui/gui_status_handler.cpp | 5 +- FreeFileSync/Source/ui/gui_status_handler.h | 28 +- FreeFileSync/Source/ui/main_dlg.cpp | 88 +- FreeFileSync/Source/ui/main_dlg.h | 62 +- FreeFileSync/Source/ui/on_completion_box.cpp | 4 +- FreeFileSync/Source/ui/osx_dock.h | 17 - FreeFileSync/Source/ui/osx_dock.mm | 24 - FreeFileSync/Source/ui/progress_indicator.cpp | 94 +- FreeFileSync/Source/ui/small_dlgs.cpp | 57 +- FreeFileSync/Source/ui/sorting.h | 7 +- FreeFileSync/Source/ui/sync_cfg.cpp | 86 +- FreeFileSync/Source/ui/taskbar.cpp | 3 +- FreeFileSync/Source/ui/tray_icon.cpp | 4 +- FreeFileSync/Source/ui/tree_view.cpp | 40 +- FreeFileSync/Source/ui/triple_splitter.cpp | 3 +- FreeFileSync/Source/version/version.h | 2 +- wx+/bitmap_button.h | 2 +- wx+/choice_enum.h | 2 +- wx+/context_menu.h | 2 +- wx+/dc.h | 9 +- wx+/file_drop.h | 4 +- wx+/graph.cpp | 37 +- wx+/graph.h | 22 +- wx+/grid.cpp | 60 +- wx+/grid.h | 22 +- wx+/image_resources.cpp | 2 +- wx+/image_tools.cpp | 2 +- wx+/popup_dlg.cpp | 10 +- wx+/popup_dlg_generated.cpp | 146 +- wx+/popup_dlg_generated.h | 56 +- wx+/rtl.h | 2 +- wx+/std_button_layout.h | 2 +- wx+/zlib_wrap.h | 10 +- zen/assert_static.h | 43 - zen/basic_math.h | 2 +- zen/dir_watcher.cpp | 68 +- zen/dst_hack.cpp | 368 ---- zen/dst_hack.h | 38 - zen/file_access.cpp | 2313 +++++++++++++++++++ zen/file_access.h | 80 + zen/file_handling.cpp | 2328 -------------------- zen/file_handling.h | 82 - zen/file_id_def.h | 7 +- zen/file_io.cpp | 13 +- zen/file_io.h | 17 +- zen/file_io_base.h | 4 +- zen/file_traverser.cpp | 117 +- zen/file_traverser.h | 15 +- zen/fixed_list.h | 34 +- zen/format_unit.h | 2 +- zen/guid.h | 6 +- zen/i18n.h | 15 +- zen/int64.h | 38 +- zen/notify_removal.cpp | 7 +- zen/notify_removal.h | 11 +- zen/osx_string.h | 82 - zen/osx_throw_exception.h | 56 - zen/privilege.cpp | 144 -- zen/privilege.h | 17 - zen/recycler.cpp | 26 +- zen/recycler.h | 1 - zen/scope_guard.h | 6 +- zen/serialize.h | 49 +- zen/shell_execute.h | 2 +- zen/stl_tools.h | 70 +- zen/string_base.h | 80 +- zen/string_tools.h | 30 +- zen/string_traits.h | 12 +- zen/sys_error.h | 31 +- zen/thread.h | 14 +- zen/tick_count.h | 18 +- zen/time.h | 11 +- zen/utf.h | 18 +- zen/win_ver.h | 43 +- zen/xml_io.cpp | 4 +- zen/zstring.cpp | 101 +- zen/zstring.h | 57 +- 148 files changed, 5648 insertions(+), 6907 deletions(-) delete mode 100644 FreeFileSync/Build/Help/html/Links.html create mode 100644 FreeFileSync/Build/Help/img/donate.png create mode 100644 FreeFileSync/Build/Languages/dutch.lng delete mode 100644 FreeFileSync/Build/Languages/outdated/dutch.lng delete mode 100644 FreeFileSync/Source/lib/osx_file_icon.h delete mode 100644 FreeFileSync/Source/lib/osx_file_icon.mm delete mode 100644 FreeFileSync/Source/lib/shadow.cpp delete mode 100644 FreeFileSync/Source/lib/shadow.h delete mode 100644 FreeFileSync/Source/ui/osx_dock.h delete mode 100644 FreeFileSync/Source/ui/osx_dock.mm delete mode 100644 zen/assert_static.h delete mode 100644 zen/dst_hack.cpp delete mode 100644 zen/dst_hack.h create mode 100644 zen/file_access.cpp create mode 100644 zen/file_access.h delete mode 100644 zen/file_handling.cpp delete mode 100644 zen/file_handling.h delete mode 100644 zen/osx_string.h delete mode 100644 zen/osx_throw_exception.h delete mode 100644 zen/privilege.cpp delete mode 100644 zen/privilege.h diff --git a/FreeFileSync/Build/Changelog.txt b/FreeFileSync/Build/Changelog.txt index 5b631823..497ce39f 100644 --- a/FreeFileSync/Build/Changelog.txt +++ b/FreeFileSync/Build/Changelog.txt @@ -1,5 +1,27 @@ -FreeFileSync 6.10 ------------------ +FreeFileSync 6.11 [2014-11-03] +------------------------------ +Updated Recycle Bin access for Windows 10 +New command line option "-edit" to load configuration without executing +Case-insensitive command line argument evaluation +New Explorer context menu options for ffs_gui, ffs_batch files +Added sync variant to folder pair info in log file +Don't process and log folder pair if nothing to do except writing DB file +Fixed liblzma.5.dylib not found during startup (OS X 10.8) +Added version info to application bundles (OS X) +Fixed incorrect warning when configuration contains empty folder pairs +Replaced misleading inotify error message "No space left on device" (Linux) +Fixed FreeFileSync launcher blocking app folder move (OS X) +Updated default main dialog layout +Fixed async error evaluation when creating volume shadow copies +Keep user interface responsive while creating a volume shadow copy +Fixed error when starting asynchronously from a batch script +Show progress of writing log files +Fixed updated file being left deleted when copying permission failed +New Project website: http://www.freefilesync.org/ + + +FreeFileSync 6.10 [2014-10-01] +------------------------------ Fixed crash when accessing recycle bin in compatibility mode (Windows 7, 8) Draw middle grid selection irrespective of focus column Don't show parts of progress graph if nothing to sync diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhc b/FreeFileSync/Build/Help/FreeFileSync.hhc index 1902cb14..f8fda04b 100644 --- a/FreeFileSync/Build/Help/FreeFileSync.hhc +++ b/FreeFileSync/Build/Help/FreeFileSync.hhc @@ -83,10 +83,5 @@ -
  • - - - - diff --git a/FreeFileSync/Build/Help/FreeFileSync.hhp b/FreeFileSync/Build/Help/FreeFileSync.hhp index a9f19ebc..531eb70c 100644 --- a/FreeFileSync/Build/Help/FreeFileSync.hhp +++ b/FreeFileSync/Build/Help/FreeFileSync.hhp @@ -25,7 +25,6 @@ html\Variable Drive Letters.html html\Volume Shadow Copy.html html\RealtimeSync.html html\Run as Service.html -html\Links.html [INFOTYPES] diff --git a/FreeFileSync/Build/Help/html/Command Line.html b/FreeFileSync/Build/Help/html/Command Line.html index 3123ba49..f9b4fb46 100644 --- a/FreeFileSync/Build/Help/html/Command Line.html +++ b/FreeFileSync/Build/Help/html/Command Line.html @@ -110,8 +110,8 @@ and check if synchronization completed successfully:

    3. Customize an existing configuration

    -

    You can replace the directories of a given ffs_gui or ffs_batch configuration file by using the -leftdir -and -rightdir parameters: +

    You can replace the directories of a given ffs_gui or ffs_batch configuration file by using the -LeftDir +and -RightDir parameters:

    diff --git a/FreeFileSync/Build/Help/html/FreeFileSync.html b/FreeFileSync/Build/Help/html/FreeFileSync.html index 106b8d3c..ad176beb 100644 --- a/FreeFileSync/Build/Help/html/FreeFileSync.html +++ b/FreeFileSync/Build/Help/html/FreeFileSync.html @@ -54,5 +54,30 @@
  • Synchronization statistics +
    + +

    FreeFileSync Links

    + +
    +
    + Homepage:
    + http://www.freefilesync.org/ - feedback, suggestions, bug-reports, official download mirrors
    + +
    + + Support the project:
    + + If you like FreeFileSync... + Donate now + + + + + +
    +
    +
    + \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/Links.html b/FreeFileSync/Build/Help/html/Links.html deleted file mode 100644 index 5f7ca317..00000000 --- a/FreeFileSync/Build/Help/html/Links.html +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - -

    FreeFileSync Links

    - -
    -
    - Homepage:
    - http://freefilesync.sf.net/ -

    - - Project on SourceForge: feedback, suggestions and bug-reports
    - http://sourceforge.net/projects/freefilesync/ -

    - - If you like FreeFileSync:
    - Support the project by a donation -
    -
    -
    - - - \ No newline at end of file diff --git a/FreeFileSync/Build/Help/html/RealtimeSync.html b/FreeFileSync/Build/Help/html/RealtimeSync.html index 6b2e2feb..f021c3de 100644 --- a/FreeFileSync/Build/Help/html/RealtimeSync.html +++ b/FreeFileSync/Build/Help/html/RealtimeSync.html @@ -12,9 +12,8 @@ Automated Synchronization -

    The primary purpose of RealtimeSync is to execute a command line each time a directory becomes -available (e. g. insert of a USB-stick) or when it detects changes in one of the monitored directories. Usually this command line will trigger -a FreeFileSync batch job. +

    The primary purpose of RealtimeSync is to execute a command line each time it detects changes in one of the monitored directories +or when a directory becomes available (e. g. insert of a USB-stick). Usually this command line will trigger a FreeFileSync batch job.


    @@ -93,7 +92,7 @@ on the stick. RealtimeSync will also trigger each time files are modified in -     cmd /c echo %change_action% "%change_path%" >> C:\log.txt +     cmd /c echo %change_action% "%change_path%" >> %UserProfile%\Desktop\log.txt diff --git a/FreeFileSync/Build/Help/img/donate.png b/FreeFileSync/Build/Help/img/donate.png new file mode 100644 index 00000000..c7fb97c4 Binary files /dev/null and b/FreeFileSync/Build/Help/img/donate.png differ diff --git a/FreeFileSync/Build/Help/img/filter.png b/FreeFileSync/Build/Help/img/filter.png index f135ca0f..09747e93 100644 Binary files a/FreeFileSync/Build/Help/img/filter.png and b/FreeFileSync/Build/Help/img/filter.png differ diff --git a/FreeFileSync/Build/Languages/dutch.lng b/FreeFileSync/Build/Languages/dutch.lng new file mode 100644 index 00000000..d8192801 --- /dev/null +++ b/FreeFileSync/Build/Languages/dutch.lng @@ -0,0 +1,1576 @@ +
    + Nederlands + Leo49 + nl_NL + flag_holland.png + 2 + n == 1 ? 0 : 1 +
    + +Open configuration for edit without executing. + + +Both sides have changed since last synchronization. +Beide zijden zijn gewijzigd sinds de laatste synchronisatie. + +Cannot determine sync-direction: +De synchronisatierichting kan niet worden bepaald: + +No change since last synchronization. +Geen veranderingen sinds de laatste synchronisatie. + +The database entry is not in sync considering current settings. +De databasevermelding is niet gesynchroniseerd volgens de huidige instellingen. + +Setting default synchronization directions: Old files will be overwritten with newer files. +Standaardwaarden instellen voor synchronisatierichtingen: oude bestanden worden overschreven door nieuwere. + +Checking recycle bin availability for folder %x... +Controleer beschikbaarheid voor de prullenbak map %x... + +Moving file %x to the recycle bin +Verplaatsen van bestand %x naar de prullenbak + +Moving folder %x to the recycle bin +Verplaatsen van de map %x naar de prullenbak + +Moving symbolic link %x to the recycle bin +Verplaatsen symbolische koppeling %x naar de prullenbak + +Deleting file %x +Verwijderen van bestand %x + +Deleting folder %x +Verwijderen van map %x + +Deleting symbolic link %x +Symbolische koppeling %x verwijderen + +The recycle bin is not available for the following folders. Files will be deleted permanently instead: +De prullenbak is niet beschikbaar voor de volgende mappen. De bestanden worden permanent verwijderd: + +An exception occurred +Een uitzondering is opgetreden + +A directory path is expected after %x. +Een mappad wordt na %x verwacht. + +Syntax error +Syntaxisfout + +Cannot open file %x. +Het bestand %x kan niet geopend worden. + +File %x does not contain a valid configuration. +Het bestand %x bevat geen geldige configuratie. + +Unequal number of left and right directories specified. +Ongelijk aantal linker- en rechter- mappen opgegeven. + +The config file must not contain settings at directory pair level when directories are set via command line. +Het configuratiebestand mag geen instellingen op mappaarniveau bevatten, als de directories via de opdrachtregel zijn ingesteld. + +Directories cannot be set for more than one configuration file. +Mappen kunnen niet worden gebruikt voor meer dan één configuratiebestand. + +Command line +Opdrachtregel + +Syntax: +Syntax: + +global config file: +globaal configuratiebestand: + +config files: +configuratiebestanden: + +directory +directory + +Path to an alternate GlobalSettings.xml file. +Bestandspad naar alternatieve GlobalSettings.xml. + +Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. +Een willekeurig aantal FreeFileSync. ffs_gui en/of .ffs_batch configuratiebestanden. + +Any number of alternative directory pairs for at most one config file. +Een willekeurig aantal alternatieve directoryparen voor maximaal een configuratiebestand. + +Cannot find the following folders: +De volgende map kon niet worden gevonden: + +You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. +Deze fout kan worden genegeerd, om de mappen als leeg te zien en de folders automatisch te creëren wanneer u synchroniseert. + +A folder input field is empty. +Invoerveld van een map is leeg. + +The corresponding folder will be considered as empty. +De betreffende map wordt als leeg beschouwd. + +The following folder paths are dependent from each other: +De volgende mappaden zijn afhankelijk van elkaar: + +File %x has an invalid date. +Het bestand %x heeft een ongeldige datum. + +Date: +Datum: + +Files %x have the same date but a different size. +De bestanden %x hebben hetzelfde datum, maar de grootte is verschillend. + +Size: +Grootte: + +Content comparison was skipped for excluded files %x. +De vergelijking van de uitgesloten bestanden %x werd overgeslagen. + +Items differ in attributes only +De items verschillen alleen in de kenmerken + +Resolving symbolic link %x +Symbolische link %x oplossen + +Comparing content of files %x +Vergelijkt de inhoud van bestanden %x + +Generating file list... +Maak bestandslijst... + +Starting comparison +Start vergelijking + +Calculating sync directions... +Berekenen sync richtingen... + +Out of memory. +Onvoldoende werkgeheugen. + +Item exists on left side only +Item bestaat alleen aan de linkerkant + +Item exists on right side only +Item bestaat alleen aan de rechterkant + +Left side is newer +Linkerkant is nieuwer + +Right side is newer +Rechterkant is nieuwer + +Items have different content +Items hebben verschillende inhoud + +Both sides are equal +Beide zijden zijn gelijk + +Conflict/item cannot be categorized +Conflict/item niet kan worden gecategoriseerd + +Copy new item to left +Kopieer nieuw item naar links + +Copy new item to right +Kopieer nieuw item naar rechts + +Delete left item +Verwijder linker item + +Delete right item +Verwijder rechter item + +Move file on left +Verplaats linker bestand + +Move file on right +Verplaats rechter bestand + +Overwrite left item +Overschrijf linker item + +Overwrite right item +Overschrijf rechter item + +Do nothing +Niets doen + +Update attributes on left +Update kenmerken aan de linkerzijde + +Update attributes on right +Update kenmerken aan de rechterzijde + + +1 byte +%x bytes + + +1 Byte +%x Bytes + + +%x MB +%x MB + +%x KB +%x KB + +%x GB +%x GB + +Cannot load file %x. +Kan bestand %x niet inlezen. + +Database file %x is incompatible. +Databasebestand %x is niet compatibel. + +Initial synchronization: +Initiële synchronisatie: + +Database file %x does not yet exist. +Databasebestand %x bestaat nog niet. + +Database file is corrupt: +Databasebestand is beschadigd: + +Cannot write file %x. +Kan bestand %x niet schrijven. + +Cannot read file %x. +Kan het bestand %x niet lezen. + +Database files do not share a common session. +Database bestanden kunnen geen gemeenschappelijke sessie delen. + +Searching for folder %x... +Zoek naar map %x... + +Cannot read file attributes of %x. +Kan de bestandskenmerken niet lezen van %x. + +Cannot get process information. +Kan geen procesinformatie krijgen. + +Waiting while directory is locked: +Wachten terwijl de direcory is vergrendeld: + +Lock owner: +Vergrendel eigenaar: + + +1 sec +%x sec + + +1 Sek. +%x Sek. + + +Detecting abandoned lock... +Opsporen van verlaten vergrendeling... + +Creating file %x +Bestand %x maken + +Items processed: +Items verwerkt: + +Items remaining: +Overgebleven items: + +Total time: +Totale tijd: + +Error parsing file %x, row %y, column %z. +Fout bij het analyseren van bestand %x, rij %y, kolom %z. + +Cannot set directory lock for %x. +De mapvergrendeling voor %x kan niet ingesteld worden. + +Scanning: +Scannen: + + +1 thread +%x threads + + +1 Thread +%x Threads + + +Encoding extended time information: %x +Codering van langere tijdinformatie: %x + +/sec +/sec + +%x items/sec +%x items/sec + +Show in Explorer +In Explorer weergeven + +Open with default application +Openen met standaardtoepassing + +Browse directory +Blader in directory + +Cannot access the Volume Shadow Copy Service. +Geen toegang tot het Volume Shadow Copy Service. + +Please use FreeFileSync 64-bit version to create shadow copies on this system. +Gebruik FreeFileSync 64-bits versie voor het maken van schaduwkopieën op dit systeem. + +Cannot determine volume name for %x. +Kan de volumenaam niet vaststellen voor %x. + +Volume name %x is not part of file path %y. +Volumenaam %x behoort niet tot bestandspad %y. + +Stop requested: Waiting for current operation to finish... +Stop opgevraagd: wachten totdat de huidige bewerking is voltooit... + +Unable to create time stamp for versioning: +Kan geen tijdstempel maken voor versiebeheer: + +&Open... +&Openen... + +Save &as... +Opslaan &als... + +&Quit +&Stoppen + +&File +&Bestand + +&View help +&Help weergeven + +&About +&Over + +&Help +&Help + +Usage: +Gebruik: + +1. Select folders to watch. +1. Selecteer mappen om te bekijken. + +2. Enter a command line. +2. Een opdrachtregel invoeren. + +3. Press 'Start'. +3. Druk op 'Start'. + +To get started just import a .ffs_batch file. +Om te beginnen gewoon een .ffs_batch-bestand importeren. + +Folders to watch: +Mappen om te bekijken: + +Add folder +Map toevoegen + +Remove folder +Map verwijderen + +Browse +Bladeren + +Select a folder +Selecteer een map + +Idle time (in seconds): +Inactieve tijd (in seconden): + +Idle time between last detected change and execution of command +Inactieve tijd tussen laatste gedetecteerde wijziging en uitvoering van de opdracht + +Command line: +Opdrachtregel: + + +The command is triggered if: +- files or subfolders change +- new folders arrive (e.g. USB stick insert) + + +De opdracht wordt geactiveerd als: +- bestanden of submappen wijzigen +- nieuwe mappen verschijnen (b.v. USB-stick wordt geplaatst) + + +&Start +&Start + +About +Over + +Build: %x +Build: %x + +All files +Alle bestanden + +Automated Synchronization +Automatische synchronisatie + +Directory monitoring active +Directory controle actief + +Waiting until all directories are available... +Wacht totdat alle directories beschikbaar zijn... + +Error +Fout + +&Restore +&Herstellen + +&Show error +&Toon fout + +Incorrect command line: +Onjuiste opdrachtregel: + +&Retry +&Probeer opnieuw + +File content +Bestandsinhoud + +File time and size +Datum en grootte bestand + +Two way +Twee richtingen + +Mirror +Spiegelen + +Update +Bijwerken + +Custom +Aangepast + +Multiple... +Meerdere... + +Moving file %x to %y +Bestand %x verplaatsen naar %y + +Moving folder %x to %y +Map %x verplaatsen naar %y + +Moving symbolic link %x to %y +Symbolische link %x verplaatsen naar %y + +Removing old versions... +Verwijderen van oude versies... + +Creating symbolic link %x +Creëren symbolische link %x + +Creating folder %x +Map %x maken + +Overwriting file %x +Overschrijven bestand %x + +Overwriting symbolic link %x +Symbolische link %x overschrijven + +Verifying file %x +Controleren van bestand %x + +Updating attributes of %x +Kenmerken van %x bijwerken + +Creating a Volume Shadow Copy for %x... +Maken van een volume schaduwkopie voor %x... + +Data verification error: %x and %y have different content. +Gegevens verificatiefout: %x en %y hebben verschillende inhoud. + +Target folder %x already existing. +Doelmap %x bestaat reeds. + +Cannot find folder %x. +Kan de map %x niet vinden. + +Target folder input field must not be empty. +Het invoerveld van de doelmap moet niet leeg zijn. + +Source folder %x not found. +Bronmap %x niet gevonden. + +Please enter a target folder for versioning. +Geef een doelmap voor versiebeheer. + +The following items have unresolved conflicts and will not be synchronized: +De volgende items hebben onopgeloste conflicten en zullen niet worden gesynchroniseerd: + +The following folders are significantly different. Make sure you are matching the correct folders for synchronization. +De volgende mappen zijn beduidend verschillend. Zorg ervoor dat u de juiste mappen voor synchronisatie geselecteerd hebt. + +Not enough free disk space available in: +Er is onvoldoende vrije schijfruimte beschikbaar in: + +Required: +Vereist: + +Available: +Beschikbaar: + +Multiple folder pairs write to a common subfolder. Please review your configuration. +Meerdere map-paren schrijven naar een gemeenschappelijk submap. Controleer uw configuratie. + +Synchronizing folder pair: +Synchroniseren van map-paar: + +Generating database... +Genereren database... + +job name +taaknaam + +Synchronization stopped +Synchronisatie gestopt + +Synchronization completed with errors +Synchronisatie met fouten voltooid + +Synchronization completed with warnings +Synchronisatie met waarschuwingen voltooid + +Nothing to synchronize +Niets om te synchroniseren + +Synchronization completed successfully +Synchronisatie succesvol voltooid + +Saving log file %x... +Opslaan logbestand %x... + +Stopped +Gestopt + +You can switch to FreeFileSync's main window to resolve this issue. +U kunt overschakelen naar het hoofdvenster van FreeFileSync om dit probleem op te lossen. + +&Don't show this warning again +&Deze waarschuwing niet opnieuw weergeven + +&Ignore +&Negeren + +&Switch +&Wisselen + +Switching to FreeFileSync's main window +Overschakelen naar het hoofdvenster van FreeFileSyncs + + +Automatic retry in 1 second... +Automatic retry in %x seconds... + + +Automatisch opnieuw proberen in 1 seconde... +Automatisch opnieuw proberen in %x seconden... + + +&Ignore subsequent errors +&Latere fouten negeren + +Retrying operation... +Bewerking opnieuw proberen... + +Serious Error +Ernstige fout + +Check for Program Updates +Controleer voor programma-updates + +A new version of FreeFileSync is available: +Er is een nieuwe versie van FreeFileSync beschikbaar: + +Download now? +Nu downloaden? + +&Download +&Downloaden + +FreeFileSync is up to date. +FreeFileSync is actueel. + +Unable to connect to sourceforge.net. +Kan geen verbinding maken met sourceforge.net. + +Cannot find current FreeFileSync version number online. Do you want to check manually? +Kan online niet het actuele FreeFileSync versienummer vinden. Wilt u handmatig controleren? + +&Check +&Controleren + +Symlink +Symlink + +Folder +Map + +Full path +Volledig pad + +Name +Naam + +Relative folder +Relatieve map + +Base folder +Basismap + +Size +Grootte + +Date +Datum + +Extension +Uitbreiding + +Category +Categorie + +Action +Actie + +Drag && drop +Slepen && neerzetten + +Local comparison settings +Lokale vergelijking instellingen + +Local synchronization settings +Lokale synchronisatie-instellingen + +Local filter +Lokale filter + +Active +Actieve + +None +Geen + +Remove local settings +Lokale instellingen verwijderen + +Clear local filter +Verwijder lokale filter + +Copy +Kopieer + +Paste +Plakken + +Local Synchronization Settings +Lokale synchronisatie-instellingen + +&New +&Nieuw + +&Save +&Opslaan + +Save as &batch job... +Opslaan als &batchverwerking... + +Start &comparison +Start &vergelijking + +Start &synchronization +Start &synchronisatie + +&Options +&Opties + +&Language +&Taal + +&Find... +&Vinden... + +&Reset layout +&Reset lay-out + +&Export file list... +&Exporteren bestandslijst... + +&Tools +&Gereedschap + +&Check for new version +&Controleren op nieuwe versie + +&Check now +&Nu conroleren + +Check &automatically once a week +Controleer eens per week &automatisch + +Cancel +Annuleren + +Compare +Vergelijken + +Synchronize +Synchroniseren + +Add folder pair +Map-paar toevoegen + +Remove folder pair +Verwijder map-paar + +Swap sides +Verwisselen van zijden + +Close search bar +Sluit de zoekbalk + +Find: +Zoeken: + +Match case +Hoofdlettergevoelig + +New +Nieuw + +Open... +Openen... + +Save +Opslaan + +Save as... +Opslaan als... + +View type: +Weergavetype: + +Select view: +Selecteer weergave: + +Statistics: +Statistieken: + +Number of files and folders that will be deleted +Aantal bestanden en mappen die worden verwijderd + +Number of files that will be overwritten +Aantal bestanden die worden overschreven + +Number of files and folders that will be created +Aantal bestanden en mappen die worden gemaakt + +Total bytes to copy +Totaal aantal te kopiëren bytes + +Use local settings: +Gebruik lokale instellingen: + +Select a variant: +Selecteer een variant: + +Identify equal files by comparing modification time and size. +Identificeer gelijke bestanden door het vergelijken van wijzigingen in tijd en grootte. + +Identify equal files by comparing the file content. +Identificeer gelijke bestanden door het vergelijken van de bestandsinhoud. + +Ignore time shift (in hours) +Negeer tijdverschuiving (in uren) + +Consider file times with specified offset as equal +Overweeg bestand-tijden met gespecificeerde verspringing als gelijkwaardig te beschouwen + +Handle daylight saving time +Zomertijd gebruiken + +Symbolic links: +Symbolische koppelingen: + +More information +Meer informatie + +Local settings: +Lokale instellingen: + +Include: +Omvatten: + +Exclude: +Uitgesloten: + +Show examples +Toon voorbeelden + +Time span: +Tijdsduur: + +File size: +Bestandsgrootte: + +Minimum: +Minimum: + +Maximum: +Maximum: + +C&lear +&Wissen + +Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. +Selecteer filterregels om bepaalde bestanden uit te sluiten van synchronisatie. Voer bestandspaden in ten opzichte van hun bijbehorend map-paar. + +Detect moved files +Verplaatste bestanden detecteren + + +- Requires and creates database files +- Detection active after initial sync +- Not supported by all file systems + + +- Vereist en maakt database bestanden. +- Detectie actief na intitiële syncronisatie +- Niet ondersteund door alle bestandssystemen + + +Detect synchronization directions with the help of database files +Detecteren van synchronisatie richtingen met behulp van databasebestanden + +Delete files: +Bestanden verwijderen: + +&Permanent +&Permanent + +Delete or overwrite files permanently +Bestanden permanent verwijderen of overschrijven + +&Recycle bin +&Prullenbak + +Back up deleted and overwritten files in the recycle bin +Back-up de verwijderde en overschreven bestanden in de prullenbak + +&Versioning +&Versiebeheer + +Move files to a user-defined folder +Verplaats bestanden naar een door de gebruiker gedefinieerde map + +Naming convention: +Naamgevingsconventie: + +Handle errors: +Fouten afhandelen: + +Hide all error and warning messages +Verberg alle foutberichten en waarschuwingen + +&Pop-up +&Pop-up + +Show pop-up on errors or warnings +Toon pop-up bij fouten of waarschuwingen + +On completion: +Na voltooiing: + +OK +OK + +Start synchronization now? +Synchronisatie nu starten? + +Variant: +Variant: + +&Don't show this dialog again +&Dit dialoogvenster niet opnieuw weergeven + +Items found: +Gevonden objecten: + +Time remaining: +Resterende tijd: + +Time elapsed: +Verstreken tijd: + +Synchronizing... +Synchroniseren... + +Minimize to notification area +Minimaliseren naar systeemvak + +Bytes copied: +Gekopieerde bytes: + +Close +Sluiten + +&Pause +&Pauze + +Stop +Stop + +Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x +Creërt een batch-bestand om zonder toezicht te synchroniseren. Dubbelklikt u op het bestand om te starten of maak een opdracht in de taakplanner: %x + +&Stop +&Stop + +Stop synchronization at first error +Synchronisatie bij eerste fout stoppen + +Run minimized +Geminimaliseerd uitvoeren + +Save log: +Logbestand opslaan: + +Limit: +Limiet: + +Limit maximum number of log files +Maximaal aantal logboekbestanden limiteren + +How can I schedule a batch job? +Hoe kan ik een batchverwerking plannen? + +The following settings are used for all synchronization jobs. +De volgende instellingen worden gebruikt voor alle synchronisatie opdrachten. + +Fail-safe file copy +Fail-safe bestand kopiëren + + +Copy to a temporary file (*.ffs_tmp) before overwriting target. +This guarantees a consistent state even in case of a serious error. + + +Kopiëren naar een tijdelijk bestand (*.ffs_tmp) voordat het doel wordt overschreven. +Dit garandeert een consistente status zelfs in het geval van een ernstige fout. + + +(recommended) +(aanbevolen) + +Copy locked files +Vergrendelde bestanden kopiëren + +Copy shared or locked files using the Volume Shadow Copy Service. +Gedeelde of vergrendelde bestanden met behulp van de Volume Schaduw copy-Service kopiëren. + +(requires administrator rights) +(vereist beheerdersrechten) + +Copy file access permissions +Toegangsmachtigingen om bestanden te kopiëren + +Transfer file and folder permissions. +Overdracht van bestands- en mapmachtigingen. + +Automatic retry on error: +Automatisch opnieuw proberen bij fouten: + +Retry count: +Aantal herhaalpogingen: + +Delay (in seconds): +Vertraging (in seconden): + +Customize context menu: +Contextmenu aanpassen: + +Description +Beschrijving + +Show hidden dialogs again +Verborgen dialoogvensters weergeven + +Show all permanently hidden dialogs and warning messages again +Alle permanent verborgen dialogen en waarschuwingsberichten opnieuw weergeven + +&Default +&Standaard + +Source code written in C++ using: +Broncode werd in C++ geschreven met: + +If you like FreeFileSync +Als FreeFileSync je bevalt + +Donate with PayPal +Doneer met PayPal + +Feedback and suggestions are welcome +Feedback en suggesties zijn welkom + +Homepage +Homepage + +Email +Email + +Published under the GNU General Public License +Gepubliceerd onder de GNU General Public License + +Many thanks for localization: +Hartelijk dank voor de lokalisatie: + +Save as Batch Job +Opslaan als batch opdracht + +Delete Items +Items verwijderen + +Options +Opties + +Select Time Span +Selecteer tijdsduur + +&Preferences... +&Voorkeuren... + +Folder Pairs +Map-paren + +Find +Zoeken + +View Settings +Instellingen bekijken + +Overview +Overzicht + +Configuration +Configuratie + +Main Bar +Hoofd balk + +Start comparison +Vergelijking starten + +Comparison settings +Vergelijkings-instellingen + +Synchronization settings +Synchronisatie-instellingen + +Start synchronization +Synchronisatie starten + +Confirm +Bevestigen + + +Do you really want to execute the command %y for one item? +Do you really want to execute the command %y for %x items? + + +Wilt u echt het commando %y voor één item uitvoeren? +Wilt u echt het commando %y voor %x items uitvoeren? + + +&Execute +&Uitvoeren + + +1 directory +%x directories + + +1 directory +%x directories + + + +1 file +%x files + + +1 bestand +%x bestanden + + + +Showing %y of 1 row +Showing %y of %x rows + + +%y van 1 rij weergeven +%y van %x rijen weergeven + + +Set direction: +Richting instellen: + +multiple selection +meervoudige selectie + +Include via filter: +Via filter bijvoegen: + +Exclude via filter: +Uitsluiten via filter: + +Include temporarily +Tijdelijk opnemen + +Exclude temporarily +Tijdelijk uitsluiten + +Delete +Verwijderen + +Include all +Omvat alle + +Exclude all +Alle uitgesloten + +Show icons: +Pictogrammen weergeven: + +Small +Klein + +Medium +Medium + +Large +Groot + +Select time span... +Selecteer tijdsduur... + +Show "%x" +Toon "%x" + +Last session +Laatste sessie + +Folder Comparison and Synchronization +Map vergelijken en synchroniseren + +Configuration saved +Configuratie opgeslagen + +FreeFileSync batch +FreeFileSync Batch + +Do you want to save changes to %x? +Wilt u de wijzigingen in %x opslaan? + +Never save &changes +&wijzigingen nooit opslaan + +Do&n't save +&Niet opslaan + +Delete selected configurations +Verwijder geselecteerde configuraties + +Synchronization Settings +Synchronisatie-instellingen + +Clear filter +Filter wissen + +Show files that exist on left side only +Toon bestanden die bestaan alleen aan de linkerkant + +Show files that exist on right side only +Toon bestanden die bestaan alleen aan de rechterkant + +Show files that are newer on left +Bestanden die nieuwer zijn aan de linkerzijde weergeven + +Show files that are newer on right +Bestanden die nieuwer zijn aan de rechterzijde weergeven + +Show files that are equal +Bestanden die gelijk zijn weergeven + +Show files that are different +Bestanden die verschillend zijn weergeven + +Show conflicts +Conflicten weergeven + +Show files that will be created on the left side +Bestanden die gemaakt worden aan de linkerkant weergeven + +Show files that will be created on the right side +Bestanden die gemaakt worden aan de rechterkant weergeven + +Show files that will be deleted on the left side +Bestanden die worden verwijderd aan de linker kant weergeven + +Show files that will be deleted on the right side +Bestanden die worden verwijderd op de rechterkant weergeven + +Show files that will be overwritten on left side +Bestanden die overschreven worden aan de linkerzijde weergeven + +Show files that will be overwritten on right side +Bestanden die overschreven worden aan de rechterzijde weergeven + +Show files that won't be copied +Bestanden weergeven die niet gekopieerd worden + +Show filtered or temporarily excluded files +Toon gefilterde of tijdelijk uitgesloten bestanden + +Set as default +Als standaard instellen + +Filter +Filter + +All files are in sync +Alle bestanden zijn in synchronisatie + +Cannot find %x +Kan %x niet vinden. + +Comma-separated values +Door komma gescheiden waarden + +File list exported +Bestandenlijst geëxporteerd + +Searching for program updates... +Programma-updates zoekt... + +Close progress dialog +Voortgang dialoogvenster sluiten + +Standby +Standby + +Log off +Afmelden + +Shut down +Afsluiten + +Hibernate +Slaapstand + +Scanning... +Scannen... + +Comparing content... +Inhoud vergelijken... + +Info +Info + +Warning +Waarschuwing + +Select all +Alles selecteren + +Paused +Onderbroken + +Initializing... +Initialiseren... + +Completed +Voltooid + +&Continue +&Doorgaan + +Progress +Voortgang + +Log +Logboek + + +Do you really want to move the following item to the recycle bin? +Do you really want to move the following %x items to the recycle bin? + + +Wilt u het volgende item echt naar de prullenbak verplaatsen? +Wilt u de volgende %x items echt naar de prullenbak verplaatsen? + + +Move +Verplaatsen + + +Do you really want to delete the following item? +Do you really want to delete the following %x items? + + +Wilt u het volgende item echt verwijderen? +Wilt u de volgende %x items echt verwijderen? + + +Preferences +Voorkeuren + +Copy NTFS permissions +Kopiëren van NTFS-machtigingen + +Integrate external applications into context menu. The following macros are available: +Externe applicaties integreren in het contextmenu. De volgende macro's zijn beschikbaar: + +- full file or folder name +- volledige bestands- of map-naam + +- folder part only +- alleen map-deel + +- Other side's counterpart to %item_path% +- Andere kant van de tegenhanger naar %item_path% + +- Other side's counterpart to %item_folder% +- Andere kant van de tegenhanger naar %item_folder% + +Show hidden dialogs and warning messages again? +Verborgen vensters en waarschuwingsberichten opnieuw weergeven? + +&Show +&Toon + +Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. +Identificeren en doorgeven van veranderingen aan beide kanten. Verwijderingen, verplaatsingen en conflicten worden automatisch gedetecteerd met behulp van een database. + +Create a mirror backup of the left folder by adapting the right folder to match. +Maak een spiegel-backup van de linkermap, door de rechtermap aan te aanpassen. + +Copy new and updated files to the right folder. +Nieuwe en bijgewerkte bestanden naar de rechter map kopiëren. + +Configure your own synchronization rules. +Configureer uw eigen synchronisatie regels. + +Exclude +Uitsluiten + +Direct +Direct + +Follow +Volgen + +Today +Vandaag + +This week +Deze week + +This month +Deze maand + +This year +Dit jaar + +Last x days +Laatste x dagen + +Byte +Byte + +KB +KB + +MB +MB + +Replace +Vervangen + +Move files and replace if existing +Verplaats bestanden en vervang bestaande + +Time stamp +Tijdstempel + +Append a time stamp to each file name +Aan elke bestandsnaam een tijdstempel toevoegen + +Comparison +Vergelijking + +Synchronization +Synchronisatie + +Leave as unresolved conflict +Laten als onopgelost conflicht + +File +Bestand + +YYYY-MM-DD hhmmss +JJJJ-MM-DD hhmmss + +Files +Bestanden + +Items +Items + +Percentage +Percentage + +Cannot monitor directory %x. +Kan map %x niet bewaken. + +Conversion error: +Conversie fout: + +Cannot delete file %x. +Kan bestand %x niet verwijderen. + +The file is locked by another process: +Het bestand is vergrendeld door een ander proces: + +Cannot move file %x to %y. +Het bestand %x kan niet naar %y verplaatst worden. + +Cannot delete directory %x. +De map %x kan niet verwijderd worden. + +Cannot write file attributes of %x. +De bestandskenmerken voor %x kunnen niet geschreven worden. + +Cannot write modification time of %x. +De modificatie-tijd van %x kan niet geschreven worden. + +Cannot read security context of %x. +Kan de beveiligingscontext van %x niet lezen. + +Cannot write security context of %x. +Kan de beveiligingscontext van %x niet schrijven. + +Cannot read permissions of %x. +Kan de aangegeven machtigingen van %x niet lezen. + +Cannot write permissions of %x. +Kan de aangegeven machtigingen van %x niet schrijven. + +Cannot create directory %x. +Kan de map %x niet maken. + +Cannot create symbolic link %x. +Kan de symbolische koppeling %x niet maken. + +Cannot find system function %x. +Kan de systeemfunctie %x niet vinden. + +Cannot copy file %x to %y. +Kan bestand %x niet kopieëren naar %y. + +Type of item %x is not supported: +Dit itemtype %x wordt niet ondersteund: + +Cannot resolve symbolic link %x. +Kan de symbolische koppeling %x niet oplossen. + +Cannot open directory %x. +Kan de folder %x niet openen. + +Cannot enumerate directory %x. +Kan de folder %x niet opsommen. + +%x TB +%x TB + +%x PB +%x PB + + +1 min +%x min + + +1 min. +%x min. + + + +1 hour +%x hours + + +1 uur +%x uren + + + +1 day +%x days + + +1 dag +%x dagen + + +Unable to register to receive system messages. +Niet in staat om de ontvangen systeemberichten te registreren. + +Cannot set privilege %x. +Kan geen privileges %x instellen. + +Unable to suspend system sleep mode. +Niet in staat om de systeem slaapstand op te schorten. + +Cannot change process I/O priorities. +De input/output prioriteiten kunnen niet gewijzigd worden. + +Unable to move %x to the recycle bin. +Niet in staat om %x naar de prullenbak te verplaatsen. + +Checking recycle bin failed for folder %x. +Controle van de prullenbak voor map %x is mislukt. + +Cannot determine final path for %x. +Kan niet het uiteindelijke pad voor %x bepalen. + +Error Code %x: +Foutcode %x: + +Cannot read the following XML elements: +Kan de volgende XML onderdelen niet lezen: + +Configuration file %x loaded partially only. +Configuratiebestand %x alleen gedeeltelijk ingelezen. + diff --git a/FreeFileSync/Build/Languages/german.lng b/FreeFileSync/Build/Languages/german.lng index bfcbaf8e..55c249eb 100644 --- a/FreeFileSync/Build/Languages/german.lng +++ b/FreeFileSync/Build/Languages/german.lng @@ -7,6 +7,15 @@ n == 1 ? 0 : 1 +Cannot copy symbolic link %x to %y. +Der Symbolische Link %x kann nicht nach %y kopiert werden. + +Unable to connect to freefilesync.org. +Es kann keine Verbindung zu freefilesync.org aufgebaut werden. + +Open configuration for edit without executing. +Konfiguration zum Editieren öffnen ohne auszuführen. + Both sides have changed since last synchronization. Beide Seiten wurden seit der letzten Synchronisation verändert. @@ -217,6 +226,9 @@ %x GB %x GB +Cannot load file %x. +Die Datei %x kann nicht geladen werden. + Database file %x is incompatible. Die Datenbankdatei %x ist nicht kompatibel. @@ -268,6 +280,9 @@ Creating file %x Erstelle Datei %x +Saving log file %x... +Speichere Protokolldatei %x... + Items processed: Verarbeitete Elemente: @@ -295,9 +310,6 @@ %x Threads -Encoding extended time information: %x -Speichere erweiterte Zeitinformation: %x - /sec /sek @@ -319,9 +331,6 @@ Please use FreeFileSync 64-bit version to create shadow copies on this system. Bitte benutzen Sie die FreeFileSync 64-Bit Version, um Schattenkopien auf diesem System zu erstellen. -Cannot load file %x. -Die Datei %x kann nicht geladen werden. - Cannot determine volume name for %x. Der Laufwerksname für %x kann nicht bestimmt werden. @@ -498,21 +507,21 @@ Die Befehlszeile wird ausgelöst, wenn: Data verification error: %x and %y have different content. Verifizierungsfehler: %x und %y haben unterschiedlichen Inhalt. -Cannot find folder %x. -Der Ordner %x wurde nicht gefunden. - Target folder %x already existing. Der Zielordner %x existiert bereits. +Cannot find folder %x. +Der Ordner %x wurde nicht gefunden. + Target folder input field must not be empty. Das Eingabefeld für den Zielordner darf nicht leer sein. -Please enter a target folder for versioning. -Bitte geben Sie einen Zielorder für die Versionierung ein. - Source folder %x not found. Der Quellordner %x wurde nicht gefunden. +Please enter a target folder for versioning. +Bitte geben Sie einen Zielorder für die Versionierung ein. + The following items have unresolved conflicts and will not be synchronized: Die folgenden Elemente haben ungelöste Konflikte und werden nicht synchronisiert werden: @@ -555,9 +564,6 @@ Die Befehlszeile wird ausgelöst, wenn: Synchronization completed successfully Synchronisation erfolgreich abgeschlossen -Saving log file %x... -Speichere Protokolldatei %x... - Stopped Unterbrochen @@ -609,9 +615,6 @@ Die Befehlszeile wird ausgelöst, wenn: FreeFileSync is up to date. FreeFileSync ist auf dem neuesten Stand. -Unable to connect to sourceforge.net. -Es kann keine Verbindung zu Sourceforge.net aufgebaut werden. - Cannot find current FreeFileSync version number online. Do you want to check manually? Die aktuelle FreeFileSync Versionsnummer wurde online nicht gefunden. Möchten Sie manuell prüfen? @@ -837,23 +840,23 @@ Die Befehlszeile wird ausgelöst, wenn: Maximum: Maximum: -C&lear -&Löschen - Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. Wählen Sie Filterregeln aus, um einzelne Dateien von der Synchronisation auszuschließen. Geben Sie Dateipfade relativ zu ihrem zugehörigen Ordnerpaar an. +C&lear +&Löschen + Detect moved files Verschobene Dateien erkennen -- Requires and creates database files - Detection active after initial sync +- Requires and creates database files - Not supported by all file systems +- Erkennung nach erstmaliger Synchronisation aktiv. - Benötigt und erstellt Datenbankdateien. -- Erst nach einmaliger Synchronisation aktiv. - Wird nicht von allen Dateisystemen unterstützt. @@ -1067,12 +1070,12 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. View Settings Ansichtseinstellungen -Overview -Übersicht - Configuration Konfiguration +Overview +Übersicht + Main Bar Hauptleiste @@ -1451,9 +1454,6 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. Cannot monitor directory %x. Das Verzeichnis %x kann nicht überwacht werden. -Conversion error: -Fehler bei Konvertierung: - Cannot delete file %x. Die Datei %x kann nicht gelöscht werden. @@ -1487,9 +1487,6 @@ Dadurch wird ein konsistenter Datenstand auch im schweren Fehlerfall garantiert. Cannot create directory %x. Das Verzeichnis %x kann nicht erstellt werden. -Cannot create symbolic link %x. -Der Symbolische Link %x kann nicht erstellt werden. - Cannot find system function %x. Die Systemfunktion %x wurde nicht gefunden. diff --git a/FreeFileSync/Build/Languages/norwegian.lng b/FreeFileSync/Build/Languages/norwegian.lng index e8fd452b..914184dc 100644 --- a/FreeFileSync/Build/Languages/norwegian.lng +++ b/FreeFileSync/Build/Languages/norwegian.lng @@ -7,12 +7,31 @@ n == 1 ? 0 : 1 +Cannot copy symbolic link %x to %y. + + + +- Detection active after initial sync +- Requires and creates database files +- Not supported by all file systems + + + C&lear Consider file times with specified offset as equal +Unable to connect to freefilesync.org. + + +Removing old log files exceeding limit... + + +Open configuration for edit without executing. + + Both sides have changed since last synchronization. Begge sider er endret siden siste synkronisering. @@ -223,6 +242,9 @@ %x GB %x GB +Cannot load file %x. +Kan ikke lese filen %x. + Database file %x is incompatible. Databasefilen %x er inkompatibel. @@ -274,6 +296,9 @@ Creating file %x Oppretter filen %x +Saving log file %x... +Lagrer rapport %x... + Items processed: Mapper behandlet: @@ -301,9 +326,6 @@ %x tråder -Encoding extended time information: %x -Oppretter udvidet tidsinformasjon: %x - /sec /sek @@ -325,9 +347,6 @@ Please use FreeFileSync 64-bit version to create shadow copies on this system. Bruk FreeFileSync 64-bit til å lage VSS kopier på dette system. -Cannot load file %x. -Kan ikke lese filen %x. - Cannot determine volume name for %x. Kan ikke bestemme volumnavn for %x. @@ -504,21 +523,21 @@ Kommandoen utføres hvis: Data verification error: %x and %y have different content. Godkjennelsesfeil: %x og %y har forskellig innhold -Cannot find folder %x. -Finner ikke mappen %x. - Target folder %x already existing. Destinasjonsmappen %x finnes allerede. +Cannot find folder %x. +Finner ikke mappen %x. + Target folder input field must not be empty. Destinasjonsmappen skal angis. -Please enter a target folder for versioning. -Angi mappe til versionering. - Source folder %x not found. Kildemappen %x ble ikke funnet. +Please enter a target folder for versioning. +Angi mappe til versionering. + The following items have unresolved conflicts and will not be synchronized: Følgende mapper har uløste konflikter og synkroniseres ikke: @@ -561,9 +580,6 @@ Kommandoen utføres hvis: Synchronization completed successfully Synkronisering gjennomført -Saving log file %x... -Lagrer rapport %x... - Stopped Avbrutt @@ -615,9 +631,6 @@ Kommandoen utføres hvis: FreeFileSync is up to date. FreeFileSync er oppdatert. -Unable to connect to sourceforge.net. -Kan ikke kontakte sourceforge.net. - Cannot find current FreeFileSync version number online. Do you want to check manually? Kunne ikke finne FreeFileSync's versjonsnummer online. Vil du kontrollere manuelt? @@ -846,17 +859,6 @@ Kommandoen utføres hvis: Detect moved files Gjennkjenn flyttede filer - -- Requires and creates database files -- Detection active after initial sync -- Not supported by all file systems - - -- Krever og oppretter databasefiler -- Gjennkjennelse aktiv etter første synk -- Understøtter ikke alle filsystemer - - Detect synchronization directions with the help of database files Gjennkjenn synkretninger ved hjelp av databasefiler @@ -1064,12 +1066,12 @@ This guarantees a consistent state even in case of a serious error. View Settings Visninger -Overview -Oversikt - Configuration Innstilling +Overview +Oversikt + Main Bar Hovedlinje @@ -1448,9 +1450,6 @@ This guarantees a consistent state even in case of a serious error. Cannot monitor directory %x. Kan ikke overvåke mappen %x. -Conversion error: -Konverteringsfeil: - Cannot delete file %x. Kan ikke slette filen %x. @@ -1484,9 +1483,6 @@ This guarantees a consistent state even in case of a serious error. Cannot create directory %x. Kan ikke opprette mappen %x. -Cannot create symbolic link %x. -Kan ikke opprette symbolsk link %x. - Cannot find system function %x. Kan ikke finne systemfunksjonen %x. diff --git a/FreeFileSync/Build/Languages/outdated/dutch.lng b/FreeFileSync/Build/Languages/outdated/dutch.lng deleted file mode 100644 index d9802c29..00000000 --- a/FreeFileSync/Build/Languages/outdated/dutch.lng +++ /dev/null @@ -1,1573 +0,0 @@ -
    - Nederlands - Edwin Dierssen - nl_NL - flag_holland.png - 2 - n == 1 ? 0 : 1 -
    - -&Show - - -Show hidden dialogs and warning messages again? - - -Clear filter - - -Start comparison - - -Show all permanently hidden dialogs and warning messages again - - -Show hidden dialogs again - - -&Stop - - -Bytes copied: - - -&Pop-up - - -&Versioning - - -&Permanent - - -C&lear - - -Handle daylight saving time - - -Consider file times with specified offset as equal - - -Ignore time shift (in hours) - - -New - - -&Reset layout - - -Start &synchronization - - -Start &comparison - - -Clear local filter - - -Relative folder - - -Multiple folder pairs write to a common subfolder. Please review your configuration. - - -Cannot find folder %x. - - -&File - - -The following folder paths are dependent from each other: - - -Both sides have changed since last synchronization. -Beide zijdes zijn veranderd sinds de laatste synchronisatie. - -Cannot determine sync-direction: -Kan de synchronisatie-richting niet bepalen: - -No change since last synchronization. -Geen veranderingen sinds de laatste synchronisatie. - -The database entry is not in sync considering current settings. -De database is niet gesynchroniseerd volgens de huidige instellingen. - -Setting default synchronization directions: Old files will be overwritten with newer files. -Stel standaard synchronisatie richtingen in: Oude bestanden worden door nieuwere bestanden overschreven. - -Checking recycle bin availability for folder %x... -Prullebak beschikbaarheid voor map %x te controleren... - -Moving file %x to the recycle bin -Bezig met verplaatsen van bestand %x naar de prullenbak - -Moving folder %x to the recycle bin -Bezig met verplaatsen van map %x naar de prullenbak - -Moving symbolic link %x to the recycle bin -Bezig met verplaatsen van snelkoppeling %x naar de prullenbak - -Deleting file %x -Verwijderen van bestand %x - -Deleting folder %x -Verwijderen van map %x - -Deleting symbolic link %x -Verwijderen van snelkoppeling %x - -The recycle bin is not available for the following folders. Files will be deleted permanently instead: -De prullenbak is niet beschikbaar voor de volgende mappen. Bestanden zullen permanent verwijderd worden: - -An exception occurred -Er heeft een uitzondering plaatsgevonden - -A directory path is expected after %x. -Een bestandslocatie pad is verwacht na %x. - -Syntax error -Syntax fout - -Cannot open file %x. -Kan bestand %x niet openen. - -File %x does not contain a valid configuration. -Bestand %x bevat geen valide configuratie. - -Unequal number of left and right directories specified. -Oneven nummer van linker en rechter bestandslocaties gespecificeerd. - -The config file must not contain settings at directory pair level when directories are set via command line. -Het configuratiebestand mag geen instellingen bevatten voor een bestandslocatie level als bestandslocaties in de command line ingesteld zijn. - -Directories cannot be set for more than one configuration file. -Bestandslocaties kunnen niet voor meer dan een configuratiebestand gemaakt worden. - -Command line -Opdrachtregel - -Syntax: -Syntax: - -global config file: -Globaal configuratiebestand: - -config files: -configuratiebestanden: - -directory -bestandslocatie - -Path to an alternate GlobalSettings.xml file. -Pad naar alternatief GlobalSettings.xml bestand. - -Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files. -Elk aantal FreeFileSync .ffs_gui en/of .ffs_batch configuratiebestanden. - -Any number of alternative directory pairs for at most one config file. -Willekeurig aantal gekoppelde mappen voor één configuratie bestand. - -Cannot find the following folders: -Kan de volgende mappen niet vinden: - -You can ignore this error to consider each folder as empty. The folders then will be created automatically during synchronization. -U kunt deze foutmelding negeren om elke map als leeg te markeren. De mappen worden dan automatisch aangemaakt tijdens de synchronisatie. - -A folder input field is empty. -Een map invoerveld is leeg. - -The corresponding folder will be considered as empty. -De overeenkomstige map zal als leeg worden beschouwd. - -File %x has an invalid date. -Bestand %x heeft een ongeldige datum. - -Date: -Datum: - -Files %x have the same date but a different size. -Bestanden %x hebben dezelfde datums maar een afwijkende grootte. - -Size: -Grootte: - -Content comparison was skipped for excluded files %x. -Vergelijking voor uitgesloten bestanden %x is overgeslagen. - -Items differ in attributes only -Items verschillen alleen in attributen - -Resolving symbolic link %x -Bezig met vinden van snelkoppeling %x - -Comparing content of files %x -De inhoud van %x bestanden wordt vergeleken - -Generating file list... -Genereren van bestandslijst... - -Starting comparison -Vergelijking starten - -Calculating sync directions... -Synchronisatie richtingen calculeren... - -Out of memory. -Onvoldoende geheugen. - -Item exists on left side only -Item bestaat alleen aan de linkerkant - -Item exists on right side only -Item bestaat alleen aan de rechterkant - -Left side is newer -De linkerkant is nieuwer - -Right side is newer -De rechterkant is nieuwer - -Items have different content -De items hebben een andere inhoud - -Both sides are equal -Beide kanten zijn gelijk - -Conflict/item cannot be categorized -Conflict/item kan niet worden gecategoriseerd - -Copy new item to left -Kopieër nieuw item naar de linkerkant - -Copy new item to right -Kopieër nieuw item naar de rechterkant - -Delete left item -Verwijder linker item - -Delete right item -Verwijder rechter item - -Move file on left -Verplaats bestand aan de linkerkant - -Move file on right -Verplaats bestand aan de rechterkant - -Overwrite left item -Overschrijf linker item - -Overwrite right item -Overschrijf rechter item - -Do nothing -Geen actie ondernemen - -Update attributes on left -Update attributen aan de linkerkant - -Update attributes on right -Update attributen aan de rechterkant - - -1 byte -%x bytes - - -1 byte -%x bytes - - -%x MB -%x MB - -%x KB -%x KB - -%x GB -%x GB - -Cannot load file %x. -Kan bestand %x niet laden. - -Database file %x is incompatible. -Databasebestand %x is niet compatibel. - -Initial synchronization: -Initiële synchronisatie: - -Database file %x does not yet exist. -Databasebestand %x bestaat nog niet. - -Database file is corrupt: -Databasebestand is corrupt: - -Cannot write file %x. -Kan bestand %x niet schrijven. - -Cannot read file %x. -Kan bestand %x niet vinden. - -Database files do not share a common session. -Databasebestanden delen geen gezamelijke sessie. - -Searching for folder %x... -Bezig met zoeken naar map %x... - -Cannot read file attributes of %x. -Kan bestandskenmerken van %x niet uitlezen. - -Cannot get process information. -Kan geen procesinformatie verkrijgen. - -Waiting while directory is locked: -Wachten terwijl directory gelocked is: - -Lock owner: -Lock eigenaar: - - -1 sec -%x sec - - -1 sec -%x sec - - -Detecting abandoned lock... -Detecteren van achter gelaten lock... - -Creating file %x -Bestand %x wordt aangemaakt - -Items processed: -Onderdelen verwerkt: - -Items remaining: -Onderdelen te gaan: - -Total time: -Totale tijd: - -Error parsing file %x, row %y, column %z. -Fout bij het parsen van bestand %x, rij %y, kolom %z. - -Cannot set directory lock for %x. -Kan locatie %x niet op slot zetten. - -Scanning: -Doorzoekt: - - -1 thread -%x threads - - -1 thread -%x threads - - -Encoding extended time information: %x -Coderen uitgebreide tijdinformatie: %x - -/sec -/sec - -%x items/sec -%x items per seconde - -Show in Explorer -Toon in Verkenner - -Open with default application -Open met standaardapplicatie - -Browse directory -Verken map - -Cannot access the Volume Shadow Copy Service. -Kan de Volume Shadow Copy Service niet benaderen. - -Please use FreeFileSync 64-bit version to create shadow copies on this system. -Gebruik alstublieft de FreeFileSync 64-bit versie om schaduwkopieën te maken op dit systeem. - -Cannot determine volume name for %x. -Kan volumenaam voor %x niet bepalen. - -Volume name %x is not part of file path %y. -Schijfnaam %x is geen deel van bestandspad %y. - -Stop requested: Waiting for current operation to finish... -Stop aangegeven: Wachten tot de huidige handeling klaar is... - -Unable to create time stamp for versioning: -Aanmaken van timestamp voor versiebeheer mislukt: - -&Open... -&Open... - -Save &as... -Ops&laan als... - -&Quit -&Afsluiten - -&View help -Help &bekijken - -&About -&Over - -&Help -&Help - -Usage: -Gebruik: - -1. Select folders to watch. -1. Selecteer mappen om te bekijken. - -2. Enter a command line. -2. Geef een opdrachtregel in. - -3. Press 'Start'. -3. Klik op 'Start'. - -To get started just import a .ffs_batch file. -Importeer een .ffs_batch bestand om te beginnen. - -Folders to watch: -Mappen om te bekijken: - -Add folder -Map toevoegen - -Remove folder -Verwijder map - -Browse -Verkennen - -Select a folder -Selecteer een map - -Idle time (in seconds): -Stationaire tijd (in seconden): - -Idle time between last detected change and execution of command -Tijd tussen de laatste gedetecteerde verandering en de uitvoering van het commando - -Command line: -Command line: - - -The command is triggered if: -- files or subfolders change -- new folders arrive (e.g. USB stick insert) - - -De opdracht word geactiveerd als: -- bestanden of subfolders veranderen -- nieuwe folders worden gevonden (bijvoorbeeld bij invoeging van USB stick) - - -&Start -&Start - -About -Informatie - -Build: %x -Build: %x - -All files -Alle bestanden - -Automated Synchronization -Automatische Synchronisatie - -Directory monitoring active -Bestandslocatie controle actief - -Waiting until all directories are available... -Wachten tot alle bestandslocaties beschikbaar zijn... - -Error -Fout - -&Restore -&Herstellen - -&Show error -Laat &fouten zien - -Incorrect command line: -Incorrectie command line: - -&Retry -&Opnieuw proberen - -File content -Bestandsinhoud - -File time and size -Bestandstijd- en grootte - -Two way -Twee kanten op - -Mirror -Spiegelen - -Update -Bijwerken - -Custom -Aangepast - -Multiple... -Meerdere... - -Moving file %x to %y -Bezig met verplaatsen van bestand %x naar %y - -Moving folder %x to %y -Bezig met verplaatsen van map %x naar %y - -Moving symbolic link %x to %y -Bezig met verplaatsen van snelkoppeling %x naar %y - -Removing old versions... -Bezig met verwijderen van oude versies... - -Creating symbolic link %x -Snelkoppeling %x wordt aangemaakt - -Creating folder %x -Map %x wordt aangemaakt - -Overwriting file %x -Bezig met overschrijven van bestand %x - -Overwriting symbolic link %x -Bezig met overschrijven van snelkoppeling %x - -Verifying file %x -Verifieert bestand %x - -Updating attributes of %x -Attributen bijwerken van %x - -Creating a Volume Shadow Copy for %x... -Aanmaken van een Volume Shadow Copy voor %x... - -Data verification error: %x and %y have different content. -Data verificatie fout: %x en %y hebben een verschillende inhoud. - -Target folder %x already existing. -Doelmap %x bestaat al. - -Target folder input field must not be empty. -Doelmap mag niet leeg zijn. - -Source folder %x not found. -Bronmap %x niet gevonden. - -Please enter a target folder for versioning. -Selecteer alstublieft een doelmap voor versiebeheer. - -The following items have unresolved conflicts and will not be synchronized: -De volgende items hebben onopgeloste conflicten en zullen niet worden gesynchroniseerd: - -The following folders are significantly different. Make sure you are matching the correct folders for synchronization. -De volgende mappen zijn heel verschillend. Wees er zeker van dat dit de goede mappen zijn voor synchronisatie. - -Not enough free disk space available in: -Niet genoeg vrije schijfruimte beschikbaar op: - -Required: -Vereist: - -Available: -Beschikbaar: - -Synchronizing folder pair: -Bezig met synchroniseren van folder paar: - -Generating database... -Genereren van database... - -job name -taaknaam - -Synchronization stopped -Synchronisatie gestopt - -Synchronization completed with errors -Synchronisatie is met fouten afgerond - -Synchronization completed with warnings -Synchronisatie afgerond met waarschuwingen - -Nothing to synchronize -Niets om te synchroniseren - -Synchronization completed successfully -Synchronisatie succesvol - -Saving log file %x... -Opslaan van logbestand %x... - -Stopped -Gestopt - -You can switch to FreeFileSync's main window to resolve this issue. -U kunt naar het hoofdvenster overschakelen om dit probleem op te lossen. - -&Don't show this warning again -Laat deze &waarschuwing niet meer zien - -&Ignore -&Negeren - -&Switch -&Omschakelen - -Switching to FreeFileSync's main window -Overschakelen naar FreeFileSync's hoofdvenster - - -Automatic retry in 1 second... -Automatic retry in %x seconds... - - -Automatisch opnieuw proberen in 1 seconde... -Automatisch opnieuw proberen in %x seconden... - - -&Ignore subsequent errors -&Negeer volgende foutmeldingen - -Retrying operation... -Handeling opnieuw proberen... - -Serious Error -Serieuze Error - -Check for Program Updates -Controleer op programma updates - -A new version of FreeFileSync is available: -Er is een nieuwe versie van FreeFileSync beschikbaar: - -Download now? -Nu downloaden? - -&Download -&Download - -FreeFileSync is up to date. -U gebruikt de nieuwste versie van FreeFileSync. - -Unable to connect to sourceforge.net. -Kan geen verbinding maken met sourceforge.net. - -Cannot find current FreeFileSync version number online. Do you want to check manually? -Kan huidige FreeFileSync versienummer niet online vinden. Wilt u handmatig controleren? - -&Check -&Check - -Symlink -Symlink - -Folder -Map - -Full path -Volledig pad - -Name -Naam - -Base folder -Hoofdmap - -Size -Grootte - -Date -Datum - -Extension -Extensie - -Category -Categorie - -Action -Actie - -Drag && drop -Drag en drop - -Local comparison settings -Lokale vergelijkings instellingen - -Local synchronization settings -Lokale synchronisatie instellingen - -Local filter -Lokale filter - -Active -Actieve - -None -Geen - -Remove local settings -Verwijder lokale instellingen - -Copy -Kopiëren - -Paste -Plakken - -Local Synchronization Settings -Lokale synchronisatie instellingen - -&New -&Nieuw - -&Save -O&pslaan - -Save as &batch job... -Opslaan als &batch opdracht... - -&Options -&Opties - -&Language -&Taal - -&Find... -&Zoek... - -&Export file list... -&Exporteer bestandslijst... - -&Tools -&Gereedschap - -&Check for new version -&Controleer voor nieuwe versie - -&Check now -&Controleer nu - -Check &automatically once a week -Controleer &automatisch eens per week - -Cancel -Annuleren - -Compare -Vergelijk - -Synchronize -Synchroniseer - -Add folder pair -Voeg gekoppelde mappen toe - -Remove folder pair -Verwijder gekoppelde mappen - -Swap sides -Wissel zijdes - -Close search bar -Sluit zoekbalk - -Find: -Zoek: - -Match case -Hoofdlettergevoelig - -Open... -Open... - -Save -Opslaan - -Save as... -Opslaan als... - -View type: -Laat type zien: - -Select view: -Selecteer uiterlijk: - -Statistics: -Statistieken: - -Number of files and folders that will be deleted -Aantal bestanden en mappen die verwijderd zullen worden - -Number of files that will be overwritten -Aantal bestanden die overschreven zullen worden - -Number of files and folders that will be created -Aantal bestanden en mappen die zullen worden aangemaakt - -Total bytes to copy -Aantal bytes om te kopiëren - -Use local settings: -Gebruik lokale instellingen: - -Select a variant: -Selecteer een variant: - -Identify equal files by comparing modification time and size. -Identificeer gelijke bestanden door bewerkingstijd en grootte te vergelijken. - -Identify equal files by comparing the file content. -Identificeer gelijke bestanden door de bestandsinhoud te vergelijken. - -Symbolic links: -Snelkoppelingen: - -More information -Meer informatie - -Local settings: -Lokale instellingen: - -Include: -Meerekenen: - -Exclude: -Uitsluiten: - -Show examples -Laat voorbeelden zien - -Time span: -Tijdspanne: - -File size: -Bestandsgrootte: - -Minimum: -Minimum: - -Maximum: -Maximum: - -Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair. -Selecteer filter regels om bepaalde bestanden uit te sluiten van synchronisatie. Geef bestandspaden in die relatief zijn aan hun corresponderende folderparen. - -Detect moved files -Detecteer verplaatste bestanden - - -- Requires and creates database files -- Detection active after initial sync -- Not supported by all file systems - - -- Vereist en creeërt database bestanden -- Detectie actief na eerste synchronisatie -- Niet ondersteund door alle bestandssystemen - - -Detect synchronization directions with the help of database files -Detecteer synchronisatierichtingen met behulp van databasebestanden - -Delete files: -Verwijder bestanden: - -Delete or overwrite files permanently -Bestanden definitief verwijderen of overschrijven - -&Recycle bin -&Prullebak - -Back up deleted and overwritten files in the recycle bin -Maak een backup van verwijderde en overschreven bestanden in de prullenbak - -Move files to a user-defined folder -Verplaats bestanden naar een gebruikers gedefineerde map - -Naming convention: -Naamgevingsconventie - -Handle errors: -Fouten afhandelen: - -Hide all error and warning messages -Verberg alle fout- en waarschuwingsberichten - -Show pop-up on errors or warnings -Laat pop-up zien bij foutmeldingen of waarschuwingen - -On completion: -Bij voltooiing: - -OK -OK - -Start synchronization now? -Start de synchronisatie nu? - -Variant: -Variant: - -&Don't show this dialog again -Laat deze dialoog &niet meer zien - -Items found: -Onderdelen gevonden: - -Time remaining: -Resterende tijd: - -Time elapsed: -Verstreken tijd: - -Synchronizing... -Synchroniseert... - -Minimize to notification area -Minimaliseer naar systeemvak - -Close -Sluiten - -&Pause -&Pauze - -Stop -Stop - -Create a batch file for unattended synchronization. To start, double-click this file or schedule in a task planner: %x -Maak een batchbestand voor onbeheerde synchronisatie. Om te starten dubbelklikt u dit bestand of rooster in een taakplanner: %x - -Stop synchronization at first error -Stop synchronisatie bij eerste foutmelding - -Run minimized -Draai geminimaliseerd - -Save log: -Bewaar logbestand: - -Limit: -Beperk: - -Limit maximum number of log files -Limiteer maximaal aantal log bestanden - -How can I schedule a batch job? -Hoe kan ik een batch opdracht inroosteren? - -The following settings are used for all synchronization jobs. -De volgende instellingen worden gebruikt voor alle synchronisaties. - -Fail-safe file copy -Fail-safe bestandskopie - - -Copy to a temporary file (*.ffs_tmp) before overwriting target. -This guarantees a consistent state even in case of a serious error. - - -Kopiëer naar een tijdelijk bestand (*.ffs_tmp) voor je het doel overschrijft. -Dit garandeert een consistente staat, zelfs in het geval van een serieuze fout. - - -(recommended) -(aanbevolen) - -Copy locked files -Kopiëer vergrendelde bestanden - -Copy shared or locked files using the Volume Shadow Copy Service. -Kopiëer gedeelde of vergrendelde bestanden met behulp van de Volume Shadow Copy Service. - -(requires administrator rights) -(vereist administrator rechten) - -Copy file access permissions -Kopiëer toegangsrechten van bestand. - -Transfer file and folder permissions. -Draag bestands- en maprechten over. - -Automatic retry on error: -Probeer automatisch opnieuw bij foutmelding: - -Retry count: -Aantal pogingen: - -Delay (in seconds): -Vertraging (in seconden): - -Customize context menu: -Contextmenu aanpassen: - -Description -Omschrijving - -&Default -&Standaard - -Source code written in C++ using: -Broncode geschreven in C++ met behulp van: - -If you like FreeFileSync -Indien FreeFileSync u bevalt - -Donate with PayPal -Doneer met PayPal - -Feedback and suggestions are welcome -Feedback en suggesties zijn welkom - -Homepage -Homepage - -Email -E-mail - -Published under the GNU General Public License -Gepubliceerd onder de GNU General Public License - -Many thanks for localization: -Veel dank voor de vertalingen: - -Save as Batch Job -Opslaan als batch opdracht - -Delete Items -Verwijder items - -Options -Opties - -Select Time Span -Selecteer tijdspanne - -&Preferences... -&Voorkeuren... - -Folder Pairs -Map koppelingen - -Find -Vind - -View Settings -Instellingen - -Overview -Overzicht - -Configuration -Configuratie - -Main Bar -Hoofdbalk - -Comparison settings -Vergelijksinstellingen - -Synchronization settings -Synchronisatieinstellingen - -Start synchronization -Start synchronisatie - -Confirm -Bevestigen - - -Do you really want to execute the command %y for one item? -Do you really want to execute the command %y for %x items? - - -Wilt u echt het commando %y uitvoeren voor 1 item? -Wilt u echt het commando %y uitvoeren voor %x items? - - -&Execute -&Uitvoeren - - -1 directory -%x directories - - -1 map -%x mappen - - - -1 file -%x files - - -1 bestand -%x bestanden - - - -Showing %y of 1 row -Showing %y of %x rows - - -Bezig met%y van 1 rij laten zien -Bezig met %y van %x rijen laten zien - - -Set direction: -Stel richting in: - -multiple selection -meervoudige selectie - -Include via filter: -Invoegen via filter: - -Exclude via filter: -Sluit via filter uit: - -Include temporarily -Tijdelijk opnemen - -Exclude temporarily -Tijdelijk uitsluiten - -Delete -Verwijderen - -Include all -Alles opnemen - -Exclude all -Alles uitsluiten - -Show icons: -Laat pictogrammen zien: - -Small -Klein - -Medium -Middel - -Large -Groot - -Select time span... -Selecteer tijdsspanne... - -Show "%x" -Toon "%x" - -Last session -Laatste sessie - -Folder Comparison and Synchronization -Mappen vergelijken en synchroniseren - -Configuration saved -Configuratie opgeslagen - -FreeFileSync batch -FreeFileSync taak - -Do you want to save changes to %x? -Wilt u de wijzigingen in %x opslaan? - -Never save &changes -Sla &veranderingen nooit op - -Do&n't save -&Niet opslaan - -Delete selected configurations -Verwijder geselecteerde configuraties - -Synchronization Settings -Synchronisatie instellingen - -Show files that exist on left side only -Toon bestanden die alleen aan de linkerzijde bestaan - -Show files that exist on right side only -Toon bestanden die alleen aan de rechterzijde bestaan - -Show files that are newer on left -Toon bestanden die aan de linkerzijde nieuwer zijn - -Show files that are newer on right -Toon bestanden die aan de rechterzijde nieuwer zijn - -Show files that are equal -Toon bestanden die gelijk zijn - -Show files that are different -Toon bestanden die verschillend zijn - -Show conflicts -Toon conflicten - -Show files that will be created on the left side -Toon bestanden die aan de linkerzijde aangemaakt zullen worden - -Show files that will be created on the right side -Toon bestanden die aan de rechterzijde aangemaakt zullen worden - -Show files that will be deleted on the left side -Toon bestanden die van de linkerzijde verwijderd zullen worden - -Show files that will be deleted on the right side -Toon bestanden die van de rechterzijde verwijderd zullen worden - -Show files that will be overwritten on left side -Toon bestanden die aan de linkerzijde overschreven zullen worden - -Show files that will be overwritten on right side -Toon bestanden die aan de rechterzijde overschreven zullen worden - -Show files that won't be copied -Toon bestanden die niet gekopiëerd zullen worden - -Show filtered or temporarily excluded files -Laat gefilterde of tijdelijk uitgesloten bestanden zien - -Set as default -Instellen als standaard - -Filter -Filter - -All files are in sync -Alle bestanden zijn gesynchroniseerd - -Cannot find %x -Kan %x niet vinden - -Comma-separated values -Door komma gescheiden waarden - -File list exported -Bestandslijst geëxporteerd - -Searching for program updates... -Bezig met zoeken naar programma updates... - -Close progress dialog -Sluit voortgangsvenster - -Standby -Stand-by - -Log off -Afmelden - -Shut down -Afsluiten - -Hibernate -Slaapstand - -Scanning... -Doorzoekt... - -Comparing content... -Inhoud vergelijken... - -Info -Info - -Warning -Waarschuwing - -Select all -Selecteer alles - -Paused -Gepauzeerd - -Initializing... -Initialiseren... - -Completed -Voltooid - -&Continue -&Doorgaan - -Progress -Voortgang - -Log -Log - - -Do you really want to move the following item to the recycle bin? -Do you really want to move the following %x items to the recycle bin? - - -Wilt u dit item echt naar de prullenbak verplaatsen? -Wilt u echt de volgende %x items naar de prullenbak verplaatsen? - - -Move -Verplaats - - -Do you really want to delete the following item? -Do you really want to delete the following %x items? - - -Wilt u het volgende item echt verwijderen? -Wilt u echt de volgende %x items verwijderen? - - -Preferences -Voorkeuren - -Copy NTFS permissions -Kopiëer NTFS permissies - -Integrate external applications into context menu. The following macros are available: -Integreer externe applicaties in het context menu. De volgende macros zijn beschikbaar: - -- full file or folder name -- volledige bestands- of mapnaam - -- folder part only -- alleen het map gedeelte - -- Other side's counterpart to %item_path% -- Tegenhanger van de andere kant naar %item_path% - -- Other side's counterpart to %item_folder% -- Tegenhanger van de andere kant naar %item_folder% - -Identify and propagate changes on both sides. Deletions, moves and conflicts are detected automatically using a database. -Identificeer en pas veranderingen toe aan beide kanten. Verwijderingen, verplaatsingen en conflicten worden automatisch gevonden met behulp van een database. - -Create a mirror backup of the left folder by adapting the right folder to match. -Maak een spiegelbackup van de linkermap door te zorgen dat de rechtermap gelijk is. - -Copy new and updated files to the right folder. -Kopiëer nieuwe en geupdate bestanden naar de rechtermap. - -Configure your own synchronization rules. -Configureer uw eigen synchronisatieregels. - -Exclude -Uitsluiten - -Direct -Direct - -Follow -Volgen - -Today -Vandaag - -This week -Deze week - -This month -Deze maand - -This year -Dit jaar - -Last x days -Laatste x dagen - -Byte -Byte - -KB -KB - -MB -MB - -Replace -Vervangen - -Move files and replace if existing -Verplaats bestanden en overschrijf bestaande bestanden - -Time stamp -Tijdstempel - -Append a time stamp to each file name -Voeg een timestamp toe aan elke bestandsnaam - -Comparison -Vergelijkingen - -Synchronization -Synchronisatie - -Leave as unresolved conflict -Beschouw als onopgelost conflict - -File -Bestand - -YYYY-MM-DD hhmmss -JJJJ-MM-DD hhmmss - -Files -Bestanden - -Items -Items - -Percentage -Percentage - -Cannot monitor directory %x. -Kan locatie %x niet controleren. - -Conversion error: -Converteerfout: - -Cannot delete file %x. -Kan bestand %x niet verwijderen. - -The file is locked by another process: -Het bestand is in gebruik door een ander proces: - -Cannot move file %x to %y. -Kan bestand %x niet verplaatsen naar %y. - -Cannot delete directory %x. -Kan bestand %x niet verwijderen. - -Cannot write file attributes of %x. -Kan bestandskenmerken van %x niet schrijven. - -Cannot write modification time of %x. -Kan wijzigingstijd van %x niet toevoegen. - -Cannot read security context of %x. -Kan de beveiligingscontext van %x niet lezen. - -Cannot write security context of %x. -Kan de beveiligingscontext van %x niet schrijven. - -Cannot read permissions of %x. -Kan de bevoegdheden van %x niet lezen. - -Cannot write permissions of %x. -Kan de bevoegdheden van %x niet schrijven. - -Cannot create directory %x. -Kan map %x niet aanmaken. - -Cannot create symbolic link %x. -Kan snelkoppeling %x niet aanmaken. - -Cannot find system function %x. -Kan systeemfunctie %x niet vinden. - -Cannot copy file %x to %y. -Kan bestand %x niet kopiëren naar %y. - -Type of item %x is not supported: -Type van bestand %x is niet ondersteund: - -Cannot resolve symbolic link %x. -Kan snelkoppeling %x niet vinden. - -Cannot open directory %x. -Kan map %x niet openen. - -Cannot enumerate directory %x. -Kan map %x niet opsommen. - -%x TB -%x TB - -%x PB -%x PB - - -1 min -%x min - - -1 min -%x min - - - -1 hour -%x hours - - -1 uur -%x uren - - - -1 day -%x days - - -1 dag -%x dagen - - -Unable to register to receive system messages. -Registreren voor het verkrijgen van systeemmeldingen is niet mogelijk. - -Cannot set privilege %x. -Kan privilege %x niet instellen. - -Unable to suspend system sleep mode. -Niet in staat om slaapstand uit te stellen. - -Cannot change process I/O priorities. -Kan de I/O prioriteiten niet aanpassen. - -Unable to move %x to the recycle bin. -Kan %x niet verplaatsen naar de prullenbak. - -Checking recycle bin failed for folder %x. -Controleren van prullenbak voor map %x mislukt. - -Cannot determine final path for %x. -Kan pad voor %x niet vaststellen. - -Error Code %x: -Foutcode %x: - -Cannot read the following XML elements: -Kan de volgende XML elementen niet lezen: - -Configuration file %x loaded partially only. -Configuratiebestand %x alleen deels geladen. - diff --git a/FreeFileSync/Source/FreeFileSync.vcxproj b/FreeFileSync/Source/FreeFileSync.vcxproj index f8b130ad..0f469f34 100644 --- a/FreeFileSync/Source/FreeFileSync.vcxproj +++ b/FreeFileSync/Source/FreeFileSync.vcxproj @@ -208,7 +208,7 @@ - + diff --git a/FreeFileSync/Source/Makefile b/FreeFileSync/Source/Makefile index ced7374a..f44202c6 100644 --- a/FreeFileSync/Source/Makefile +++ b/FreeFileSync/Source/Makefile @@ -69,7 +69,7 @@ CPP_LIST+=lib/versioning.cpp CPP_LIST+=lib/ffs_paths.cpp CPP_LIST+=../../zen/xml_io.cpp CPP_LIST+=../../zen/recycler.cpp -CPP_LIST+=../../zen/file_handling.cpp +CPP_LIST+=../../zen/file_access.cpp CPP_LIST+=../../zen/file_io.cpp CPP_LIST+=../../zen/file_traverser.cpp CPP_LIST+=../../zen/zstring.cpp diff --git a/FreeFileSync/Source/RealtimeSync/Makefile b/FreeFileSync/Source/RealtimeSync/Makefile index cd888bcf..4cdcb607 100644 --- a/FreeFileSync/Source/RealtimeSync/Makefile +++ b/FreeFileSync/Source/RealtimeSync/Makefile @@ -27,7 +27,7 @@ CPP_LIST+=../lib/resolve_path.cpp CPP_LIST+=../lib/ffs_paths.cpp CPP_LIST+=../../../zen/xml_io.cpp CPP_LIST+=../../../zen/dir_watcher.cpp -CPP_LIST+=../../../zen/file_handling.cpp +CPP_LIST+=../../../zen/file_access.cpp CPP_LIST+=../../../zen/file_io.cpp CPP_LIST+=../../../zen/file_traverser.cpp CPP_LIST+=../../../zen/zstring.cpp diff --git a/FreeFileSync/Source/RealtimeSync/RealtimeSync.vcxproj b/FreeFileSync/Source/RealtimeSync/RealtimeSync.vcxproj index fefe060a..adcc161d 100644 --- a/FreeFileSync/Source/RealtimeSync/RealtimeSync.vcxproj +++ b/FreeFileSync/Source/RealtimeSync/RealtimeSync.vcxproj @@ -210,7 +210,7 @@ - + diff --git a/FreeFileSync/Source/RealtimeSync/application.cpp b/FreeFileSync/Source/RealtimeSync/application.cpp index 068a7cef..85c89f6b 100644 --- a/FreeFileSync/Source/RealtimeSync/application.cpp +++ b/FreeFileSync/Source/RealtimeSync/application.cpp @@ -6,7 +6,7 @@ #include "application.h" #include "main_dlg.h" -#include +#include #include #include #include @@ -70,7 +70,7 @@ bool Application::OnInit() //SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application." ::SetErrorMode(SEM_FAILCRITICALERRORS); - setAppUserModeId(L"RealtimeSync", L"SourceForge.RealtimeSync"); //noexcept + setAppUserModeId(L"RealtimeSync", L"Zenju.RealtimeSync"); //noexcept //consider: RealtimeSync.exe, RealtimeSync_Win32.exe, RealtimeSync_x64.exe wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only diff --git a/FreeFileSync/Source/RealtimeSync/application.h b/FreeFileSync/Source/RealtimeSync/application.h index 3752f824..90180d23 100644 --- a/FreeFileSync/Source/RealtimeSync/application.h +++ b/FreeFileSync/Source/RealtimeSync/application.h @@ -12,15 +12,15 @@ class Application : public wxApp { public: - virtual bool OnInit(); - virtual int OnExit(); - virtual int OnRun(); - virtual bool OnExceptionInMainLoop() { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() + bool OnInit() override; + int OnExit() override; + int OnRun () override; + bool OnExceptionInMainLoop() override { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() void onQueryEndSession(wxEvent& event); private: void onEnterEventLoop(wxEvent& event); - //virtual wxLayoutDirection GetLayoutDirection() const { return wxLayout_LeftToRight; } + //wxLayoutDirection GetLayoutDirection() const override { return wxLayout_LeftToRight; } }; #endif // REALTIMESYNCAPP_H diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp index 934e9215..121f5486 100644 --- a/FreeFileSync/Source/RealtimeSync/main_dlg.cpp +++ b/FreeFileSync/Source/RealtimeSync/main_dlg.cpp @@ -12,8 +12,7 @@ #include #include #include -#include -#include +#include #include #include "xml_proc.h" #include "tray_menu.h" @@ -93,7 +92,7 @@ MainDialog::MainDialog(wxDialog* dlg, const Zstring& cfgFileName) Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(MainDialog::OnKeyPressed), nullptr, this); //prepare drag & drop - dirpathFirst.reset(new DirectoryName(*m_panelMainFolder, *m_buttonSelectDirMain, *m_txtCtrlDirectoryMain, m_staticTextFinalPath)); + dirpathFirst = zen::make_unique>(*m_panelMainFolder, *m_buttonSelectDirMain, *m_txtCtrlDirectoryMain, m_staticTextFinalPath); //--------------------------- load config values ------------------------------------ xmlAccess::XmlRealConfig newConfig; @@ -202,7 +201,7 @@ void MainDialog::OnMenuAbout(wxCommandEvent& event) #endif build += zen::is64BitBuild ? L" x64" : L" x86"; - assert_static(zen::is32BitBuild || zen::is64BitBuild); + static_assert(zen::is32BitBuild || zen::is64BitBuild, ""); showNotificationDialog(this, DialogInfoType::INFO, PopupDialogCfg(). setTitle(_("About")). diff --git a/FreeFileSync/Source/RealtimeSync/main_dlg.h b/FreeFileSync/Source/RealtimeSync/main_dlg.h index d70aac27..b77ee045 100644 --- a/FreeFileSync/Source/RealtimeSync/main_dlg.h +++ b/FreeFileSync/Source/RealtimeSync/main_dlg.h @@ -36,17 +36,17 @@ private: void loadConfig(const Zstring& filepath); - virtual void OnClose (wxCloseEvent& event) { Destroy(); } - virtual void OnShowHelp (wxCommandEvent& event); - virtual void OnMenuAbout (wxCommandEvent& event); - virtual void OnAddFolder (wxCommandEvent& event); - virtual void OnRemoveFolder (wxCommandEvent& event); - virtual void OnRemoveTopFolder(wxCommandEvent& event); - virtual void OnKeyPressed (wxKeyEvent& event); - virtual void OnStart (wxCommandEvent& event); - virtual void OnConfigSave (wxCommandEvent& event); - virtual void OnConfigLoad (wxCommandEvent& event); - virtual void OnMenuQuit (wxCommandEvent& event) { Close(); } + void OnClose (wxCloseEvent& event ) override { Destroy(); } + void OnShowHelp (wxCommandEvent& event) override; + void OnMenuAbout (wxCommandEvent& event) override; + void OnAddFolder (wxCommandEvent& event) override; + void OnRemoveFolder (wxCommandEvent& event); + void OnRemoveTopFolder(wxCommandEvent& event) override; + void OnKeyPressed (wxKeyEvent& event); + void OnStart (wxCommandEvent& event) override; + void OnConfigSave (wxCommandEvent& event) override; + void OnConfigLoad (wxCommandEvent& event) override; + void OnMenuQuit (wxCommandEvent& event) override { Close(); } void onFilesDropped(zen::FileDropEvent& event); void setConfiguration(const xmlAccess::XmlRealConfig& cfg); diff --git a/FreeFileSync/Source/RealtimeSync/monitor.cpp b/FreeFileSync/Source/RealtimeSync/monitor.cpp index b8cd57e8..4c17467e 100644 --- a/FreeFileSync/Source/RealtimeSync/monitor.cpp +++ b/FreeFileSync/Source/RealtimeSync/monitor.cpp @@ -7,7 +7,7 @@ #include "monitor.h" #include #include -#include +#include #include #include #include @@ -75,7 +75,7 @@ WaitResult waitForChanges(const std::vector& dirpathPhrases, //throw Fi if (!ftDirExists.get()) return WaitResult(dirpathFmt); - watches.push_back(std::make_pair(dirpathFmt, std::make_shared(dirpathFmt))); //throw FileError + watches.emplace_back(dirpathFmt, std::make_shared(dirpathFmt)); //throw FileError } catch (FileError&) { @@ -171,7 +171,7 @@ void waitForMissingDirs(const std::vector& dirpathPhrases, //throw File allExisting = false; //wait some time... const int refreshInterval = rts::UI_UPDATE_INTERVAL / 2; - assert_static(CHECK_DIR_INTERVAL * 1000 % refreshInterval == 0); + static_assert(CHECK_DIR_INTERVAL * 1000 % refreshInterval == 0, ""); for (int i = 0; i < CHECK_DIR_INTERVAL * 1000 / refreshInterval; ++i) { onRefreshGui(dirpathFmt); //may throw! diff --git a/FreeFileSync/Source/RealtimeSync/monitor.h b/FreeFileSync/Source/RealtimeSync/monitor.h index 5fd8fb4b..77fe4740 100644 --- a/FreeFileSync/Source/RealtimeSync/monitor.h +++ b/FreeFileSync/Source/RealtimeSync/monitor.h @@ -25,8 +25,8 @@ struct MonitorCallback MONITOR_PHASE_WAITING, }; virtual void setPhase(WatchPhase mode) = 0; - virtual void executeExternalCommand() = 0; - virtual void requestUiRefresh() = 0; + virtual void executeExternalCommand () = 0; + virtual void requestUiRefresh () = 0; virtual void reportError(const std::wstring& msg) = 0; //automatically retries after return! }; void monitorDirectories(const std::vector& dirpathPhrases, diff --git a/FreeFileSync/Source/RealtimeSync/tray_menu.cpp b/FreeFileSync/Source/RealtimeSync/tray_menu.cpp index 7cf1919f..2a59902f 100644 --- a/FreeFileSync/Source/RealtimeSync/tray_menu.cpp +++ b/FreeFileSync/Source/RealtimeSync/tray_menu.cpp @@ -122,7 +122,7 @@ private: CONTEXT_ABORT = wxID_EXIT }; - virtual wxMenu* CreatePopupMenu() + wxMenu* CreatePopupMenu() override { wxMenu* contextMenu = new wxMenu; @@ -268,7 +268,7 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf MonitorCallbackImpl(const wxString& jobname, const Zstring& cmdLine) : trayIcon(jobname), cmdLine_(cmdLine) {} - virtual void setPhase(WatchPhase mode) + void setPhase(WatchPhase mode) override { switch (mode) { @@ -281,12 +281,12 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf } } - virtual void executeExternalCommand() + void executeExternalCommand() override { auto cmdLineExp = expandMacros(cmdLine_); try { - shellExecute2(cmdLineExp, EXEC_TYPE_SYNC); //throw FileError + shellExecute(cmdLineExp, EXEC_TYPE_SYNC); //throw FileError } catch (const FileError& e) { @@ -294,19 +294,19 @@ rts::AbortReason rts::startDirectoryMonitor(const xmlAccess::XmlRealConfig& conf } } - virtual void requestUiRefresh() + void requestUiRefresh() override { if (updateUiIsAllowed()) trayIcon.doUiRefreshNow(); //throw AbortMonitoring } - virtual void reportError(const std::wstring& msg) + void reportError(const std::wstring& msg) override { trayIcon.setMode(TRAY_MODE_ERROR); trayIcon.clearShowErrorRequested(); //wait for some time, then return to retry - assert_static(15 * 1000 % UI_UPDATE_INTERVAL == 0); + static_assert(15 * 1000 % UI_UPDATE_INTERVAL == 0, ""); for (int i = 0; i < 15 * 1000 / UI_UPDATE_INTERVAL; ++i) { trayIcon.doUiRefreshNow(); //throw AbortMonitoring diff --git a/FreeFileSync/Source/RealtimeSync/xml_proc.cpp b/FreeFileSync/Source/RealtimeSync/xml_proc.cpp index 3457f8f2..04edbf86 100644 --- a/FreeFileSync/Source/RealtimeSync/xml_proc.cpp +++ b/FreeFileSync/Source/RealtimeSync/xml_proc.cpp @@ -5,7 +5,7 @@ // ************************************************************************** #include "xml_proc.h" -#include +#include #include #include #include "../lib/process_xml.h" diff --git a/FreeFileSync/Source/algorithm.cpp b/FreeFileSync/Source/algorithm.cpp index b5791f90..37f627ee 100644 --- a/FreeFileSync/Source/algorithm.cpp +++ b/FreeFileSync/Source/algorithm.cpp @@ -6,8 +6,9 @@ #include "algorithm.h" #include +#include #include -#include +#include #include #include #include @@ -336,7 +337,7 @@ private: { if (fileObj.getFileId() != FileId()) { - auto rv = exLeftOnly.insert(std::make_pair(fileObj.getFileId(), &fileObj)); + auto rv = exLeftOnly.emplace(fileObj.getFileId(), &fileObj); assert(rv.second); if (!rv.second) //duplicate file ID! rv.first->second = nullptr; @@ -346,7 +347,7 @@ private: { if (fileObj.getFileId() != FileId()) { - auto rv = exRightOnly.insert(std::make_pair(fileObj.getFileId(), &fileObj)); + auto rv = exRightOnly.emplace(fileObj.getFileId(), &fileObj); assert(rv.second); if (!rv.second) //duplicate file ID! rv.first->second = nullptr; @@ -728,15 +729,15 @@ void zen::setSyncDirectionRec(SyncDirection newDirection, FileSystemObject& fsOb struct Recurse: public FSObjectVisitor { Recurse(SyncDirection newDir) : newDir_(newDir) {} - virtual void visit(const FilePair& fileObj) + void visit(const FilePair& fileObj) override { SetNewDirection::execute(const_cast(fileObj), newDir_); //phyiscal object is not const in this method anyway } - virtual void visit(const SymlinkPair& linkObj) + void visit(const SymlinkPair& linkObj) override { SetNewDirection::execute(const_cast(linkObj), newDir_); // } - virtual void visit(const DirPair& dirObj) + void visit(const DirPair& dirObj) override { SetNewDirection::execute(const_cast(dirObj), newDir_); // } @@ -783,9 +784,9 @@ void zen::setActiveStatus(bool newStatus, zen::FileSystemObject& fsObj) struct Recurse: public FSObjectVisitor { Recurse(bool newStat) : newStatus_(newStat) {} - virtual void visit(const FilePair& fileObj) {} - virtual void visit(const SymlinkPair& linkObj) {} - virtual void visit(const DirPair& dirObj) + void visit(const FilePair& fileObj) override {} + void visit(const SymlinkPair& linkObj) override {} + void visit(const DirPair& dirObj) override { if (newStatus_) inOrExcludeAllRows(const_cast(dirObj)); //object is not physically const here anyway @@ -1164,7 +1165,7 @@ void categorize(const std::set& rowsIn, recExists = recycleBinExists(baseDirPf, [&] { callback.reportStatus(msg); /*may throw*/ }); //throw FileError }, callback); - hasRecyclerBuffer.insert(std::make_pair(baseDirPf, recExists)); + hasRecyclerBuffer.emplace(baseDirPf, recExists); return recExists; #elif defined ZEN_LINUX || defined ZEN_MAC @@ -1203,7 +1204,7 @@ struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow cons } } - virtual void visit(const FilePair& fileObj) + void visit(const FilePair& fileObj) override { notifyFileDeletion(fileObj.getFullPath()); @@ -1213,7 +1214,7 @@ struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow cons zen::removeFile(fileObj.getFullPath()); //throw FileError } - virtual void visit(const SymlinkPair& linkObj) + void visit(const SymlinkPair& linkObj) override { notifySymlinkDeletion(linkObj.getFullPath()); @@ -1228,7 +1229,7 @@ struct ItemDeleter : public FSObjectVisitor //throw FileError, but nothrow cons } } - virtual void visit(const DirPair& dirObj) + void visit(const DirPair& dirObj) override { notifyDirectoryDeletion(dirObj.getFullPath()); //notfied twice; see below -> no big deal @@ -1293,7 +1294,7 @@ void zen::deleteFromGridAndHD(const std::vector& rowsToDelete throw std::logic_error("Programming Error: Contract violation! " + std::string(__FILE__) + ":" + numberTo(__LINE__)); //build up mapping from base directory to corresponding direction config - hash_map baseDirCfgs; + std::unordered_map baseDirCfgs; for (auto it = folderCmp.begin(); it != folderCmp.end(); ++it) baseDirCfgs[&** it] = directCfgs[it - folderCmp.begin()]; diff --git a/FreeFileSync/Source/application.cpp b/FreeFileSync/Source/application.cpp index 9056d962..3ca0c53c 100644 --- a/FreeFileSync/Source/application.cpp +++ b/FreeFileSync/Source/application.cpp @@ -6,7 +6,7 @@ #include "application.h" #include -#include +#include #include #include #include @@ -102,7 +102,7 @@ std::vector getCommandlineArgs(const wxApp& app) { if (iterStart != cmdLine.end()) { - args.push_back(Zstring(iterStart, it)); + args.emplace_back(iterStart, it); iterStart = cmdLine.end(); //expect consecutive blanks! } } @@ -120,7 +120,7 @@ std::vector getCommandlineArgs(const wxApp& app) } } if (iterStart != cmdLine.end()) - args.push_back(Zstring(iterStart, cmdLine.end())); + args.emplace_back(iterStart, cmdLine.end()); if (!args.empty()) args.erase(args.begin()); //remove first argument which is exe path by convention: http://blogs.msdn.com/b/oldnewthing/archive/2006/05/15/597984.aspx @@ -155,7 +155,7 @@ bool Application::OnInit() //SEM_FAILCRITICALERRORS at startup. This is to prevent error mode dialogs from hanging the application." ::SetErrorMode(SEM_FAILCRITICALERRORS); - setAppUserModeId(L"FreeFileSync", L"SourceForge.FreeFileSync"); //noexcept + setAppUserModeId(L"FreeFileSync", L"Zenju.FreeFileSync"); //noexcept //consider: FreeFileSync.exe, FreeFileSync_Win32.exe, FreeFileSync_x64.exe wxToolTip::SetMaxWidth(-1); //disable tooltip wrapping -> Windows only @@ -270,7 +270,7 @@ void Application::onQueryEndSession(wxEvent& event) void runGuiMode (const Zstring& globalConfigFile); -void runGuiMode (const Zstring& globalConfigFile, const XmlGuiConfig& guiCfg, const std::vector& referenceFiles); +void runGuiMode (const Zstring& globalConfigFile, const XmlGuiConfig& guiCfg, const std::vector& referenceFiles, bool startComparison); void runBatchMode(const Zstring& globalConfigFile, const XmlBatchConfig& batchCfg, const Zstring& referenceFile, FfsReturnCode& returnCode); void showSyntaxHelp(); @@ -294,28 +294,36 @@ void Application::launch(const std::vector& commandArgs) raiseReturnCode(returnCode, FFS_RC_ABORTED); }; + auto equalNoCase = [](const Zstring& lhs, const Zstring& rhs) { return utfCvrtTo(lhs).CmpNoCase(utfCvrtTo(rhs)) == 0; }; + //parse command line arguments std::vector leftDirs; std::vector rightDirs; std::vector> configFiles; //XmlType: batch or GUI files only Opt globalConfigFile; + bool openForEdit = false; { + const Zchar optionEdit [] = Zstr("-edit"); const Zchar optionLeftDir [] = Zstr("-leftdir"); const Zchar optionRightDir[] = Zstr("-rightdir"); - auto syntaxHelpRequested = [](const Zstring& arg) + auto syntaxHelpRequested = [&](const Zstring& arg) { auto it = std::find_if(arg.begin(), arg.end(), [](Zchar c) { return c != Zchar('/') && c != Zchar('-'); }); + if (it == arg.begin()) return false; //require at least one prefix character + const Zstring argTmp(it, arg.end()); - return argTmp == Zstr("help") || - argTmp == Zstr("h") || + return equalNoCase(argTmp, Zstr("help")) || + equalNoCase(argTmp, Zstr("h")) || argTmp == Zstr("?"); }; for (auto it = commandArgs.begin(); it != commandArgs.end(); ++it) if (syntaxHelpRequested(*it)) return showSyntaxHelp(); - else if (*it == optionLeftDir) + else if (equalNoCase(*it, optionEdit)) + openForEdit = true; + else if (equalNoCase(*it, optionLeftDir)) { if (++it == commandArgs.end()) { @@ -324,7 +332,7 @@ void Application::launch(const std::vector& commandArgs) } leftDirs.push_back(*it); } - else if (*it == optionRightDir) + else if (equalNoCase(*it, optionRightDir)) { if (++it == commandArgs.end()) { @@ -356,10 +364,10 @@ void Application::launch(const std::vector& commandArgs) switch (getXmlType(filepath)) //throw FileError { case XML_TYPE_GUI: - configFiles.push_back(std::make_pair(filepath, XML_TYPE_GUI)); + configFiles.emplace_back(filepath, XML_TYPE_GUI); break; case XML_TYPE_BATCH: - configFiles.push_back(std::make_pair(filepath, XML_TYPE_BATCH)); + configFiles.emplace_back(filepath, XML_TYPE_BATCH); break; case XML_TYPE_GLOBAL: globalConfigFile = filepath; @@ -409,9 +417,8 @@ void Application::launch(const std::vector& commandArgs) mainCfg.firstPair.dirpathPhraseRight = rightDirs[0]; } else - mainCfg.additionalPairs.push_back(FolderPairEnh(leftDirs [i], - rightDirs[i], - nullptr, nullptr, FilterConfig())); + mainCfg.additionalPairs.emplace_back(leftDirs[i], rightDirs[i], + nullptr, nullptr, FilterConfig()); } return true; }; @@ -433,7 +440,7 @@ void Application::launch(const std::vector& commandArgs) if (!replaceDirectories(guiCfg.mainCfg)) return; - runGuiMode(globalConfigFilePath, guiCfg, std::vector()); + runGuiMode(globalConfigFilePath, guiCfg, std::vector(), !openForEdit); } } else if (configFiles.size() == 1) @@ -441,7 +448,7 @@ void Application::launch(const std::vector& commandArgs) const Zstring filepath = configFiles[0].first; //batch mode - if (configFiles[0].second == XML_TYPE_BATCH) + if (configFiles[0].second == XML_TYPE_BATCH && !openForEdit) { XmlBatchConfig batchCfg; try @@ -461,14 +468,14 @@ void Application::launch(const std::vector& commandArgs) return; runBatchMode(globalConfigFilePath, batchCfg, filepath, returnCode); } - //GUI mode: single config + //GUI mode: single config (ffs_gui *or* ffs_batch) else { XmlGuiConfig guiCfg; try { std::wstring warningMsg; - readConfig(filepath, guiCfg, warningMsg); //throw FileError + readAnyConfig({ filepath }, guiCfg, warningMsg); //throw FileError if (!warningMsg.empty()) showNotificationDialog(nullptr, DialogInfoType::WARNING, PopupDialogCfg().setDetailInstructions(warningMsg)); @@ -484,7 +491,7 @@ void Application::launch(const std::vector& commandArgs) //what about simulating changed config due to directory replacement? //-> propably fine to not show as changed on GUI and not ask user to save on exit! - runGuiMode(globalConfigFilePath, guiCfg, { filepath }); //caveat: guiCfg and filepath do not match if directories were set/replaced via command line! + runGuiMode(globalConfigFilePath, guiCfg, { filepath }, !openForEdit); //caveat: guiCfg and filepath do not match if directories were set/replaced via command line! } } //gui mode: merged configs @@ -515,7 +522,7 @@ void Application::launch(const std::vector& commandArgs) notifyError(e.toString(), std::wstring()); return; } - runGuiMode(globalConfigFilePath, guiCfg, filepaths); + runGuiMode(globalConfigFilePath, guiCfg, filepaths, !openForEdit); } } @@ -525,9 +532,10 @@ void runGuiMode(const Zstring& globalConfigFile) { MainDialog::create(globalConf void runGuiMode(const Zstring& globalConfigFile, const xmlAccess::XmlGuiConfig& guiCfg, - const std::vector& referenceFiles) + const std::vector& referenceFiles, + bool startComparison) { - MainDialog::create(globalConfigFile, nullptr, guiCfg, referenceFiles, /*bool startComparison = */true); + MainDialog::create(globalConfigFile, nullptr, guiCfg, referenceFiles, startComparison); } @@ -538,18 +546,22 @@ void showSyntaxHelp() setDetailInstructions(_("Syntax:") + L"\n\n" + L"FreeFileSync.exe " + L"\n" + - L" [" + _("global config file:") + L" GlobalSettings.xml]\n" + - L" [" + _("config files:") + L" *.ffs_gui/*.ffs_batch]\n" + - L" [-leftdir " + _("directory") + L"] [-rightdir " + _("directory") + L"]" + L"\n\n" + + L" [" + _("global config file:") + L" GlobalSettings.xml]" + L"\n" + + L" [" + _("config files:") + L" *.ffs_gui/*.ffs_batch]" + L"\n" + + L" [-LeftDir " + _("directory") + L"] [-RightDir " + _("directory") + L"]" + L"\n" + + L" [-Edit]" + L"\n\n" + _("global config file:") + L"\n" + _("Path to an alternate GlobalSettings.xml file.") + L"\n\n" + _("config files:") + L"\n" + - _("Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.") + L"\n\n" + _("Any number of FreeFileSync .ffs_gui and/or .ffs_batch configuration files.") + L"\n\n" + + + L"-LeftDir " + _("directory") + L" -RightDir " + _("directory") + L"\n" + + _("Any number of alternative directory pairs for at most one config file.") + L"\n\n" + - L"-leftdir " + _("directory") + L" -rightdir " + _("directory") + L"\n" + - _("Any number of alternative directory pairs for at most one config file."))); + L"-Edit" + L"\n" + + _("Open configuration for edit without executing."))); } diff --git a/FreeFileSync/Source/application.h b/FreeFileSync/Source/application.h index defc1e17..ce0284e8 100644 --- a/FreeFileSync/Source/application.h +++ b/FreeFileSync/Source/application.h @@ -19,10 +19,10 @@ public: Application() : returnCode(zen::FFS_RC_SUCCESS) {} private: - virtual bool OnInit(); - virtual int OnExit(); - virtual int OnRun(); - virtual bool OnExceptionInMainLoop() { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() + bool OnInit() override; + int OnExit() override; + int OnRun() override; + bool OnExceptionInMainLoop() override { throw; } //just re-throw and avoid display of additional messagebox: it will be caught in OnRun() void onEnterEventLoop(wxEvent& event); void onQueryEndSession(wxEvent& event); diff --git a/FreeFileSync/Source/comparison.cpp b/FreeFileSync/Source/comparison.cpp index e4495802..08d5ee71 100644 --- a/FreeFileSync/Source/comparison.cpp +++ b/FreeFileSync/Source/comparison.cpp @@ -69,9 +69,8 @@ std::vector resolveDirectoryNames(const std::vector output; for (const FolderPairCfg& fpCfg : cfgList) - output.push_back(ResolvedFolderPair( - getFormattedDirectoryPath(fpCfg.dirpathPhraseLeft), - getFormattedDirectoryPath(fpCfg.dirpathPhraseRight))); + output.emplace_back(getFormattedDirectoryPath(fpCfg.dirpathPhraseLeft), + getFormattedDirectoryPath(fpCfg.dirpathPhraseRight)); warn_static("get volume by name for idle HDD! => call async getFormattedDirectoryPath, but currently not thread-safe") return output; } @@ -142,7 +141,7 @@ void checkFolderDependency(const std::vector& folderPairs, b { std::vector> dependentDirs; - auto areDependent = [](const Zstring& lhs, const Zstring& rhs) + auto havePathDependency = [](const Zstring& lhs, const Zstring& rhs) { return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), //note: this is NOT an equivalence relation! Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); @@ -151,8 +150,8 @@ void checkFolderDependency(const std::vector& folderPairs, b for (const ResolvedFolderPair& fp : folderPairs) if (!fp.dirpathLeft.empty() && !fp.dirpathRight.empty()) //empty folders names may be accepted by user { - if (areDependent(fp.dirpathLeft, fp.dirpathRight)) //test wheter leftDirectory begins with rightDirectory or the other way round - dependentDirs.push_back(std::make_pair(fp.dirpathLeft, fp.dirpathRight)); + if (havePathDependency(fp.dirpathLeft, fp.dirpathRight)) //test wheter leftDirectory begins with rightDirectory or the other way round + dependentDirs.emplace_back(fp.dirpathLeft, fp.dirpathRight); } if (!dependentDirs.empty()) @@ -201,7 +200,7 @@ ComparisonBuffer::ComparisonBuffer(const std::set& keysToRead, Pro callback_(pcb), itemsReported(0) {} - virtual void reportStatus(const std::wstring& statusMsg, int itemsTotal) + void reportStatus(const std::wstring& statusMsg, int itemsTotal) override { callback_.updateProcessedData(itemsTotal - itemsReported, 0); //processed bytes are reported in subfunctions! itemsReported = itemsTotal; @@ -210,7 +209,7 @@ ComparisonBuffer::ComparisonBuffer(const std::set& keysToRead, Pro //callback_.requestUiRefresh(); //already called by reportStatus() } - virtual HandleError reportError(const std::wstring& msg, size_t retryNumber) + HandleError reportError(const std::wstring& msg, size_t retryNumber) override { switch (callback_.reportError(msg, retryNumber)) { @@ -808,11 +807,11 @@ void zen::compare(xmlAccess::OptionalDialogs& warnings, std::vector> totalWorkLoad; for (size_t i = 0; i < cfgList.size(); ++i) - totalWorkLoad.push_back(std::make_pair(resInfo.resolvedPairs[i], cfgList[i])); + totalWorkLoad.emplace_back(resInfo.resolvedPairs[i], cfgList[i]); //lock (existing) directories before comparison if (createDirLocks) - dirLocks = make_unique(resInfo.existingDirs, warnings.warningDirectoryLockFailed, callback); + dirLocks = zen::make_unique(resInfo.existingDirs, warnings.warningDirectoryLockFailed, callback); try { @@ -822,9 +821,9 @@ void zen::compare(xmlAccess::OptionalDialogs& warnings, for (const auto& w : totalWorkLoad) { if (dirAvailable(w.first.dirpathLeft)) //only traverse *currently existing* directories: at this point user is aware that non-ex + empty string are seen as empty folder! - dirsToRead.insert(DirectoryKey(w.first.dirpathLeft, w.second.filter.nameFilter, w.second.handleSymlinks)); + dirsToRead.emplace(w.first.dirpathLeft, w.second.filter.nameFilter, w.second.handleSymlinks); if (dirAvailable(w.first.dirpathRight)) - dirsToRead.insert(DirectoryKey(w.first.dirpathRight, w.second.filter.nameFilter, w.second.handleSymlinks)); + dirsToRead.emplace(w.first.dirpathRight, w.second.filter.nameFilter, w.second.handleSymlinks); } FolderComparison outputTmp; //write to output as a transaction! diff --git a/FreeFileSync/Source/file_hierarchy.h b/FreeFileSync/Source/file_hierarchy.h index d4e92cde..1468750e 100644 --- a/FreeFileSync/Source/file_hierarchy.h +++ b/FreeFileSync/Source/file_hierarchy.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -77,14 +78,14 @@ template <> struct SelectParam { template - static T& get(T& left, T& right) { return left; } + static T& ref(T& left, T& right) { return left; } }; template <> struct SelectParam { template - static T& get(T& left, T& right) { return right; } + static T& ref(T& left, T& right) { return right; } }; @@ -96,18 +97,11 @@ class FileSystemObject; //------------------------------------------------------------------ -/* -ERD: - DirContainer 1 --> 0..n DirContainer - DirContainer 1 --> 0..n FileDescriptor - DirContainer 1 --> 0..n LinkDescriptor -*/ - struct DirContainer { //------------------------------------------------------------------ typedef std::map DirList; // - typedef std::map FileList; //key: shortName + typedef std::map FileList; //key: file name typedef std::map LinkList; // //------------------------------------------------------------------ @@ -118,19 +112,18 @@ struct DirContainer //convenience DirContainer& addSubDir(const Zstring& shortName) { - //use C++11 emplace when available return dirs[shortName]; //value default-construction is okay here - //return dirs.insert(std::make_pair(shortName, DirContainer())).first->second; + //return dirs.emplace(shortName, DirContainer()).first->second; } void addSubFile(const Zstring& shortName, const FileDescriptor& fileData) { - files.insert(std::make_pair(shortName, fileData)); + files.emplace(shortName, fileData); } void addSubLink(const Zstring& shortName, const LinkDescriptor& linkData) { - links.insert(std::make_pair(shortName, linkData)); + links.emplace(shortName, linkData); } }; @@ -142,8 +135,8 @@ struct DirContainer | FileSystemObject HierarchyObject /|\ /|\ - _______________|______________ ______|______ - | | | | | + _______________|_______________ ______|______ + | | | | | SymlinkPair FilePair DirPair BaseDirPair ------------------------------------------------------------------*/ @@ -212,7 +205,7 @@ protected: void removeEmptyRec(); private: - virtual void notifySyncCfgChanged() {} + virtual void notifySyncCfgChanged() {}; HierarchyObject (const HierarchyObject&) = delete; //this class is referenced by it's child elements => make it non-copyable/movable! HierarchyObject& operator=(const HierarchyObject&) = delete; @@ -264,12 +257,9 @@ public: int getFileTimeTolerance() const { return fileTimeTolerance_; } unsigned int getTimeShift() const { return optTimeShiftHours_; } - virtual void flip(); + void flip() override; private: - BaseDirPair (const BaseDirPair&) = delete; //this class is referenced by HierarchyObject => make it non-copyable/movable! - BaseDirPair& operator=(const BaseDirPair&) = delete; - const HardFilter::FilterRef filter_; //filter used while scanning directory: represents sub-view of actual files! const CompareVariant cmpVar_; const int fileTimeTolerance_; @@ -321,9 +311,8 @@ DerefIter inline b DerefIter inline end (const FolderComparison& vect) { return vect.end (); } //------------------------------------------------------------------ -class FSObjectVisitor +struct FSObjectVisitor { -public: virtual ~FSObjectVisitor() {} virtual void visit(const FilePair& fileObj) = 0; virtual void visit(const SymlinkPair& linkObj) = 0; @@ -357,7 +346,7 @@ private: ObjectMgr (const ObjectMgr& rhs) = delete; ObjectMgr& operator=(const ObjectMgr& rhs) = delete; //it's not well-defined what copying an objects means regarding object-identity in this context - static hash_set& activeObjects() { static hash_set inst; return inst; } //external linkage (even in header file!) + static std::unordered_set& activeObjects() { static std::unordered_set inst; return inst; } //external linkage (even in header file!) }; //------------------------------------------------------------------ @@ -431,6 +420,9 @@ protected: void setSynced(const Zstring& shortName); private: + FileSystemObject (const FileSystemObject&) = delete; + FileSystemObject& operator=(const FileSystemObject&) = delete; + virtual void removeObjectL() = 0; virtual void removeObjectR() = 0; @@ -459,7 +451,7 @@ class DirPair : public FileSystemObject, public HierarchyObject friend class HierarchyObject; public: - virtual void accept(FSObjectVisitor& visitor) const; + void accept(FSObjectVisitor& visitor) const override; CompareDirResult getDirCategory() const; //returns actually used subset of CompareFilesResult @@ -472,15 +464,15 @@ public: syncOpBuffered(SO_DO_NOTHING), syncOpUpToDate(false) {} - virtual SyncOperation getSyncOperation() const; + SyncOperation getSyncOperation() const override; void setSyncedTo(const Zstring& shortName); //call after sync, sets DIR_EQUAL private: - virtual void flip(); - virtual void removeObjectL(); - virtual void removeObjectR(); - virtual void notifySyncCfgChanged() { syncOpUpToDate = false; FileSystemObject::notifySyncCfgChanged(); HierarchyObject::notifySyncCfgChanged(); } + void flip () override; + void removeObjectL() override; + void removeObjectR() override; + void notifySyncCfgChanged() override { syncOpUpToDate = false; FileSystemObject::notifySyncCfgChanged(); HierarchyObject::notifySyncCfgChanged(); } mutable SyncOperation syncOpBuffered; //determining sync-op for directory may be expensive as it depends on child-objects -> buffer it mutable bool syncOpUpToDate; // @@ -493,7 +485,7 @@ class FilePair : public FileSystemObject friend class HierarchyObject; //construction public: - virtual void accept(FSObjectVisitor& visitor) const; + void accept(FSObjectVisitor& visitor) const override; FilePair(const Zstring& shortNameLeft, //use empty string if "not existing" const FileDescriptor& left, @@ -506,8 +498,8 @@ public: dataRight(right), moveFileRef(nullptr) {} - template std::int64_t getLastWriteTime () const; - template std::uint64_t getFileSize() const; + template std::int64_t getLastWriteTime() const; + template std::uint64_t getFileSize() const; template FileId getFileId () const; template bool isFollowedSymlink() const; @@ -516,8 +508,8 @@ public: CompareFilesResult getFileCategory() const; - virtual SyncOperation testSyncOperation(SyncDirection testSyncDir) const; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! - virtual SyncOperation getSyncOperation() const; + SyncOperation testSyncOperation(SyncDirection testSyncDir) const override; //semantics: "what if"! assumes "active, no conflict, no recursion (directory)! + SyncOperation getSyncOperation() const override; template void setSyncedTo(const Zstring& shortName, //call after sync, sets FILE_EQUAL @@ -532,9 +524,9 @@ public: private: SyncOperation applyMoveOptimization(SyncOperation op) const; - virtual void flip(); - virtual void removeObjectL(); - virtual void removeObjectR(); + void flip () override; + void removeObjectL() override; + void removeObjectR() override; FileDescriptor dataLeft; FileDescriptor dataRight; @@ -571,9 +563,9 @@ public: std::int64_t lastWriteTimeSrc); private: - virtual void flip(); - virtual void removeObjectL(); - virtual void removeObjectR(); + void flip() override; + void removeObjectL() override; + void removeObjectR() override; LinkDescriptor dataLeft; LinkDescriptor dataRight; @@ -663,7 +655,7 @@ inline void FileSystemObject::setSyncDirConflict(const std::wstring& description) { syncDir_ = SyncDirection::NONE; - syncDirConflict.reset(new std::wstring(description)); + syncDirConflict = zen::make_unique(description); notifySyncCfgChanged(); } @@ -695,7 +687,7 @@ void FileSystemObject::setActive(bool active) template inline bool FileSystemObject::isEmpty() const { - return SelectParam::get(shortNameLeft_, shortNameRight_).empty(); + return SelectParam::ref(shortNameLeft_, shortNameRight_).empty(); } @@ -709,7 +701,7 @@ bool FileSystemObject::isEmpty() const template inline const Zstring& FileSystemObject::getItemName() const { - return SelectParam::get(shortNameLeft_, shortNameRight_); //empty if not existing + return SelectParam::ref(shortNameLeft_, shortNameRight_); //empty if not existing } @@ -794,14 +786,14 @@ inline void FileSystemObject::setCategoryConflict(const std::wstring& description) { cmpResult = FILE_CONFLICT; - cmpResultDescr.reset(new std::wstring(description)); + cmpResultDescr = zen::make_unique(description); } inline void FileSystemObject::setCategoryDiffMetadata(const std::wstring& description) { cmpResult = FILE_DIFFERENT_METADATA; - cmpResultDescr.reset(new std::wstring(description)); + cmpResultDescr = zen::make_unique(description); } inline @@ -973,14 +965,14 @@ void DirPair::removeObjectR() template inline bool BaseDirPair::isExisting() const { - return SelectParam::get(dirExistsLeft_, dirExistsRight_); + return SelectParam::ref(dirExistsLeft_, dirExistsRight_); } template inline void BaseDirPair::setExisting(bool value) { - SelectParam::get(dirExistsLeft_, dirExistsRight_) = value; + SelectParam::ref(dirExistsLeft_, dirExistsRight_) = value; } @@ -1009,29 +1001,29 @@ void FilePair::removeObjectR() template inline std::int64_t FilePair::getLastWriteTime() const { - return SelectParam::get(dataLeft, dataRight).lastWriteTimeRaw; + return SelectParam::ref(dataLeft, dataRight).lastWriteTimeRaw; } template inline std::uint64_t FilePair::getFileSize() const { - return SelectParam::get(dataLeft, dataRight).fileSize; + return SelectParam::ref(dataLeft, dataRight).fileSize; } template inline FileId FilePair::getFileId() const { - return FileId(SelectParam::get(dataLeft, dataRight).devId, - SelectParam::get(dataLeft, dataRight).fileIdx); + return FileId(SelectParam::ref(dataLeft, dataRight).devId, + SelectParam::ref(dataLeft, dataRight).fileIdx); } template inline bool FilePair::isFollowedSymlink() const { - return SelectParam::get(dataLeft, dataRight).isFollowedSymlink; + return SelectParam::ref(dataLeft, dataRight).isFollowedSymlink; } @@ -1048,8 +1040,8 @@ void FilePair::setSyncedTo(const Zstring& shortName, //FILE_EQUAL is only allowed for same short name and file size: enforced by this method! static const SelectedSide sideSrc = OtherSide::result; - SelectParam::get(dataLeft, dataRight) = FileDescriptor(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg); - SelectParam::get(dataLeft, dataRight) = FileDescriptor(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc); + SelectParam::ref(dataLeft, dataRight) = FileDescriptor(lastWriteTimeTrg, fileSize, fileIdTrg, isSymlinkTrg); + SelectParam::ref(dataLeft, dataRight) = FileDescriptor(lastWriteTimeSrc, fileSize, fileIdSrc, isSymlinkSrc); moveFileRef = nullptr; FileSystemObject::setSynced(shortName); //set FileSystemObject specific part @@ -1063,8 +1055,8 @@ void SymlinkPair::setSyncedTo(const Zstring& shortName, { static const SelectedSide sideSrc = OtherSide::result; - SelectParam::get(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeTrg); - SelectParam::get(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeSrc); + SelectParam::ref(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeTrg); + SelectParam::ref(dataLeft, dataRight) = LinkDescriptor(lastWriteTimeSrc); FileSystemObject::setSynced(shortName); //set FileSystemObject specific part } @@ -1080,7 +1072,7 @@ void DirPair::setSyncedTo(const Zstring& shortName) template inline std::int64_t SymlinkPair::getLastWriteTime() const { - return SelectParam::get(dataLeft, dataRight).lastWriteTimeRaw; + return SelectParam::ref(dataLeft, dataRight).lastWriteTimeRaw; } diff --git a/FreeFileSync/Source/lib/db_file.cpp b/FreeFileSync/Source/lib/db_file.cpp index b1f0098e..c1bc5478 100644 --- a/FreeFileSync/Source/lib/db_file.cpp +++ b/FreeFileSync/Source/lib/db_file.cpp @@ -5,7 +5,8 @@ // ************************************************************************** #include "db_file.h" -#include +#include +#include #include #include #include @@ -54,7 +55,7 @@ Zstring getDatabaseFilePath(const BaseDirPair& baseDirObj, bool tempfile = false //####################################################################################################################################### -void saveStreams(const DbStreams& streamList, const Zstring& filepath) //throw FileError +void saveStreams(const DbStreams& streamList, const Zstring& filepath, const std::function& onUpdateSaveStatus) //throw FileError { BinStreamOut streamOut; @@ -73,8 +74,8 @@ void saveStreams(const DbStreams& streamList, const Zstring& filepath) //throw F writeContainer(streamOut, stream.second); } - assert(!somethingExists(filepath)); //orphan tmp files should be cleaned up already at this point! - saveBinStream(filepath, streamOut.get()); //throw FileError + assert(!somethingExists(filepath)); //orphan tmp files should have been cleaned up at this point! + saveBinStream(filepath, streamOut.get(), onUpdateSaveStatus); //throw FileError #ifdef ZEN_WIN //be careful to avoid CreateFile() + CREATE_ALWAYS on a hidden file -> see file_io.cpp @@ -87,7 +88,9 @@ DbStreams loadStreams(const Zstring& filepath) //throw FileError, FileErrorDatab { try { - BinStreamIn streamIn = loadBinStream(filepath); //throw FileError + warn_static("TODO: implement loadBinStream callback") + + BinStreamIn streamIn = loadBinStream(filepath, nullptr); //throw FileError //read FreeFileSync file identifier char formatDescr[sizeof(FILE_FORMAT_DESCR)] = {}; @@ -245,8 +248,8 @@ private: writeNumber(output, descr.lastWriteTimeRaw); writeNumber(output, descr.fileId.first); writeNumber(output, descr.fileId.second); - assert_static(sizeof(descr.fileId.first ) <= sizeof(std::uint64_t)); - assert_static(sizeof(descr.fileId.second) <= sizeof(std::uint64_t)); + static_assert(sizeof(descr.fileId.first ) <= sizeof(std::uint64_t), ""); + static_assert(sizeof(descr.fileId.second) <= sizeof(std::uint64_t), ""); } static void writeLink(BinStreamOut& output, const InSyncDescrLink& descr) @@ -427,14 +430,14 @@ private: template static V& updateItem(M& map, const Zstring& key, const V& value) { - auto rv = map.insert(typename M::value_type(key, value)); + auto rv = map.emplace(key, value); if (!rv.second) { #if defined ZEN_WIN || defined ZEN_MAC //caveat: key must be updated, if there is a change in short name case!!! if (rv.first->first != key) { map.erase(rv.first); - return map.insert(typename M::value_type(key, value)).first->second; + return map.emplace(key, value).first->second; } #endif rv.first->second = value; @@ -455,7 +458,7 @@ private: if (it->first != key) { map.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly - return map.insert(typename M::value_type(key, value)).first->second; + return map.emplace(key, value).first->second; } #endif it->second = value; @@ -467,7 +470,7 @@ private: void process(const HierarchyObject::SubFileVec& currentFiles, const Zstring& parentRelativeNamePf, InSyncDir::FileList& dbFiles) { - hash_set toPreserve; //referencing fixed-in-memory std::map elements + std::unordered_set toPreserve; //referencing fixed-in-memory std::map elements for (const FilePair& fileObj : currentFiles) if (!fileObj.isEmpty()) { @@ -511,7 +514,7 @@ private: void process(const HierarchyObject::SubLinkVec& currentLinks, const Zstring& parentRelativeNamePf, InSyncDir::LinkList& dbLinks) { - hash_set toPreserve; + std::unordered_set toPreserve; for (const SymlinkPair& linkObj : currentLinks) if (!linkObj.isEmpty()) { @@ -547,7 +550,7 @@ private: void process(const HierarchyObject::SubDirVec& currentDirs, const Zstring& parentRelativeNamePf, InSyncDir::DirList& dbDirs) { - hash_set toPreserve; + std::unordered_set toPreserve; for (const DirPair& dirObj : currentDirs) if (!dirObj.isEmpty()) switch (dirObj.getDirCategory()) @@ -558,7 +561,7 @@ private: //update directory entry only (shallow), but do *not touch* exising child elements!!! const Zstring& key = dirObj.getPairShortName(); - auto insertResult = dbDirs.insert(std::make_pair(key, InSyncDir(InSyncDir::DIR_STATUS_IN_SYNC))); //get or create + auto insertResult = dbDirs.emplace(key, InSyncDir(InSyncDir::DIR_STATUS_IN_SYNC)); //get or create auto it = insertResult.first; #if defined ZEN_WIN || defined ZEN_MAC //caveat: key might need to be updated, too, if there is a change in short name case!!! @@ -567,7 +570,7 @@ private: { auto oldValue = std::move(it->second); dbDirs.erase(it); //don't fiddle with decrementing "it"! - you might lose while optimizing pointlessly - it = dbDirs.insert(InSyncDir::DirList::value_type(key, std::move(oldValue))).first; + it = dbDirs.emplace(key, std::move(oldValue)).first; } #endif InSyncDir& dir = it->second; @@ -583,7 +586,7 @@ private: //Example: directories on left and right differ in case while sub-files are equal { //reuse last "in-sync" if available or insert strawman entry (do not try to update and thereby remove child elements!!!) - InSyncDir& dir = dbDirs.insert(std::make_pair(dirObj.getPairShortName(), InSyncDir(InSyncDir::DIR_STATUS_STRAW_MAN))).first->second; + InSyncDir& dir = dbDirs.emplace(dirObj.getPairShortName(), InSyncDir(InSyncDir::DIR_STATUS_STRAW_MAN)).first->second; toPreserve.insert(&dir); recurse(dirObj, dir); } @@ -739,12 +742,14 @@ void zen::saveLastSynchronousState(const BaseDirPair& baseDirObj) //throw FileEr streamsLeft [sessionID] = std::move(updatedStreamLeft); streamsRight[sessionID] = std::move(updatedStreamRight); + warn_static("TODO: implement saveStreams callback") + //write (temp-) files... zen::ScopeGuard guardTempFileLeft = zen::makeGuard([&] {zen::removeFile(dbNameLeftTmp); }); - saveStreams(streamsLeft, dbNameLeftTmp); //throw FileError + saveStreams(streamsLeft, dbNameLeftTmp, nullptr); //throw FileError zen::ScopeGuard guardTempFileRight = zen::makeGuard([&] {zen::removeFile(dbNameRightTmp); }); - saveStreams(streamsRight, dbNameRightTmp); //throw FileError + saveStreams(streamsRight, dbNameRightTmp, nullptr); //throw FileError //operation finished: rename temp files -> this should work transactionally: //if there were no write access, creation of temp files would have failed diff --git a/FreeFileSync/Source/lib/db_file.h b/FreeFileSync/Source/lib/db_file.h index ffaa0e62..9998bbc3 100644 --- a/FreeFileSync/Source/lib/db_file.h +++ b/FreeFileSync/Source/lib/db_file.h @@ -76,18 +76,17 @@ struct InSyncDir //convenience InSyncDir& addDir(const Zstring& shortName, InSyncStatus st) { - //use C++11 emplace when available - return dirs.insert(std::make_pair(shortName, InSyncDir(st))).first->second; + return dirs.emplace(shortName, InSyncDir(st)).first->second; } void addFile(const Zstring& shortName, const InSyncDescrFile& dataL, const InSyncDescrFile& dataR, CompareVariant cmpVar, std::uint64_t fileSize) { - files.insert(std::make_pair(shortName, InSyncFile(dataL, dataR, cmpVar, fileSize))); + files.emplace(shortName, InSyncFile(dataL, dataR, cmpVar, fileSize)); } void addSymlink(const Zstring& shortName, const InSyncDescrLink& dataL, const InSyncDescrLink& dataR, CompareVariant cmpVar) { - symlinks.insert(std::make_pair(shortName, InSyncSymlink(dataL, dataR, cmpVar))); + symlinks.emplace(shortName, InSyncSymlink(dataL, dataR, cmpVar)); } }; diff --git a/FreeFileSync/Source/lib/dir_exist_async.h b/FreeFileSync/Source/lib/dir_exist_async.h index 4d73f699..a7e9b0b7 100644 --- a/FreeFileSync/Source/lib/dir_exist_async.h +++ b/FreeFileSync/Source/lib/dir_exist_async.h @@ -8,7 +8,7 @@ #define DIR_EXIST_HEADER_08173281673432158067342132467183267 #include -#include +#include #include #include "../process_callback.h" #include "resolve_path.h" @@ -39,7 +39,7 @@ DirectoryStatus getExistingDirsUpdating(const std::set& d std::list>> futureInfo; for (const Zstring& dirpath : dirpaths) if (!dirpath.empty()) //skip empty dirs - futureInfo.push_back(std::make_pair(dirpath, async2([=]() -> bool + futureInfo.emplace_back(dirpath, async([=]() -> bool { #ifdef ZEN_WIN //1. login to network share, if necessary @@ -47,7 +47,7 @@ DirectoryStatus getExistingDirsUpdating(const std::set& d #endif //2. check dir existence return dirExists(dirpath); - }))); + })); //don't wait (almost) endlessly like win32 would on not existing network shares: const boost::system_time endTime = boost::get_system_time() + boost::posix_time::seconds(20); //consider CD-rom insert or hard disk spin up time from sleep diff --git a/FreeFileSync/Source/lib/dir_lock.cpp b/FreeFileSync/Source/lib/dir_lock.cpp index 1d22f455..1d1ce2ae 100644 --- a/FreeFileSync/Source/lib/dir_lock.cpp +++ b/FreeFileSync/Source/lib/dir_lock.cpp @@ -12,9 +12,8 @@ #include #include #include -#include #include -#include +#include #include #include @@ -341,8 +340,8 @@ struct LockInformation //throw FileError writeArray(stream, LOCK_FORMAT_DESCR, sizeof(LOCK_FORMAT_DESCR)); writeNumber(stream, LOCK_FORMAT_VER); - assert_static(sizeof(processId) <= sizeof(std::uint64_t)); //ensure cross-platform compatibility! - assert_static(sizeof(sessionId) <= sizeof(std::uint64_t)); // + static_assert(sizeof(processId) <= sizeof(std::uint64_t), ""); //ensure cross-platform compatibility! + static_assert(sizeof(sessionId) <= sizeof(std::uint64_t), ""); // writeContainer(stream, lockId); writeContainer(stream, computerName); @@ -368,7 +367,7 @@ struct LockInformation //throw FileError LockInformation retrieveLockInfo(const Zstring& lockfilepath) //throw FileError { - BinStreamIn streamIn = loadBinStream(lockfilepath); //throw FileError + BinStreamIn streamIn = loadBinStream(lockfilepath, nullptr); //throw FileError try { return LockInformation(streamIn); //throw UnexpectedEndOfStreamError @@ -484,7 +483,7 @@ void waitOnDirLock(const Zstring& lockfilepath, DirLockCallback* callback) //thr } //wait some time... - assert_static(1000 * POLL_LIFE_SIGN_INTERVAL % GUI_CALLBACK_INTERVAL == 0); + static_assert(1000 * POLL_LIFE_SIGN_INTERVAL % GUI_CALLBACK_INTERVAL == 0, ""); for (size_t i = 0; i < 1000 * POLL_LIFE_SIGN_INTERVAL / GUI_CALLBACK_INTERVAL; ++i) { if (callback) callback->requestUiRefresh(); diff --git a/FreeFileSync/Source/lib/error_log.h b/FreeFileSync/Source/lib/error_log.h index 2971f746..f9e351c6 100644 --- a/FreeFileSync/Source/lib/error_log.h +++ b/FreeFileSync/Source/lib/error_log.h @@ -34,7 +34,7 @@ void logError(const std::string& msg) //throw() const std::string logEntry = "[" + formatTime(FORMAT_DATE) + " "+ formatTime(FORMAT_TIME) + "] " + msg; try { - saveBinStream(getConfigDir() + Zstr("LastError.log"), logEntry); //throw FileError + saveBinStream(getConfigDir() + Zstr("LastError.log"), logEntry, nullptr); //throw FileError } catch (const FileError&) {} } diff --git a/FreeFileSync/Source/lib/ffs_paths.cpp b/FreeFileSync/Source/lib/ffs_paths.cpp index d5ef3e48..32026b25 100644 --- a/FreeFileSync/Source/lib/ffs_paths.cpp +++ b/FreeFileSync/Source/lib/ffs_paths.cpp @@ -5,7 +5,7 @@ // ************************************************************************** #include "ffs_paths.h" -#include +#include #include #include #include @@ -124,7 +124,7 @@ Zstring zen::getFreeFileSyncLauncher() ZEN_ON_SCOPE_EXIT(if (appURL) ::CFRelease(appURL)); if (::LSFindApplicationForInfo(kLSUnknownCreator, // OSType inCreator, - CFSTR("net.SourceForge.FreeFileSync"),//CFStringRef inBundleID, + CFSTR("Zenju.FreeFileSync"),//CFStringRef inBundleID, nullptr, //CFStringRef inName, nullptr, //FSRef *outAppRef, &appURL) == noErr) //CFURLRef *outAppURL diff --git a/FreeFileSync/Source/lib/generate_logfile.h b/FreeFileSync/Source/lib/generate_logfile.h index 41f6c945..af8d05a9 100644 --- a/FreeFileSync/Source/lib/generate_logfile.h +++ b/FreeFileSync/Source/lib/generate_logfile.h @@ -29,15 +29,44 @@ struct SummaryInfo void saveLogToFile(const SummaryInfo& summary, //throw FileError const ErrorLog& log, - FileOutput& fileOut); + FileOutput& fileOut, + const std::function& onUpdateSaveStatus); void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError const ErrorLog& log, - size_t maxBytesToWrite); + size_t maxBytesToWrite, + const std::function& onUpdateSaveStatus); +Zstring getLastSyncsLogfilePath(); +struct OnUpdateLogfileStatusNoThrow +{ + OnUpdateLogfileStatusNoThrow(ProcessCallback& pc, const Zstring& logfilePath) : pc_(pc), logfilePath_(logfilePath), bytesWritten(), + msg(replaceCpy(_("Saving log file %x..."), L"%x", fmtFileName(logfilePath_))) {} + + void operator()(std::int64_t bytesDelta) + { + bytesWritten += bytesDelta; + + if (updateUiIsAllowed()) //test if specific time span between ui updates is over + try + { + pc_.reportStatus(msg + L" (" + filesizeToShortString(bytesWritten) + L")"); //throw? + pc_.forceUiRefresh(); //throw? + } + catch (...) {} + } + +private: + ProcessCallback& pc_; + const Zstring logfilePath_; + std::int64_t bytesWritten; + const std::wstring msg; +}; + + //####################### implementation ####################### namespace @@ -78,12 +107,12 @@ std::wstring generateLogHeader(const SummaryInfo& s) //calculate max width, this considers UTF-16 only, not true Unicode...but maybe good idea? those 2-char-UTF16 codes are usually wider than fixed width chars anyway! size_t sepLineLen = 0; - std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { sepLineLen = std::max(sepLineLen, str.size()); }); + for (const std::wstring& str : results) sepLineLen = std::max(sepLineLen, str.size()); output.resize(output.size() + sepLineLen + 1, L'_'); output += L'\n'; - std::for_each(results.begin(), results.end(), [&](const std::wstring& str) { output += L'|'; output += str; output += L'\n'; }); + for (const std::wstring& str : results) { output += L'|'; output += str; output += L'\n'; } output += L'|'; output.resize(output.size() + sepLineLen, L'_'); @@ -97,7 +126,8 @@ std::wstring generateLogHeader(const SummaryInfo& s) inline void saveLogToFile(const SummaryInfo& summary, //throw FileError const ErrorLog& log, - FileOutput& fileOut) + FileOutput& fileOut, + const std::function& onUpdateSaveStatus) { Utf8String header = utfCvrtTo(generateLogHeader(summary)); replace(header, '\n', LINE_BREAK); //don't replace line break any earlier @@ -112,16 +142,22 @@ void saveLogToFile(const SummaryInfo& summary, //throw FileError msg += LINE_BREAK; //make sure string is not empty! fileOut.write(&*msg.begin(), msg.size()); //throw FileError + if (onUpdateSaveStatus) onUpdateSaveStatus(msg.size()); } } +inline +Zstring getLastSyncsLogfilePath() { return getConfigDir() + Zstr("LastSyncs.log"); } + + inline void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError const ErrorLog& log, - size_t maxBytesToWrite) //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems! + size_t maxBytesToWrite, //log may be *huge*, e.g. 1 million items; LastSyncs.log *must not* create performance problems! + const std::function& onUpdateSaveStatus) { - const Zstring filepath = getConfigDir() + Zstr("LastSyncs.log"); + const Zstring filepath = getLastSyncsLogfilePath(); Utf8String newStream = utfCvrtTo(generateLogHeader(summary)); replace(newStream, '\n', LINE_BREAK); //don't replace line break any earlier @@ -144,10 +180,14 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError //fill up the rest of permitted space by appending old log if (newStream.size() < maxBytesToWrite) { + std::function onUpdateLoadStatus; + if (onUpdateSaveStatus) + onUpdateLoadStatus = [&](std::int64_t bytesDelta) { onUpdateSaveStatus(0); }; + Utf8String oldStream; try { - oldStream = loadBinStream(filepath); //throw FileError + oldStream = loadBinStream(filepath, onUpdateLoadStatus); //throw FileError } catch (FileError&) {} @@ -174,7 +214,7 @@ void saveToLastSyncsLog(const SummaryInfo& summary, //throw FileError } } - saveBinStream(filepath, newStream); //throw FileError + saveBinStream(filepath, newStream, onUpdateSaveStatus); //throw FileError } } diff --git a/FreeFileSync/Source/lib/hard_filter.cpp b/FreeFileSync/Source/lib/hard_filter.cpp index 28ba1b40..ca1005d1 100644 --- a/FreeFileSync/Source/lib/hard_filter.cpp +++ b/FreeFileSync/Source/lib/hard_filter.cpp @@ -57,7 +57,7 @@ bool zen::operator<(const HardFilter& lhs, const HardFilter& rhs) namespace { //constructing them in addFilterEntry becomes perf issue for large filter lists -const Zstring asterisk(Zstr('*')); +const Zstring asterisk(Zstr("*")); const Zstring sepAsterisk = FILE_NAME_SEPARATOR + asterisk; const Zstring asteriskSep = asterisk + FILE_NAME_SEPARATOR; const Zstring asteriskSepAsterisk = asteriskSep + asterisk; @@ -68,10 +68,9 @@ void addFilterEntry(const Zstring& filterPhrase, std::vector& fileFilte { #if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case - Zstring filterFormatted = filterPhrase; - makeUpper(filterFormatted); + const Zstring& filterFmt= makeUpperCopy(filterPhrase); #elif defined ZEN_LINUX - const Zstring& filterFormatted = filterPhrase; + const Zstring& filterFmt = filterPhrase; //Linux DOES distinguish between upper/lower-case: nothing to do here #endif /* @@ -116,13 +115,13 @@ void addFilterEntry(const Zstring& filterPhrase, std::vector& fileFilte } }; - if (startsWith(filterFormatted, FILE_NAME_SEPARATOR)) // \abc - processTail(afterFirst(filterFormatted, FILE_NAME_SEPARATOR)); + if (startsWith(filterFmt, FILE_NAME_SEPARATOR)) // \abc + processTail(afterFirst(filterFmt, FILE_NAME_SEPARATOR)); else { - processTail(filterFormatted); - if (startsWith(filterFormatted, asteriskSep)) // *\abc - processTail(afterFirst(filterFormatted, asteriskSep)); + processTail(filterFmt); + if (startsWith(filterFmt, asteriskSep)) // *\abc + processTail(afterFirst(filterFmt, asteriskSep)); } } @@ -302,14 +301,13 @@ NameFilter::NameFilter(const Zstring& includeFilter, const Zstring& excludeFilte bool NameFilter::passFileFilter(const Zstring& relFilename) const { #if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case - Zstring nameFormatted = relFilename; - makeUpper(nameFormatted); + const Zstring& nameFmt = makeUpperCopy(relFilename); #elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case - const Zstring& nameFormatted = relFilename; //nothing to do here + const Zstring& nameFmt = relFilename; //nothing to do here #endif - return matchesFilter(nameFormatted, filterFileIn) && //process include filters - !matchesFilter(nameFormatted, filterFileEx); //process exclude filters + return matchesFilter(nameFmt, filterFileIn) && //process include filters + !matchesFilter(nameFmt, filterFileEx); //process exclude filters } @@ -318,24 +316,23 @@ bool NameFilter::passDirFilter(const Zstring& relDirname, bool* subObjMightMatch assert(!subObjMightMatch || *subObjMightMatch == true); //check correct usage #if defined ZEN_WIN || defined ZEN_MAC //Windows does NOT distinguish between upper/lower-case - Zstring nameFormatted = relDirname; - makeUpper(nameFormatted); + const Zstring& nameFmt = makeUpperCopy(relDirname); #elif defined ZEN_LINUX //Linux DOES distinguish between upper/lower-case - const Zstring& nameFormatted = relDirname; //nothing to do here + const Zstring& nameFmt = relDirname; //nothing to do here #endif - if (matchesFilter(nameFormatted, filterFolderEx)) //process exclude filters + if (matchesFilter(nameFmt, filterFolderEx)) //process exclude filters { if (subObjMightMatch) *subObjMightMatch = false; //exclude subfolders/subfiles as well return false; } - if (!matchesFilter(nameFormatted, filterFolderIn)) //process include filters + if (!matchesFilter(nameFmt, filterFolderIn)) //process include filters { if (subObjMightMatch) { - const Zstring& subNameBegin = nameFormatted + FILE_NAME_SEPARATOR; //const-ref optimization + const Zstring& subNameBegin = nameFmt + FILE_NAME_SEPARATOR; //const-ref optimization *subObjMightMatch = matchesFilterBegin(subNameBegin, filterFileIn) || //might match a file in subdirectory matchesFilterBegin(subNameBegin, filterFolderIn); //or another subdirectory diff --git a/FreeFileSync/Source/lib/hard_filter.h b/FreeFileSync/Source/lib/hard_filter.h index e747c626..19e7003b 100644 --- a/FreeFileSync/Source/lib/hard_filter.h +++ b/FreeFileSync/Source/lib/hard_filter.h @@ -67,16 +67,16 @@ HardFilter::FilterRef combineFilters(const HardFilter::FilterRef& first, class NullFilter : public HardFilter //no filtering at all { public: - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; - virtual bool isNull() const; + bool passFileFilter(const Zstring& relFilename) const override; + bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const override; + bool isNull() const override; private: friend class HardFilter; - // virtual void save(ZstreamOut& stream) const {} - virtual std::string uniqueClassIdentifier() const { return "NullFilter"; } + // void save(ZstreamOut& stream) const override {} + std::string uniqueClassIdentifier() const override { return "NullFilter"; } // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError - virtual bool cmpLessSameType(const HardFilter& other) const; + bool cmpLessSameType(const HardFilter& other) const override; }; @@ -85,18 +85,18 @@ class NameFilter : public HardFilter //standard filter by filepath public: NameFilter(const Zstring& includeFilter, const Zstring& excludeFilter); - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; + bool passFileFilter(const Zstring& relFilename) const override; + bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const override; - virtual bool isNull() const; + bool isNull() const override; static bool isNull(const Zstring& includeFilter, const Zstring& excludeFilter); //*fast* check without expensively constructing NameFilter instance! private: friend class HardFilter; - // virtual void save(ZstreamOut& stream) const; - virtual std::string uniqueClassIdentifier() const { return "NameFilter"; } + // void save(ZstreamOut& stream) const override; + std::string uniqueClassIdentifier() const override { return "NameFilter"; } // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError - virtual bool cmpLessSameType(const HardFilter& other) const; + bool cmpLessSameType(const HardFilter& other) const override; std::vector filterFileIn; // std::vector filterFolderIn; //upper case (windows) + unique items by construction @@ -113,16 +113,16 @@ class CombinedFilter : public HardFilter //combine two filters to match if and public: CombinedFilter(const FilterRef& first, const FilterRef& second) : first_(first), second_(second) {} - virtual bool passFileFilter(const Zstring& relFilename) const; - virtual bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const; - virtual bool isNull() const; + bool passFileFilter(const Zstring& relFilename) const override; + bool passDirFilter(const Zstring& relDirname, bool* subObjMightMatch) const override; + bool isNull() const override; private: friend class HardFilter; - // virtual void save(ZstreamOut& stream) const; - virtual std::string uniqueClassIdentifier() const { return "CombinedFilter"; } + // void save(ZstreamOut& stream) const override; + std::string uniqueClassIdentifier() const override { return "CombinedFilter"; } // static FilterRef load(ZstreamIn& stream); //throw UnexpectedEndOfStreamError - virtual bool cmpLessSameType(const HardFilter& other) const; + bool cmpLessSameType(const HardFilter& other) const override; const FilterRef first_; const FilterRef second_; diff --git a/FreeFileSync/Source/lib/icon_buffer.cpp b/FreeFileSync/Source/lib/icon_buffer.cpp index 73fe47d1..df0bee8b 100644 --- a/FreeFileSync/Source/lib/icon_buffer.cpp +++ b/FreeFileSync/Source/lib/icon_buffer.cpp @@ -32,7 +32,7 @@ namespace const size_t BUFFER_SIZE_MAX = 800; //maximum number of icons to hold in buffer: must be big enough to hold visible icons + preload buffer! Consider OS limit on GDI resources (wxBitmap)!!! #ifndef NDEBUG -boost::thread::id mainThreadId = boost::this_thread::get_id(); +const boost::thread::id mainThreadId = boost::this_thread::get_id(); #endif #ifdef ZEN_WIN @@ -89,7 +89,7 @@ public: wxBitmap extractWxBitmap() { ZEN_ON_SCOPE_EXIT(assert(!*this)); - assert(boost::this_thread::get_id() == mainThreadId ); + assert(boost::this_thread::get_id() == mainThreadId); if (!handle_) return wxNullBitmap; @@ -396,20 +396,20 @@ class WorkLoad public: Zstring extractNextFile() //context of worker thread, blocking { - assert(boost::this_thread::get_id() != mainThreadId ); + assert(boost::this_thread::get_id() != mainThreadId); boost::unique_lock dummy(lockFiles); while (filesToLoad.empty()) conditionNewFiles.timed_wait(dummy, boost::posix_time::milliseconds(100)); //interruption point! - Zstring filepath = filesToLoad.back(); - filesToLoad.pop_back(); + Zstring filepath = filesToLoad.back(); //yes, not std::bad_alloc exception-safe, but bad_alloc is not relevant for us + filesToLoad.pop_back(); // return filepath; } void setWorkload(const std::vector& newLoad) //context of main thread { - assert(boost::this_thread::get_id() == mainThreadId ); + assert(boost::this_thread::get_id() == mainThreadId); { boost::lock_guard dummy(lockFiles); filesToLoad = newLoad; @@ -420,7 +420,7 @@ public: void addToWorkload(const Zstring& newEntry) //context of main thread { - assert(boost::this_thread::get_id() == mainThreadId ); + assert(boost::this_thread::get_id() == mainThreadId); { boost::lock_guard dummy(lockFiles); filesToLoad.push_back(newEntry); //set as next item to retrieve @@ -474,7 +474,7 @@ public: boost::lock_guard dummy(lockIconList); //thread safety: moving IconHolder is free from side effects, but ~wxBitmap() is NOT! => do NOT delete items from iconList here! - auto rc = iconList.insert(std::make_pair(entryName, makeValueObject())); + auto rc = iconList.emplace(entryName, makeValueObject()); assert(rc.second); if (rc.second) //if insertion took place { diff --git a/FreeFileSync/Source/lib/localization.cpp b/FreeFileSync/Source/lib/localization.cpp index ab4b49d1..f891fdb4 100644 --- a/FreeFileSync/Source/lib/localization.cpp +++ b/FreeFileSync/Source/lib/localization.cpp @@ -5,6 +5,7 @@ // ************************************************************************** #include "localization.h" +#include #include #include #include @@ -38,7 +39,7 @@ public: wxLanguage langId() const { return langId_; } - virtual std::wstring translate(const std::wstring& text) override + std::wstring translate(const std::wstring& text) override { //look for translation in buffer table auto it = transMapping.find(text); @@ -47,7 +48,7 @@ public: return text; //fallback } - virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) override + std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) override { auto it = transMappingPl.find(std::make_pair(singular, plural)); if (it != transMappingPl.end()) @@ -60,7 +61,7 @@ public: } private: - typedef hash_map Translation; //hash_map is 15% faster than std::map on GCC + typedef std::unordered_map Translation; //hash_map is 15% faster than std::map on GCC typedef std::map, std::vector> TranslationPlural; Translation transMapping; //map original text |-> translation @@ -75,7 +76,7 @@ FFSTranslation::FFSTranslation(const Zstring& filepath, wxLanguage languageId) : std::string inputStream; try { - inputStream = loadBinStream(filepath); //throw FileError + inputStream = loadBinStream(filepath, nullptr); //throw FileError } catch (const FileError& e) { @@ -91,7 +92,7 @@ FFSTranslation::FFSTranslation(const Zstring& filepath, wxLanguage languageId) : { const std::wstring original = utfCvrtTo(item.first); const std::wstring translation = utfCvrtTo(item.second); - transMapping.insert(std::make_pair(original, translation)); + transMapping.emplace(original, translation); } for (const auto& item : transPluralInput) @@ -103,10 +104,10 @@ FFSTranslation::FFSTranslation(const Zstring& filepath, wxLanguage languageId) : for (const std::string& pf : item.second) plFormsWide.push_back(utfCvrtTo(pf)); - transMappingPl.insert(std::make_pair(std::make_pair(engSingular, engPlural), plFormsWide)); + transMappingPl.emplace(std::make_pair(engSingular, engPlural), plFormsWide); } - pluralParser = make_unique(header.pluralDefinition); //throw parse_plural::ParsingError + pluralParser = zen::make_unique(header.pluralDefinition); //throw parse_plural::ParsingError } @@ -115,16 +116,16 @@ class FindLngfiles : public zen::TraverseCallback public: FindLngfiles(std::vector& lngFiles) : lngFiles_(lngFiles) {} - virtual void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) + void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) override { if (endsWith(filepath, Zstr(".lng"))) lngFiles_.push_back(filepath); } - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) { return LINK_SKIP; } - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) { return nullptr; } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { return LINK_SKIP; } + TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) override { return nullptr; } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { assert(false); return ON_ERROR_IGNORE; } // private: std::vector& lngFiles_; @@ -197,7 +198,7 @@ ExistingTranslations::ExistingTranslations() { try { - const std::string stream = loadBinStream(filepath); //throw FileError + const std::string stream = loadBinStream(filepath, nullptr); //throw FileError lngfile::TransHeader lngHeader; lngfile::parseHeader(stream, lngHeader); //throw ParsingError @@ -395,7 +396,7 @@ public: static void init(wxLanguage lng) { locale.reset(); //avoid global locale lifetime overlap! wxWidgets cannot handle this and will crash! - locale.reset(new wxLocale); + locale = zen::make_unique(); const wxLanguageInfo* sysLngInfo = wxLocale::GetLanguageInfo(wxLocale::GetSystemLanguage()); const wxLanguageInfo* selLngInfo = wxLocale::GetLanguageInfo(lng); @@ -453,7 +454,7 @@ void zen::setLanguage(int language) //throw FileError else try { - zen::setTranslator(new FFSTranslation(utfCvrtTo(languageFile), static_cast(language))); //throw lngfile::ParsingError, parse_plural::ParsingError + zen::setTranslator(zen::make_unique(utfCvrtTo(languageFile), static_cast(language))); //throw lngfile::ParsingError, parse_plural::ParsingError } catch (lngfile::ParsingError& e) { diff --git a/FreeFileSync/Source/lib/lock_holder.h b/FreeFileSync/Source/lib/lock_holder.h index 2d7b6ef1..93588310 100644 --- a/FreeFileSync/Source/lib/lock_holder.h +++ b/FreeFileSync/Source/lib/lock_holder.h @@ -29,8 +29,8 @@ public: { public: WaitOnLockHandler(ProcessCallback& pc) : pc_(pc) {} - virtual void requestUiRefresh() { pc_.requestUiRefresh(); } //allowed to throw exceptions - virtual void reportStatus(const std::wstring& text) { pc_.reportStatus(text); } + void requestUiRefresh() override { pc_.requestUiRefresh(); } //allowed to throw exceptions + void reportStatus(const std::wstring& text) override { pc_.reportStatus(text); } private: ProcessCallback& pc_; } callback(procCallback); @@ -38,7 +38,7 @@ public: try { //lock file creation is synchronous and may block noticeably for very slow devices (usb sticks, mapped cloud storages) - lockHolder.push_back(DirLock(dirpathFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback)); //throw FileError + lockHolder.emplace_back(dirpathFmt + Zstr("sync") + LOCK_FILE_ENDING, &callback); //throw FileError } catch (const FileError& e) { diff --git a/FreeFileSync/Source/lib/osx_file_icon.h b/FreeFileSync/Source/lib/osx_file_icon.h deleted file mode 100644 index 5edfd740..00000000 --- a/FreeFileSync/Source/lib/osx_file_icon.h +++ /dev/null @@ -1,36 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_FILE_ICON_8427508422345342 -#define OSX_FILE_ICON_8427508422345342 - -#include -#include - -namespace osx -{ -struct ImageData -{ - ImageData(int w, int h) : width(w), height(h), rgb(w* h * 3), alpha(w* h) {} - ImageData(ImageData&& tmp) : width(tmp.width), height(tmp.height) { rgb.swap(tmp.rgb); alpha.swap(tmp.alpha); } - - const int width; - const int height; - std::vector rgb; //rgb-byte order for use with wxImage - std::vector alpha; - -private: - ImageData(const ImageData&); - ImageData& operator=(const ImageData&); -}; - -ImageData getThumbnail(const char* filename, int requestedSize); //throw SysError -ImageData getFileIcon (const char* filename, int requestedSize); //throw SysError -ImageData getDefaultFileIcon (int requestedSize); //throw SysError -ImageData getDefaultFolderIcon(int requestedSize); //throw SysError -} - -#endif //OSX_FILE_ICON_8427508422345342 diff --git a/FreeFileSync/Source/lib/osx_file_icon.mm b/FreeFileSync/Source/lib/osx_file_icon.mm deleted file mode 100644 index e6b384d3..00000000 --- a/FreeFileSync/Source/lib/osx_file_icon.mm +++ /dev/null @@ -1,179 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "osx_file_icon.h" -#include -#include -#include - -namespace -{ -osx::ImageData extractBytes(NSImage* nsImg, int requestedSize) //throw SysError; NSException? -{ - /* - wxBitmap(NSImage*) is not good enough: it calls "[NSBitmapImageRep imageRepWithData:[img TIFFRepresentation]]" - => inefficient: TIFFRepresentation converts all contained images of an NSImage - => lacking: imageRepWithData extracts the first contained image only! - => wxBitmap(NSImage*) is wxCocoa only, deprecated! - => wxWidgets generally is not thread-safe so care must be taken to use wxBitmap from main thread only! (e.g. race-condition on non-atomic ref-count!!!) - - -> we need only a single bitmap at a specific size => extract raw bytes for use with wxImage in a thread-safe way! - */ - - //we choose the Core Graphics solution; for the equivalent App-Kit way see: http://www.cocoabuilder.com/archive/cocoa/193131-is-lockfocus-main-thread-specific.html#193191 - - ZEN_OSX_ASSERT(requestedSize > 0); - NSRect rectProposed = NSMakeRect(0, 0, requestedSize, requestedSize); //this is merely a hint! - - CGImageRef imgRef = [nsImg CGImageForProposedRect:&rectProposed context:nil hints:nil]; - ZEN_OSX_ASSERT(imgRef != NULL); //can this fail? not documented; ownership?? not documented! - - const size_t width = ::CGImageGetWidth (imgRef); - const size_t height = ::CGImageGetHeight(imgRef); - - ZEN_OSX_ASSERT(width > 0 && height > 0 && requestedSize > 0); - - int trgWidth = width; - int trgHeight = height; - - const int maxExtent = std::max(width, height); //don't stretch small images, but shrink large ones instead! - if (requestedSize < maxExtent) - { - trgWidth = width * requestedSize / maxExtent; - trgHeight = height * requestedSize / maxExtent; - } - - CGColorSpaceRef colorSpace = ::CGColorSpaceCreateDeviceRGB(); - ZEN_OSX_ASSERT(colorSpace != NULL); //may fail - ZEN_ON_SCOPE_EXIT(::CGColorSpaceRelease(colorSpace)); - - std::vector buf(trgWidth* trgHeight * 4); //32-bit ARGB, little endian byte order -> already initialized with 0 = fully transparent - - //supported color spaces: https://developer.apple.com/library/mac/#documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB - CGContextRef ctxRef = ::CGBitmapContextCreate(&buf[0], //void *data, - trgWidth, //size_t width, - trgHeight, //size_t height, - 8, //size_t bitsPerComponent, - 4 * trgWidth, //size_t bytesPerRow, - colorSpace, //CGColorSpaceRef colorspace, - kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little); //CGBitmapInfo bitmapInfo - ZEN_OSX_ASSERT(ctxRef != NULL); - ZEN_ON_SCOPE_EXIT(::CGContextRelease(ctxRef)); - - ::CGContextDrawImage(ctxRef, CGRectMake(0, 0, trgWidth, trgHeight), imgRef); //can this fail? not documented - - //::CGContextFlush(ctxRef); //"If you pass [...] a bitmap context, this function does nothing." - - osx::ImageData imgOut(trgWidth, trgHeight); - - auto it = buf.begin(); - auto itOutRgb = imgOut.rgb.begin(); - auto itOutAlpha = imgOut.alpha.begin(); - for (int i = 0; i < trgWidth * trgHeight; ++i) - { - const unsigned char b = *it++; - const unsigned char g = *it++; - const unsigned char r = *it++; - const unsigned char a = *it++; - - //unsigned arithmetics caveat! - auto demultiplex = [&](unsigned char c) { return static_cast(numeric::clampCpy(a == 0 ? 0 : (c * 255 + a - 1) / a, 0, 255)); }; //=ceil(c * 255 / a) - - *itOutRgb++ = demultiplex(r); - *itOutRgb++ = demultiplex(g); - *itOutRgb++ = demultiplex(b); - *itOutAlpha++ = a; - } - - return imgOut; -} -} - - -osx::ImageData osx::getThumbnail(const char* filename, int requestedSize) //throw SysError -{ - @try - { - @autoreleasepool - { - NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding]; - ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented - //stringWithCString returns string which is already set to autorelease! - - NSImage* nsImg = [[[NSImage alloc] initWithContentsOfFile:nsFile] autorelease]; - ZEN_OSX_ASSERT(nsImg != nil); //may fail - - return extractBytes(nsImg, requestedSize); //throw SysError - } - } - @catch(NSException* e) - { - throwSysError(e); //throw SysError - } -} - - -osx::ImageData osx::getFileIcon(const char* filename, int requestedSize) //throw SysError -{ - @try - { - @autoreleasepool - { - NSString* nsFile = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding]; - ZEN_OSX_ASSERT(nsFile != nil); //throw SysError; can this fail? not documented - //stringWithCString returns string which is already set to autorelease! - - NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFile:nsFile]; - ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented - - return extractBytes(nsImg, requestedSize); //throw SysError - } - } - @catch (NSException* e) - { - throwSysError(e); //throw SysError - } -} - - -osx::ImageData osx::getDefaultFileIcon(int requestedSize) //throw SysError -{ - @try - { - @autoreleasepool - { - NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericDocumentIcon)]; - //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:@"dat"]; - ZEN_OSX_ASSERT(nsImg != nil); //can this fail? not documented - - return extractBytes(nsImg, requestedSize); //throw SysError - } - } - @catch (NSException* e) - { - throwSysError(e); //throw SysError - } -} - - -osx::ImageData osx::getDefaultFolderIcon(int requestedSize) //throw SysError -{ - @try - { - @autoreleasepool - { - NSImage* nsImg = [NSImage imageNamed:NSImageNameFolder]; - //NSImage* nsImg = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kGenericFolderIcon)]; - ZEN_OSX_ASSERT(nsImg != nil); //may fail - - return extractBytes(nsImg, requestedSize); //throw SysError - } - } - @catch (NSException* e) - { - throwSysError(e); //throw SysError - } -} diff --git a/FreeFileSync/Source/lib/parallel_scan.cpp b/FreeFileSync/Source/lib/parallel_scan.cpp index 18ac4216..930078b5 100644 --- a/FreeFileSync/Source/lib/parallel_scan.cpp +++ b/FreeFileSync/Source/lib/parallel_scan.cpp @@ -91,14 +91,14 @@ DiskInfo retrieveDiskInfo(const Zstring& pathName) std::vector 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 - nullptr, // lpInBuffer - 0, // nInBufferSize - &buffer[0], // output buffer - static_cast(buffer.size()), // size of output buffer - &bytesReturned, // number of bytes returned - nullptr)) // OVERLAPPED structure + if (!::DeviceIoControl(hVolume, //_In_ HANDLE hDevice, + IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, //_In_ DWORD dwIoControlCode, + nullptr, //_In_opt_ LPVOID lpInBuffer, + 0, //_In_ DWORD nInBufferSize, + &buffer[0], //_Out_opt_ LPVOID lpOutBuffer, + static_cast(buffer.size()), //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned + nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped return output; const VOLUME_DISK_EXTENTS& volDisks = *reinterpret_cast(&buffer[0]); @@ -313,13 +313,13 @@ public: relNameParentPf_(relNameParentPf), output_(output) {} - virtual void onFile (const Zchar* shortName, const Zstring& filepath, const FileInfo& details); - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details); - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath); - virtual void releaseDirTraverser(TraverseCallback* trav); + void onFile (const Zchar* shortName, const Zstring& filepath, const FileInfo& details) override; + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override; + TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) override; + void releaseDirTraverser(TraverseCallback* trav) override; - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber); - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName); + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override; + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override; private: TraverserShared& cfg; @@ -467,30 +467,6 @@ DirCallback::HandleError DirCallback::reportItemError(const std::wstring& msg, s return ON_ERROR_IGNORE; } - -#ifdef ZEN_WIN -class DstHackCallbackImpl : public DstHackCallback -{ -public: - DstHackCallbackImpl(AsyncCallback& acb, long threadID) : - acb_(acb), - threadID_(threadID), - textApplyingDstHack(replaceCpy(_("Encoding extended time information: %x"), L"%x", L"\n%x")) {} - -private: - virtual void requestUiRefresh(const Zstring& filepath) //applying DST hack imposes significant one-time performance drawback => callback to inform user - { - boost::this_thread::interruption_point(); - - acb_.reportCurrentStatus(replaceCpy(textApplyingDstHack, L"%x", fmtFileName(filepath)), threadID_); - } - - AsyncCallback& acb_; - long threadID_; - const std::wstring textApplyingDstHack; -}; -#endif - //------------------------------------------------------------------------------------------ class WorkerThread @@ -523,14 +499,8 @@ public: Zstring(), dirOutput_.dirCont); - DstHackCallback* dstCallbackPtr = nullptr; -#ifdef ZEN_WIN - DstHackCallbackImpl dstCallback(*acb_, threadID_); - dstCallbackPtr = &dstCallback; -#endif - //get all files and folders from directoryPostfixed (and subdirectories) - traverseFolder(dirKey_.dirpath_, traverser, dstCallbackPtr); //exceptions may be thrown! + traverseFolder(dirKey_.dirpath_, traverser); //exceptions may be thrown! } private: diff --git a/FreeFileSync/Source/lib/parse_lng.h b/FreeFileSync/Source/lib/parse_lng.h index 12e5290b..9fbebff4 100644 --- a/FreeFileSync/Source/lib/parse_lng.h +++ b/FreeFileSync/Source/lib/parse_lng.h @@ -139,8 +139,8 @@ public: private: struct Item { virtual ~Item() {} virtual bool hasTranslation() const = 0; }; - struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationMap ::value_type value; }; - struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} virtual bool hasTranslation() const { return !value.second.empty(); } TranslationPluralMap::value_type value; }; + struct RegularItem : public Item { RegularItem(const TranslationMap ::value_type& val) : value(val) {} bool hasTranslation() const override { return !value.second.empty(); } TranslationMap ::value_type value; }; + struct PluralItem : public Item { PluralItem (const TranslationPluralMap::value_type& val) : value(val) {} bool hasTranslation() const override { return !value.second.empty(); } TranslationPluralMap::value_type value; }; const TranslationNewItemPos newItemPos_; std::list> sequence; //ordered list of translation elements @@ -212,28 +212,28 @@ private: KnownTokens() { //header information - tokens.insert(std::make_pair(Token::TK_HEADER_BEGIN, "
    ")); - tokens.insert(std::make_pair(Token::TK_HEADER_END, "
    ")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_LANG_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_TRANS_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_TRANS_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_LOCALE_NAME_END, "")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_FLAG_FILE_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_COUNT_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_DEF_END, "")); + tokens.emplace(Token::TK_HEADER_BEGIN, "
    "); + tokens.emplace(Token::TK_HEADER_END, "
    "); + tokens.emplace(Token::TK_LANG_NAME_BEGIN, ""); + tokens.emplace(Token::TK_LANG_NAME_END, ""); + tokens.emplace(Token::TK_TRANS_NAME_BEGIN, ""); + tokens.emplace(Token::TK_TRANS_NAME_END, ""); + tokens.emplace(Token::TK_LOCALE_NAME_BEGIN, ""); + tokens.emplace(Token::TK_LOCALE_NAME_END, ""); + tokens.emplace(Token::TK_FLAG_FILE_BEGIN, ""); + tokens.emplace(Token::TK_FLAG_FILE_END, ""); + tokens.emplace(Token::TK_PLURAL_COUNT_BEGIN, ""); + tokens.emplace(Token::TK_PLURAL_COUNT_END, ""); + tokens.emplace(Token::TK_PLURAL_DEF_BEGIN, ""); + tokens.emplace(Token::TK_PLURAL_DEF_END, ""); //item level - tokens.insert(std::make_pair(Token::TK_SRC_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_SRC_END, "")); - tokens.insert(std::make_pair(Token::TK_TRG_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_TRG_END, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_BEGIN, "")); - tokens.insert(std::make_pair(Token::TK_PLURAL_END, "")); + tokens.emplace(Token::TK_SRC_BEGIN, ""); + tokens.emplace(Token::TK_SRC_END, ""); + tokens.emplace(Token::TK_TRG_BEGIN, ""); + tokens.emplace(Token::TK_TRG_END, ""); + tokens.emplace(Token::TK_PLURAL_BEGIN, ""); + tokens.emplace(Token::TK_PLURAL_END, ""); } TokenMap tokens; }; @@ -418,7 +418,7 @@ private: consumeToken(Token::TK_TRG_END); validateTranslation(original, translation); //throw throw ParsingError - out.insert(std::make_pair(original, translation)); + out.emplace(original, translation); } void parsePlural(TranslationPluralMap& pluralOut, const parse_plural::PluralFormInfo& pluralInfo) @@ -453,7 +453,7 @@ private: const SingularPluralPair original(engSingular, engPlural); validateTranslation(original, pluralList, pluralInfo); - pluralOut.insert(std::make_pair(original, pluralList)); + pluralOut.emplace(original, pluralList); } void validateTranslation(const std::string& original, const std::string& translation) //throw ParsingError diff --git a/FreeFileSync/Source/lib/parse_plural.h b/FreeFileSync/Source/lib/parse_plural.h index bac933c9..c19714de 100644 --- a/FreeFileSync/Source/lib/parse_plural.h +++ b/FreeFileSync/Source/lib/parse_plural.h @@ -122,7 +122,7 @@ struct BinaryExp : public Expr typedef std::shared_ptr> ExpRhs; BinaryExp(const ExpLhs& lhs, const ExpRhs& rhs, StlOp biop) : lhs_(lhs), rhs_(rhs), biop_(biop) { assert(lhs && rhs); } - virtual typename StlOp::result_type eval() const { return biop_(lhs_->eval(), rhs_->eval()); } + typename StlOp::result_type eval() const override { return biop_(lhs_->eval(), rhs_->eval()); } private: ExpLhs lhs_; ExpRhs rhs_; @@ -146,7 +146,7 @@ struct ConditionalExp : public Expr const std::shared_ptr>& thenExp, const std::shared_ptr>& elseExp) : ifExp_(ifExp), thenExp_(thenExp), elseExp_(elseExp) { assert(ifExp && thenExp && elseExp); } - virtual typename Expr::ValueType eval() const { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } + typename Expr::ValueType eval() const override { return ifExp_->eval() ? thenExp_->eval() : elseExp_->eval(); } private: std::shared_ptr> ifExp_; std::shared_ptr> thenExp_; @@ -156,7 +156,7 @@ private: struct ConstNumberExp : public Expr { ConstNumberExp(std::int64_t n) : n_(n) {} - virtual std::int64_t eval() const { return n_; } + std::int64_t eval() const override { return n_; } private: std::int64_t n_; }; @@ -164,7 +164,7 @@ private: struct VariableNumberNExp : public Expr { VariableNumberNExp(std::int64_t& n) : n_(n) {} - virtual std::int64_t eval() const { return n_; } + std::int64_t eval() const override { return n_; } private: std::int64_t& n_; }; @@ -205,21 +205,21 @@ class Scanner public: Scanner(const std::string& stream) : stream_(stream), pos(stream_.begin()) { - tokens.push_back(std::make_pair("?" , Token::TK_TERNARY_QUEST)); - tokens.push_back(std::make_pair(":" , Token::TK_TERNARY_COLON)); - tokens.push_back(std::make_pair("||", Token::TK_OR )); - tokens.push_back(std::make_pair("&&", Token::TK_AND )); - tokens.push_back(std::make_pair("==", Token::TK_EQUAL )); - tokens.push_back(std::make_pair("!=", Token::TK_NOT_EQUAL )); - tokens.push_back(std::make_pair("<=", Token::TK_LESS_EQUAL )); - tokens.push_back(std::make_pair("<" , Token::TK_LESS )); - tokens.push_back(std::make_pair(">=", Token::TK_GREATER_EQUAL)); - tokens.push_back(std::make_pair(">" , Token::TK_GREATER )); - tokens.push_back(std::make_pair("%" , Token::TK_MODULUS )); - tokens.push_back(std::make_pair("n" , Token::TK_VARIABLE_N )); - tokens.push_back(std::make_pair("N" , Token::TK_VARIABLE_N )); - tokens.push_back(std::make_pair("(" , Token::TK_BRACKET_LEFT )); - tokens.push_back(std::make_pair(")" , Token::TK_BRACKET_RIGHT)); + tokens.emplace_back("?" , Token::TK_TERNARY_QUEST); + tokens.emplace_back(":" , Token::TK_TERNARY_COLON); + tokens.emplace_back("||", Token::TK_OR ); + tokens.emplace_back("&&", Token::TK_AND ); + tokens.emplace_back("==", Token::TK_EQUAL ); + tokens.emplace_back("!=", Token::TK_NOT_EQUAL ); + tokens.emplace_back("<=", Token::TK_LESS_EQUAL ); + tokens.emplace_back("<" , Token::TK_LESS ); + tokens.emplace_back(">=", Token::TK_GREATER_EQUAL); + tokens.emplace_back(">" , Token::TK_GREATER ); + tokens.emplace_back("%" , Token::TK_MODULUS ); + tokens.emplace_back("n" , Token::TK_VARIABLE_N ); + tokens.emplace_back("N" , Token::TK_VARIABLE_N ); + tokens.emplace_back("(" , Token::TK_BRACKET_LEFT ); + tokens.emplace_back(")" , Token::TK_BRACKET_RIGHT); } Token nextToken() diff --git a/FreeFileSync/Source/lib/process_xml.cpp b/FreeFileSync/Source/lib/process_xml.cpp index 98262882..5f7dbf14 100644 --- a/FreeFileSync/Source/lib/process_xml.cpp +++ b/FreeFileSync/Source/lib/process_xml.cpp @@ -7,7 +7,7 @@ #include "process_xml.h" #include #include -#include +#include #include #include #include "ffs_paths.h" @@ -794,11 +794,11 @@ void readConfig(const XmlIn& in, SyncConfig& syncCfg) void readConfig(const XmlIn& in, FilterConfig& filter) { - std::vector tmp = splitFilterByLines(filter.includeFilter); //default value + std::vector tmp = splitFilterByLines(filter.includeFilter); //save default value in["Include"](tmp); filter.includeFilter = mergeFilterLines(tmp); - std::vector tmp2 = splitFilterByLines(filter.excludeFilter); //default value + std::vector tmp2 = splitFilterByLines(filter.excludeFilter); //save default value in["Exclude"](tmp2); filter.excludeFilter = mergeFilterLines(tmp2); diff --git a/FreeFileSync/Source/lib/process_xml.h b/FreeFileSync/Source/lib/process_xml.h index 6d834922..6f07e0c9 100644 --- a/FreeFileSync/Source/lib/process_xml.h +++ b/FreeFileSync/Source/lib/process_xml.h @@ -209,17 +209,17 @@ struct XmlGlobalSettings { //default external apps will be translated "on the fly"!!! First entry will be used for [Enter] or mouse double-click! #ifdef ZEN_WIN - externelApplications.push_back(std::make_pair(L"Show in Explorer", L"explorer /select, \"%item_path%\"")); - externelApplications.push_back(std::make_pair(L"Open with default application", L"\"%item_path%\"")); + externelApplications.emplace_back(L"Show in Explorer", L"explorer /select, \"%item_path%\""); + externelApplications.emplace_back(L"Open with default application", L"\"%item_path%\""); //mark for extraction: _("Show in Explorer") //mark for extraction: _("Open with default application") #elif defined ZEN_LINUX - externelApplications.push_back(std::make_pair(L"Browse directory", L"xdg-open \"%item_folder%\"")); - externelApplications.push_back(std::make_pair(L"Open with default application", L"xdg-open \"%item_path%\"")); + externelApplications.emplace_back(L"Browse directory", L"xdg-open \"%item_folder%\""); + externelApplications.emplace_back(L"Open with default application", L"xdg-open \"%item_path%\""); //mark for extraction: _("Browse directory") Linux doesn't use the term "folder" #elif defined ZEN_MAC - externelApplications.push_back(std::make_pair(L"Browse directory", L"open -R \"%item_path%\"")); - externelApplications.push_back(std::make_pair(L"Open with default application", L"open \"%item_path%\"")); + externelApplications.emplace_back(L"Browse directory", L"open -R \"%item_path%\""); + externelApplications.emplace_back(L"Open with default application", L"open \"%item_path%\""); #endif } diff --git a/FreeFileSync/Source/lib/resolve_path.cpp b/FreeFileSync/Source/lib/resolve_path.cpp index 4688ccbd..a589260f 100644 --- a/FreeFileSync/Source/lib/resolve_path.cpp +++ b/FreeFileSync/Source/lib/resolve_path.cpp @@ -10,7 +10,7 @@ #ifdef ZEN_WIN #include -#include +#include #include //includes "windows.h" #include #include @@ -112,7 +112,7 @@ private: { Zstring dirpath = buffer; if (!dirpath.empty()) - output.insert(std::make_pair(paramName, dirpath)); + output.emplace(paramName, dirpath); } }; @@ -138,7 +138,7 @@ private: Zstring dirpath = path; if (!dirpath.empty()) - output.insert(std::make_pair(paramName, dirpath)); + output.emplace(paramName, dirpath); } } }; @@ -224,7 +224,7 @@ std::unique_ptr getEnvironmentVar(const Zstring& envName) //return null value.length() >= 2) value = wxString(value.c_str() + 1, value.length() - 2); - return make_unique(utfCvrtTo(value)); + return zen::make_unique(utfCvrtTo(value)); } @@ -235,13 +235,13 @@ std::unique_ptr resolveMacro(const Zstring& macro, //macro without %-ch //there exist environment variables named %TIME%, %DATE% so check for our internal macros first! if (equalNoCase(macro, Zstr("time"))) - return make_unique(formatTime(Zstr("%H%M%S"))); + return zen::make_unique(formatTime(Zstr("%H%M%S"))); if (equalNoCase(macro, Zstr("date"))) - return make_unique(formatTime(FORMAT_ISO_DATE)); + return zen::make_unique(formatTime(FORMAT_ISO_DATE)); if (equalNoCase(macro, Zstr("timestamp"))) - return make_unique(formatTime(Zstr("%Y-%m-%d %H%M%S"))); //e.g. "2012-05-15 131513" + return zen::make_unique(formatTime(Zstr("%Y-%m-%d %H%M%S"))); //e.g. "2012-05-15 131513" std::unique_ptr cand; auto processPhrase = [&](const Zchar* phrase, const Zchar* format) -> bool @@ -249,7 +249,7 @@ std::unique_ptr resolveMacro(const Zstring& macro, //macro without %-ch if (!equalNoCase(macro, phrase)) return false; - cand = make_unique(formatTime(format)); + cand = zen::make_unique(formatTime(format)); return true; }; @@ -266,7 +266,7 @@ std::unique_ptr resolveMacro(const Zstring& macro, //macro without %-ch { auto it = std::find_if(ext.begin(), ext.end(), [&](const std::pair& p) { return equalNoCase(macro, p.first); }); if (it != ext.end()) - return make_unique(it->second); + return zen::make_unique(it->second); } //try to resolve as environment variable @@ -279,7 +279,7 @@ std::unique_ptr resolveMacro(const Zstring& macro, //macro without %-ch const auto& csidlMap = CsidlConstants::get(); auto it = csidlMap.find(macro); if (it != csidlMap.end()) - return make_unique(it->second); + return zen::make_unique(it->second); } #endif @@ -471,7 +471,7 @@ void getDirectoryAliasesRecursive(const Zstring& dirpath, std::set value = getEnvironmentVar(envName)) - envToDir.insert(std::make_pair(envName, *value)); + envToDir.emplace(envName, *value); }; #ifdef ZEN_WIN addEnvVar(L"AllUsersProfile"); // C:\ProgramData @@ -498,11 +498,7 @@ void getDirectoryAliasesRecursive(const Zstring& dirpath, std::set bool { #if defined ZEN_WIN || defined ZEN_MAC - Zstring tmp = path; - Zstring tmp2 = prefix; - ::makeUpper(tmp); - ::makeUpper(tmp2); - return startsWith(tmp, tmp2); + return startsWith(makeUpperCopy(path), makeUpperCopy(prefix)); #elif defined ZEN_LINUX return startsWith(path, prefix); #endif diff --git a/FreeFileSync/Source/lib/shadow.cpp b/FreeFileSync/Source/lib/shadow.cpp deleted file mode 100644 index be9ba1f3..00000000 --- a/FreeFileSync/Source/lib/shadow.cpp +++ /dev/null @@ -1,128 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "shadow.h" -#include -#include //includes "windows.h" -#include -#include -#include -#include -#include -#include "../dll/ShadowCopy/shadow.h" -#include - -using namespace zen; -using namespace shadow; - - -namespace -{ -bool runningWOW64() //test if process is running under WOW64 (reference http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx) -{ - typedef BOOL (WINAPI* IsWow64ProcessFun)(HANDLE hProcess, PBOOL Wow64Process); - - const SysDllFun isWow64Process(L"kernel32.dll", "IsWow64Process"); - if (isWow64Process) - { - BOOL isWow64 = FALSE; - if (isWow64Process(::GetCurrentProcess(), &isWow64)) - return isWow64 != FALSE; - } - return false; -} -} - -//############################################################################################################# - -class ShadowCopy::ShadowVolume -{ -public: - ShadowVolume(const Zstring& volumeNamePf) : //throw FileError - createShadowCopy (getDllName(), funName_createShadowCopy), - releaseShadowCopy (getDllName(), funName_releaseShadowCopy), - getShadowVolume (getDllName(), funName_getShadowVolume), - getLastErrorMessage(getDllName(), funName_getLastErrorMessage), - backupHandle(nullptr) - { - //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 - if (runningWOW64()) - throw FileError(_("Cannot access the Volume Shadow Copy Service."), - _("Please use FreeFileSync 64-bit version to create shadow copies on this system.")); - - //check if shadow copy dll was loaded correctly - if (!createShadowCopy || !releaseShadowCopy || !getShadowVolume || !getLastErrorMessage) - throw FileError(_("Cannot access the Volume Shadow Copy Service."), - replaceCpy(_("Cannot load file %x."), L"%x", fmtFileName(getDllName()))); - - //--------------------------------------------------------------------------------------------------------- - //start volume shadow copy service: - backupHandle = createShadowCopy(volumeNamePf.c_str()); - if (!backupHandle) - throw FileError(_("Cannot access the Volume Shadow Copy Service."), getLastErrorMessage() + std::wstring(L" Volume: ") + fmtFileName(volumeNamePf)); - - shadowVolPf = appendSeparator(getShadowVolume(backupHandle)); //shadowVolName NEVER has a trailing backslash - } - - ~ShadowVolume() { releaseShadowCopy(backupHandle); } //fast! no performance optimization necessary - - Zstring geNamePf() const { return shadowVolPf; } //with trailing path separator - -private: - ShadowVolume (const ShadowVolume&) = delete; - ShadowVolume& operator=(const ShadowVolume&) = delete; - - const DllFun createShadowCopy; - const DllFun releaseShadowCopy; - const DllFun getShadowVolume; - const DllFun getLastErrorMessage; - - Zstring shadowVolPf; - ShadowHandle backupHandle; -}; - -//############################################################################################################# - -Zstring ShadowCopy::makeShadowCopy(const Zstring& inputFile, const std::function& onBeforeMakeVolumeCopy) -{ - Zstring filepathFinal = inputFile; - - //try to resolve symlinks and junctions: - //1. symlinks: we need to retrieve the target path, else we would just return a symlink on a VSS volume while the target outside were still locked! - //2. junctions: C:\Users\ is a junction that may link to e.g. D:\Users\, so GetVolumePathName() returns "D:\" => "Volume name %x not part of file name %y." - if (vistaOrLater()) - filepathFinal = getResolvedFilePath(inputFile); //throw FileError; requires Vista or later! - //-> returns paths with \\?\ prefix! => make sure to avoid duplicate shadow copies for volume paths with/without prefix - - DWORD bufferSize = 10000; - std::vector volBuffer(bufferSize); - if (!::GetVolumePathName(filepathFinal.c_str(), //__in LPCTSTR lpszFileName, - &volBuffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - throwFileError(replaceCpy(_("Cannot determine volume name for %x."), L"%x", fmtFileName(filepathFinal)), L"GetVolumePathName", ::GetLastError()); - - const Zstring volumeNamePf = appendSeparator(&volBuffer[0]); //msdn: if buffer is 1 char too short, GetVolumePathName() may skip last separator without error! - - //input file is always absolute! directory formatting takes care of this! Therefore volume name can always be found. - const size_t pos = filepathFinal.find(volumeNamePf); //filepathFinal needs NOT to begin with volumeNamePf: consider \\?\ prefix! - if (pos == Zstring::npos) - throw FileError(replaceCpy(replaceCpy(_("Volume name %x is not part of file path %y."), - L"%x", fmtFileName(volumeNamePf)), - L"%y", fmtFileName(filepathFinal))); - - //get or create instance of shadow volume - auto it = shadowVol.find(volumeNamePf); - if (it == shadowVol.end()) - { - onBeforeMakeVolumeCopy(volumeNamePf); //notify client before (potentially) blocking some time - auto newEntry = std::make_shared(volumeNamePf); //throw FileError - it = shadowVol.insert(std::make_pair(volumeNamePf, newEntry)).first; - } - - //return filepath alias on shadow copy volume - return it->second->geNamePf() + Zstring(filepathFinal.c_str() + pos + volumeNamePf.length()); -} diff --git a/FreeFileSync/Source/lib/shadow.h b/FreeFileSync/Source/lib/shadow.h deleted file mode 100644 index 4f2dbf32..00000000 --- a/FreeFileSync/Source/lib/shadow.h +++ /dev/null @@ -1,36 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef SHADOW_H_INCLUDED -#define SHADOW_H_INCLUDED - -#include -#include -#include -#include -#include - -namespace shadow -{ -class ShadowCopy //take and buffer Windows Volume Shadow Copy snapshots as needed -{ -public: - ShadowCopy() {} - - //return filepath on shadow copy volume - follows symlinks! - Zstring makeShadowCopy(const Zstring& inputFile, const std::function& onBeforeMakeVolumeCopy); //throw FileError - -private: - ShadowCopy (const ShadowCopy&) = delete; - ShadowCopy& operator=(const ShadowCopy&) = delete; - - class ShadowVolume; - typedef std::map, LessFilename> VolNameShadowMap; - VolNameShadowMap shadowVol; -}; -} - -#endif //SHADOW_H_INCLUDED diff --git a/FreeFileSync/Source/lib/status_handler.h b/FreeFileSync/Source/lib/status_handler.h index fd0c3cb6..1ee23c38 100644 --- a/FreeFileSync/Source/lib/status_handler.h +++ b/FreeFileSync/Source/lib/status_handler.h @@ -59,16 +59,16 @@ public: protected: //implement parts of ProcessCallback - virtual void initNewPhase(int objectsTotal, std::int64_t dataTotal, Phase phaseId) //may throw + void initNewPhase(int objectsTotal, std::int64_t dataTotal, Phase phaseId) override //may throw { currentPhase_ = phaseId; refNumbers(numbersTotal_, currentPhase_) = std::make_pair(objectsTotal, dataTotal); } - virtual void updateProcessedData(int objectsDelta, std::int64_t dataDelta) { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods MUST NOT throw in order - virtual void updateTotalData (int objectsDelta, std::int64_t dataDelta) { updateData(numbersTotal_ , objectsDelta, dataDelta); } //to properly allow undoing setting of statistics! + void updateProcessedData(int objectsDelta, std::int64_t dataDelta) override { updateData(numbersCurrent_, objectsDelta, dataDelta); } //note: these methods MUST NOT throw in order + void updateTotalData (int objectsDelta, std::int64_t dataDelta) override { updateData(numbersTotal_ , objectsDelta, dataDelta); } //to properly allow undoing setting of statistics! - virtual void requestUiRefresh() //throw X + void requestUiRefresh() override //throw X { if (abortRequested) //triggered by requestAbortion() { @@ -79,26 +79,26 @@ protected: forceUiRefresh(); } - virtual void reportStatus(const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw X */ } - virtual void reportInfo (const std::wstring& text) { statusText_ = text; requestUiRefresh(); /*throw X */ } //log text in derived class + void reportStatus(const std::wstring& text) override { if (!abortRequested) statusText_ = text; requestUiRefresh(); /*throw X */ } + void reportInfo (const std::wstring& text) override { if (!abortRequested) statusText_ = text; requestUiRefresh(); /*throw X */ } //log text in derived class //implement AbortCallback - virtual void requestAbortion() + void requestAbortion() override { abortRequested = true; statusText_ = _("Stop requested: Waiting for current operation to finish..."); - } //this does NOT call abortProcessNow() immediately, but when we're out of the C GUI call stack + } //called from GUI code: this does NOT call abortProcessNow() immediately, but when we're out of the C GUI call stack //implement Statistics - virtual Phase currentPhase() const { return currentPhase_; } + Phase currentPhase() const override { return currentPhase_; } - virtual int getObjectsCurrent(Phase phaseId) const { return refNumbers(numbersCurrent_, phaseId).first; } - virtual int getObjectsTotal (Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).first; } + int getObjectsCurrent(Phase phaseId) const override { return refNumbers(numbersCurrent_, phaseId).first; } + int getObjectsTotal (Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).first; } - virtual std::int64_t getDataCurrent(Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersCurrent_, phaseId).second; } - virtual std::int64_t getDataTotal (Phase phaseId) const { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).second; } + std::int64_t getDataCurrent(Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersCurrent_, phaseId).second; } + std::int64_t getDataTotal (Phase phaseId) const override { assert(phaseId != PHASE_SCANNING); return refNumbers(numbersTotal_ , phaseId).second; } - virtual const std::wstring& currentStatusText() const { return statusText_; } + const std::wstring& currentStatusText() const override { return statusText_; } bool abortIsRequested() const { return abortRequested; } diff --git a/FreeFileSync/Source/lib/versioning.cpp b/FreeFileSync/Source/lib/versioning.cpp index 9da6bdf0..58648771 100644 --- a/FreeFileSync/Source/lib/versioning.cpp +++ b/FreeFileSync/Source/lib/versioning.cpp @@ -1,7 +1,7 @@ #include "versioning.h" #include #include //required by GCC 4.8.1 to find ptrdiff_t -#include +#include #include #include @@ -172,14 +172,13 @@ void moveFile(const Zstring& sourceFile, //throw FileError { auto copyDelete = [&] { + assert(!somethingExists(targetFile)); + //create target if (symlinkExists(sourceFile)) copySymlink(sourceFile, targetFile, false); //throw FileError; don't copy filesystem permissions else - { - auto onDeleteTargetFile = [&]() { assert(!somethingExists(targetFile)); }; - copyFile(sourceFile, targetFile, false, true, onDeleteTargetFile, onUpdateCopyStatus); //throw FileError - permissions "false", transactional copy "true" - } + copyFile(sourceFile, targetFile, false, true, nullptr, onUpdateCopyStatus); //throw FileError - permissions "false", transactional copy "true" //delete source removeFile(sourceFile); //throw FileError; newly copied file is NOT deleted if exception is thrown here! @@ -210,12 +209,12 @@ public: TraverseFilesOneLevel(std::vector& files, std::vector& dirs) : files_(files), dirs_(dirs) {} private: - virtual void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) + void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) override { files_.push_back(shortName); } - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { if (dirExists(linkpath)) //dir symlink dirs_.push_back(shortName); @@ -224,14 +223,14 @@ private: return LINK_SKIP; } - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) + TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) override { dirs_.push_back(shortName); return nullptr; //DON'T traverse into subdirs; moveDirectory works recursively! } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); } - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); } + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { throw FileError(msg); } std::vector& files_; std::vector& dirs_; @@ -317,11 +316,11 @@ void FileVersioner::revisionDirImpl(const Zstring& dirpath, const Zstring& relat std::vector dirList; // { TraverseFilesOneLevel tol(fileList, dirList); //throw FileError - traverseFolder(dirpath, tol); // + traverseFolder(dirpath, tol); // } const Zstring dirpathPf = appendSeparator(dirpath); - const Zstring relpathPf = appendSeparator(relativePath); + const Zstring relpathPf = appendSeparator(relativePath); //move files for (const Zstring& shortname : fileList) @@ -352,11 +351,11 @@ public: TraverseVersionsOneLevel(std::vector& files, std::function updateUI) : files_(files), updateUI_(updateUI) {} private: - virtual void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) { files_.push_back(shortName); updateUI_(); } - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) { files_.push_back(shortName); updateUI_(); return LINK_SKIP; } - virtual std::shared_ptr onDir(const Zchar* shortName, const Zstring& dirpath) { updateUI_(); return nullptr; } //DON'T traverse into subdirs - virtual HandleError reportDirError (const std::wstring& msg) { throw FileError(msg); } - virtual HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) { throw FileError(msg); } + void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) override { files_.push_back(shortName); updateUI_(); } + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { files_.push_back(shortName); updateUI_(); return LINK_SKIP; } + std::shared_ptr onDir(const Zchar* shortName, const Zstring& dirpath) override { updateUI_(); return nullptr; } //DON'T traverse into subdirs + HandleError reportDirError (const std::wstring& msg) override { throw FileError(msg); } + HandleError reportItemError(const std::wstring& msg, const Zchar* shortName) override { throw FileError(msg); } std::vector& files_; std::function updateUI_; diff --git a/FreeFileSync/Source/structures.cpp b/FreeFileSync/Source/structures.cpp index fa9c8b16..f8716669 100644 --- a/FreeFileSync/Source/structures.cpp +++ b/FreeFileSync/Source/structures.cpp @@ -35,9 +35,9 @@ std::wstring zen::getVariantName(DirectionConfig::Variant var) case DirectionConfig::TWOWAY: return L"<- " + _("Two way") + L" ->"; case DirectionConfig::MIRROR: - return _("Mirror") + L" ->>"; + return _("Mirror") + L" ->"; case DirectionConfig::UPDATE: - return _("Update") + L" ->"; + return _("Update") + L" >"; case DirectionConfig::CUSTOM: return _("Custom"); } @@ -454,7 +454,7 @@ MainConfiguration zen::merge(const std::vector& mainCfgs) auto it = std::find_if(cmpCfgStat.begin(), cmpCfgStat.end(), [&](const std::pair& entry) { return effectivelyEqual(entry.first, cmpCfg); }); if (it == cmpCfgStat.end()) - cmpCfgStat.push_back(std::make_pair(cmpCfg, 1)); + cmpCfgStat.emplace_back(cmpCfg, 1); else ++(it->second); } @@ -464,7 +464,7 @@ MainConfiguration zen::merge(const std::vector& mainCfgs) auto it = std::find_if(syncCfgStat.begin(), syncCfgStat.end(), [&](const std::pair& entry) { return effectivelyEqual(entry.first, syncCfg); }); if (it == syncCfgStat.end()) - syncCfgStat.push_back(std::make_pair(syncCfg, 1)); + syncCfgStat.emplace_back(syncCfg, 1); else ++(it->second); } diff --git a/FreeFileSync/Source/structures.h b/FreeFileSync/Source/structures.h index bf0f3baf..8f718d15 100644 --- a/FreeFileSync/Source/structures.h +++ b/FreeFileSync/Source/structures.h @@ -10,7 +10,6 @@ #include #include #include -#include namespace zen { diff --git a/FreeFileSync/Source/synchronization.cpp b/FreeFileSync/Source/synchronization.cpp index 2c3af007..8aed5d16 100644 --- a/FreeFileSync/Source/synchronization.cpp +++ b/FreeFileSync/Source/synchronization.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -142,7 +142,7 @@ void SyncStatistics::processFile(const FilePair& fileObj) break; case SO_UNRESOLVED_CONFLICT: - conflictMsgs.push_back(std::make_pair(fileObj.getPairRelativePath(), fileObj.getSyncOpConflict())); + conflictMsgs.emplace_back(fileObj.getPairRelativePath(), fileObj.getSyncOpConflict()); break; case SO_COPY_METADATA_TO_LEFT: @@ -192,7 +192,7 @@ void SyncStatistics::processLink(const SymlinkPair& linkObj) break; case SO_UNRESOLVED_CONFLICT: - conflictMsgs.push_back(std::make_pair(linkObj.getPairRelativePath(), linkObj.getSyncOpConflict())); + conflictMsgs.emplace_back(linkObj.getPairRelativePath(), linkObj.getSyncOpConflict()); break; case SO_MOVE_LEFT_SOURCE: @@ -229,7 +229,7 @@ void SyncStatistics::processDir(const DirPair& dirObj) break; case SO_UNRESOLVED_CONFLICT: - conflictMsgs.push_back(std::make_pair(dirObj.getPairRelativePath(), dirObj.getSyncOpConflict())); + conflictMsgs.emplace_back(dirObj.getPairRelativePath(), dirObj.getSyncOpConflict()); break; case SO_COPY_METADATA_TO_LEFT: @@ -277,7 +277,8 @@ std::vector zen::extractSyncCfg(const MainConfiguration& FolderPairSyncCfg(syncCfg.directionCfg.var == DirectionConfig::TWOWAY || detectMovedFilesEnabled(syncCfg.directionCfg), syncCfg.handleDeletion, syncCfg.versioningStyle, - getFormattedDirectoryPath(syncCfg.versioningDirectory))); + getFormattedDirectoryPath(syncCfg.versioningDirectory), + syncCfg.directionCfg.var)); } return output; } @@ -349,7 +350,7 @@ private: FileVersioner& getOrCreateVersioner() //throw FileError! => dont create in DeletionHandling()!!! { if (!versioner.get()) - versioner = make_unique(versioningDir_, versioningStyle_, timeStamp_); //throw FileError + versioner = zen::make_unique(versioningDir_, versioningStyle_, timeStamp_); //throw FileError return *versioner; }; @@ -484,7 +485,6 @@ void DeletionHandling::tryCleanup(bool allowUserCallback) //throw FileError; thr #ifdef ZEN_WIN if (!toBeRecycled.empty()) { - //may throw: first exception is swallowed, notifyDeletionStatus() is then called again where it should throw again and the exception will propagate as expected auto notifyDeletionStatus = [&](const Zstring& currentItem) { if (!currentItem.empty()) @@ -865,7 +865,7 @@ private: const std::function& onDeleteTargetFile, const std::function& onNotifyFileCopy) const; //throw FileError - void verifyFileCopy(const Zstring& source, const Zstring& target) const; + void verifyFiles(const Zstring& source, const Zstring& target, const std::function& onUpdateStatus) const; //throw FileError template DeletionHandling& getDelHandling(); @@ -1684,9 +1684,9 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation reportInfo(txtCreatingFolder, target); try { - makeDirectoryPlain(target, dirObj.getFullPath(), copyFilePermissions_); //throw FileError, ErrorTargetExisting, (ErrorTargetPathMissing) + makeDirectoryPlain(target, dirObj.getFullPath(), copyFilePermissions_); //throw FileError } - catch (const ErrorTargetExisting&) { if (!dirExists(target)) throw; } //detect clash with file (dir-symlink OTOH is okay) + catch (const FileError&) { if (!dirExists(target)) throw; } //update DirPair dirObj.setSyncedTo(dirObj.getItemName()); @@ -1761,7 +1761,6 @@ void SynchronizeFolderPair::synchronizeFolderInt(DirPair& dirObj, SyncOperation procCallback_.requestUiRefresh(); //may throw } - //########################################################################################### InSyncAttributes SynchronizeFolderPair::copyFileWithCallback(const Zstring& sourceFile, //throw FileError @@ -1783,7 +1782,10 @@ InSyncAttributes SynchronizeFolderPair::copyFileWithCallback(const Zstring& sour if (verifyCopiedFiles_) { auto guardTarget = makeGuard([&] { removeFile(targetFile); }); //delete target if verification fails - verifyFileCopy(sourceFileTmp, targetFile); //throw FileError + + procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtFileName(targetFile))); + verifyFiles(sourceFileTmp, targetFile, [&](std::int64_t bytesDelta) { procCallback_.requestUiRefresh(); }); //throw FileError + guardTarget.dismiss(); } //#################### /Verification ############################# @@ -1806,11 +1808,10 @@ InSyncAttributes SynchronizeFolderPair::copyFileWithCallback(const Zstring& sour try { //contains prefix: E.g. "\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy1\Program Files\FFS\sample.dat" - shadowSource = shadowCopyHandler_->makeShadowCopy(sourceFile, //throw FileError + shadowSource = shadowCopyHandler_->makeShadowCopy(sourceFile, //throw FileError [&](const Zstring& volumeName) { procCallback_.reportStatus(replaceCpy(_("Creating a Volume Shadow Copy for %x..."), L"%x", fmtFileName(volumeName))); - procCallback_.forceUiRefresh(); }); } catch (const FileError& e2) //enhance error massage @@ -1827,17 +1828,13 @@ InSyncAttributes SynchronizeFolderPair::copyFileWithCallback(const Zstring& sour } //--------------------- data verification ------------------------- -struct VerifyCallback -{ - virtual ~VerifyCallback() {} - virtual void updateStatus() = 0; -}; - -void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& callback) //throw FileError +void SynchronizeFolderPair::verifyFiles(const Zstring& source, const Zstring& target, const std::function& onUpdateStatus) const //throw FileError { static std::vector memory1(1024 * 1024); //1024 kb seems to be a reasonable buffer size static std::vector memory2(1024 * 1024); + warn_static("redesign: access still buffered:") + #ifdef ZEN_WIN wxFile file1(applyLongPathPrefix(source).c_str(), wxFile::read); //don't use buffered file input for verification! #elif defined ZEN_LINUX || defined ZEN_MAC @@ -1859,15 +1856,16 @@ void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& c const size_t length1 = file1.Read(&memory1[0], memory1.size()); if (file1.Error()) throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(source))); - callback.updateStatus(); const size_t length2 = file2.Read(&memory2[0], memory2.size()); if (file2.Error()) throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(target))); - callback.updateStatus(); if (length1 != length2 || ::memcmp(&memory1[0], &memory2[0], length1) != 0) throw FileError(replaceCpy(replaceCpy(_("Data verification error: %x and %y have different content."), L"%x", L"\n" + fmtFileName(source)), L"%y", L"\n" + fmtFileName(target))); + + if (onUpdateStatus) + onUpdateStatus(length1); } while (!file1.Eof()); @@ -1875,27 +1873,6 @@ void verifyFiles(const Zstring& source, const Zstring& target, VerifyCallback& c throw FileError(replaceCpy(replaceCpy(_("Data verification error: %x and %y have different content."), L"%x", L"\n" + fmtFileName(source)), L"%y", L"\n" + fmtFileName(target))); } - -class VerifyStatusUpdater : public VerifyCallback -{ -public: - VerifyStatusUpdater(ProcessCallback& statusHandler) : statusHandler_(statusHandler) {} - - virtual void updateStatus() { statusHandler_.requestUiRefresh(); } //trigger display refresh - -private: - ProcessCallback& statusHandler_; -}; - - -void SynchronizeFolderPair::verifyFileCopy(const Zstring& source, const Zstring& target) const -{ - procCallback_.reportInfo(replaceCpy(txtVerifying, L"%x", fmtFileName(target))); - - VerifyStatusUpdater callback(procCallback_); - tryReportingError([&] { verifyFiles(source, target, callback); }, procCallback_); -} - //########################################################################################### template //create base directories first (if not yet existing) -> no symlink or attribute copying! @@ -1941,6 +1918,13 @@ struct ReadWriteCount size_t reads; size_t writes; }; + +enum class FolderPairJobType +{ + PROCESS, + WRITE_DB_ONLY, + SKIP, +}; } @@ -1992,7 +1976,7 @@ void zen::synchronize(const TimeComp& timeStamp, ProcessCallback::PHASE_SYNCHRONIZING); - std::deque skipFolderPair(folderCmp.size()); //folder pairs may be skipped after fatal errors were found + std::vector jobType(folderCmp.size(), FolderPairJobType::PROCESS); //folder pairs may be skipped after fatal errors were found //-------------------execute basic checks all at once before starting sync-------------------------------------- @@ -2009,7 +1993,7 @@ void zen::synchronize(const TimeComp& timeStamp, return false; }; - auto dependentDir = [](const Zstring& lhs, const Zstring& rhs) //note: this is NOT an equivalence relation! + auto havePathDependency = [](const Zstring& lhs, const Zstring& rhs) //note: this is NOT an equivalence relation! { return EqualFilename()(Zstring(lhs.c_str(), std::min(lhs.length(), rhs.length())), Zstring(rhs.c_str(), std::min(lhs.length(), rhs.length()))); @@ -2019,21 +2003,26 @@ void zen::synchronize(const TimeComp& timeStamp, std::map dirReadWriteCount; //count read/write accesses for (auto j = begin(folderCmp); j != end(folderCmp); ++j) { - dirReadWriteCount[j->getBaseDirPf()]; //create all entries first! - dirReadWriteCount[j->getBaseDirPf()]; //=> counting accesses is complex for later inserts! + //create all entries first! otherwise counting accesses is too complex during later inserts! + if (!j->getBaseDirPf().empty()) // is always a dependent directory => exclude! + dirReadWriteCount[j->getBaseDirPf()]; + if (!j->getBaseDirPf().empty()) + dirReadWriteCount[j->getBaseDirPf()]; } auto incReadCount = [&](const Zstring& baseDir) { - for (auto& item : dirReadWriteCount) - if (dependentDir(baseDir, item.first)) - ++item.second.reads; + if (!baseDir.empty()) + for (auto& item : dirReadWriteCount) + if (havePathDependency(baseDir, item.first)) + ++item.second.reads; }; auto incWriteCount = [&](const Zstring& baseDir) { - for (auto& item : dirReadWriteCount) - if (dependentDir(baseDir, item.first)) - ++item.second.writes; + if (!baseDir.empty()) + for (auto& item : dirReadWriteCount) + if (havePathDependency(baseDir, item.first)) + ++item.second.writes; }; std::vector> significantDiffPairs; @@ -2054,7 +2043,7 @@ void zen::synchronize(const TimeComp& timeStamp, //exclude some pathological case (leftdir, rightdir are empty) if (EqualFilename()(j->getBaseDirPf(), j->getBaseDirPf())) { - skipFolderPair[folderIndex] = true; + jobType[folderIndex] = FolderPairJobType::SKIP; continue; } @@ -2069,20 +2058,20 @@ void zen::synchronize(const TimeComp& timeStamp, folderPairStat.getUpdate() + folderPairStat.getDelete() > 0; - //skip folder pair if there is nothing to do (except for two-way mode and move-detection, where DB files need to be written) - if (!writeLeft && !writeRight && - !folderPairCfg.saveSyncDB_) + //skip folder pair if there is nothing to do (except for two-way mode and move-detection, where DB files need to be updated) + //-> skip creating (not yet existing) base directories in particular if there's no need + if (!writeLeft && !writeRight) { - skipFolderPair[folderIndex] = true; //skip creating (not yet existing) base directories in particular if there's no need + jobType[folderIndex] = folderPairCfg.saveSyncDB_ ? FolderPairJobType::WRITE_DB_ONLY : FolderPairJobType::SKIP; continue; } //aggregate information of folders used by multiple pairs in read/write access - if (!dependentDir(j->getBaseDirPf(), j->getBaseDirPf())) //true in general + if (!havePathDependency(j->getBaseDirPf(), j->getBaseDirPf())) //true in general { if (writeLeft && writeRight) { - incWriteCount(j->getBaseDirPf()); + incWriteCount(j->getBaseDirPf()); incWriteCount(j->getBaseDirPf()); } else if (writeLeft) @@ -2107,7 +2096,7 @@ void zen::synchronize(const TimeComp& timeStamp, (j->getBaseDirPf().empty() && (writeRight || folderPairCfg.saveSyncDB_))) { callback.reportFatalError(_("Target folder input field must not be empty.")); - skipFolderPair[folderIndex] = true; + jobType[folderIndex] = FolderPairJobType::SKIP; continue; } @@ -2117,7 +2106,7 @@ void zen::synchronize(const TimeComp& timeStamp, if (dirNotFoundAnymore(j->getBaseDirPf(), j->isExisting()) || dirNotFoundAnymore(j->getBaseDirPf(), j->isExisting())) { - skipFolderPair[folderIndex] = true; + jobType[folderIndex] = FolderPairJobType::SKIP; continue; } @@ -2140,7 +2129,7 @@ void zen::synchronize(const TimeComp& timeStamp, if (sourceDirNotFound(j->getBaseDirPf(), j->isExisting()) || sourceDirNotFound(j->getBaseDirPf(), j->isExisting())) { - skipFolderPair[folderIndex] = true; + jobType[folderIndex] = FolderPairJobType::SKIP; continue; } @@ -2152,14 +2141,14 @@ void zen::synchronize(const TimeComp& timeStamp, { //should not arrive here: already checked in SyncCfgDialog callback.reportFatalError(_("Please enter a target folder for versioning.")); - skipFolderPair[folderIndex] = true; + jobType[folderIndex] = FolderPairJobType::SKIP; continue; } } //check if more than 50% of total number of files/dirs are to be created/overwritten/deleted if (significantDifferenceDetected(folderPairStat)) - significantDiffPairs.push_back(std::make_pair(j->getBaseDirPf(), j->getBaseDirPf())); + significantDiffPairs.emplace_back(j->getBaseDirPf(), j->getBaseDirPf()); //check for sufficient free diskspace auto checkSpace = [&](const Zstring& baseDirPf, std::int64_t minSpaceNeeded) @@ -2170,7 +2159,7 @@ void zen::synchronize(const TimeComp& timeStamp, if (0 < freeSpace && //zero disk space probably means "request not supported" (e.g. see WebDav) freeSpace < minSpaceNeeded) - diskSpaceMissing.push_back(std::make_pair(baseDirPf, std::make_pair(minSpaceNeeded, freeSpace))); + diskSpaceMissing.emplace_back(baseDirPf, std::make_pair(minSpaceNeeded, freeSpace)); } catch (FileError&) {} }; @@ -2296,13 +2285,14 @@ void zen::synchronize(const TimeComp& timeStamp, const size_t folderIndex = j - begin(folderCmp); const FolderPairSyncCfg& folderPairCfg = syncConfig[folderIndex]; - if (skipFolderPair[folderIndex]) //folder pairs may be skipped after fatal errors were found + if (jobType[folderIndex] == FolderPairJobType::SKIP) //folder pairs may be skipped after fatal errors were found continue; //------------------------------------------------------------------------------------------ - callback.reportInfo(_("Synchronizing folder pair:") + L"\n" + - L" " + j->getBaseDirPf() + L"\n" + - L" " + j->getBaseDirPf()); + if (jobType[folderIndex] == FolderPairJobType::PROCESS) + callback.reportInfo(_("Synchronizing folder pair:") + L" [" + getVariantName(folderPairCfg.syncVariant_) + L"]\n" + + L" " + j->getBaseDirPf() + L"\n" + + L" " + j->getBaseDirPf()); //------------------------------------------------------------------------------------------ //checking a second time: (a long time may have passed since the intro checks!) @@ -2326,60 +2316,63 @@ void zen::synchronize(const TimeComp& timeStamp, catch (FileError&) {} }); - //guarantee removal of invalid entries (where element on both sides is empty) - ZEN_ON_SCOPE_EXIT(BaseDirPair::removeEmpty(*j);); - - bool copyPermissionsFp = false; - tryReportingError([&] + if (jobType[folderIndex] == FolderPairJobType::PROCESS) { - copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides! - !j->getBaseDirPf().empty() && //scenario: directory selected on one side only - !j->getBaseDirPf().empty() && // - supportsPermissions(beforeLast(j->getBaseDirPf(), FILE_NAME_SEPARATOR)) && //throw FileError - supportsPermissions(beforeLast(j->getBaseDirPf(), FILE_NAME_SEPARATOR)); - }, callback); //show error dialog if necessary + //guarantee removal of invalid entries (where element is empty on both sides) + ZEN_ON_SCOPE_EXIT(BaseDirPair::removeEmpty(*j);); + bool copyPermissionsFp = false; + tryReportingError([&] + { + copyPermissionsFp = copyFilePermissions && //copy permissions only if asked for and supported by *both* sides! + !j->getBaseDirPf().empty() && //scenario: directory selected on one side only + !j->getBaseDirPf().empty() && // + supportsPermissions(beforeLast(j->getBaseDirPf(), FILE_NAME_SEPARATOR)) && //throw FileError + supportsPermissions(beforeLast(j->getBaseDirPf(), FILE_NAME_SEPARATOR)); + }, callback); //show error dialog if necessary - auto getEffectiveDeletionPolicy = [&](const Zstring& baseDirPf) -> DeletionPolicy - { -#ifdef ZEN_WIN - if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) + + auto getEffectiveDeletionPolicy = [&](const Zstring& baseDirPf) -> DeletionPolicy { - auto it = baseDirHasRecycler.find(baseDirPf); - if (it != baseDirHasRecycler.end()) //buffer filled during intro checks (but only if deletions are expected) - if (!it->second) - return DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks) - } +#ifdef ZEN_WIN + if (folderPairCfg.handleDeletion == DELETE_TO_RECYCLER) + { + auto it = baseDirHasRecycler.find(baseDirPf); + if (it != baseDirHasRecycler.end()) //buffer filled during intro checks (but only if deletions are expected) + if (!it->second) + return DELETE_PERMANENTLY; //Windows' ::SHFileOperation() will do this anyway, but we have a better and faster deletion routine (e.g. on networks) + } #endif - return folderPairCfg.handleDeletion; - }; + return folderPairCfg.handleDeletion; + }; - DeletionHandling delHandlerL(getEffectiveDeletionPolicy(j->getBaseDirPf()), - folderPairCfg.versioningFolder, - folderPairCfg.versioningStyle_, - timeStamp, - j->getBaseDirPf(), - callback); + DeletionHandling delHandlerL(getEffectiveDeletionPolicy(j->getBaseDirPf()), + folderPairCfg.versioningFolder, + folderPairCfg.versioningStyle_, + timeStamp, + j->getBaseDirPf(), + callback); - DeletionHandling delHandlerR(getEffectiveDeletionPolicy(j->getBaseDirPf()), - folderPairCfg.versioningFolder, - folderPairCfg.versioningStyle_, - timeStamp, - j->getBaseDirPf(), - callback); + DeletionHandling delHandlerR(getEffectiveDeletionPolicy(j->getBaseDirPf()), + folderPairCfg.versioningFolder, + folderPairCfg.versioningStyle_, + timeStamp, + j->getBaseDirPf(), + callback); - SynchronizeFolderPair syncFP(callback, verifyCopiedFiles, copyPermissionsFp, transactionalFileCopy, + SynchronizeFolderPair syncFP(callback, verifyCopiedFiles, copyPermissionsFp, transactionalFileCopy, #ifdef ZEN_WIN - shadowCopyHandler.get(), + shadowCopyHandler.get(), #endif - delHandlerL, delHandlerR); - syncFP.startSync(*j); + delHandlerL, delHandlerR); + syncFP.startSync(*j); - //(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway... - tryReportingError([&] { delHandlerL.tryCleanup(); /*throw FileError*/}, callback); //show error dialog if necessary - tryReportingError([&] { delHandlerR.tryCleanup(); /*throw FileError*/}, callback); // + //(try to gracefully) cleanup temporary Recycle bin folders and versioning -> will be done in ~DeletionHandling anyway... + tryReportingError([&] { delHandlerL.tryCleanup(); /*throw FileError*/}, callback); //show error dialog if necessary + tryReportingError([&] { delHandlerR.tryCleanup(); /*throw FileError*/}, callback); // + } //(try to gracefully) write database file if (folderPairCfg.saveSyncDB_) diff --git a/FreeFileSync/Source/synchronization.h b/FreeFileSync/Source/synchronization.h index 11f560be..6b1cbfdd 100644 --- a/FreeFileSync/Source/synchronization.h +++ b/FreeFileSync/Source/synchronization.h @@ -63,16 +63,19 @@ struct FolderPairSyncCfg FolderPairSyncCfg(bool saveSyncDB, const DeletionPolicy handleDel, VersioningStyle versioningStyle, - const Zstring& versioningDirFmt) : + const Zstring& versioningDirFmt, + DirectionConfig::Variant syncVariant) : saveSyncDB_(saveSyncDB), handleDeletion(handleDel), versioningStyle_(versioningStyle), - versioningFolder(versioningDirFmt) {} + versioningFolder(versioningDirFmt), + syncVariant_(syncVariant) {} bool saveSyncDB_; //save database if in automatic mode or dection of moved files is active DeletionPolicy handleDeletion; VersioningStyle versioningStyle_; Zstring versioningFolder; //formatted directory name + DirectionConfig::Variant syncVariant_; }; std::vector extractSyncCfg(const MainConfiguration& mainCfg); diff --git a/FreeFileSync/Source/ui/batch_config.cpp b/FreeFileSync/Source/ui/batch_config.cpp index dc6a76c1..643fde32 100644 --- a/FreeFileSync/Source/ui/batch_config.cpp +++ b/FreeFileSync/Source/ui/batch_config.cpp @@ -40,16 +40,16 @@ public: size_t onCompletionHistoryMax); private: - virtual void OnClose (wxCloseEvent& event) { EndModal(BUTTON_CANCEL); } - virtual void OnCancel (wxCommandEvent& event) { EndModal(BUTTON_CANCEL); } - virtual void OnSaveBatchJob(wxCommandEvent& event); - virtual void OnErrorPopup (wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_POPUP; updateGui(); } - virtual void OnErrorIgnore(wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_IGNORE; updateGui(); } - virtual void OnErrorStop (wxCommandEvent& event) { localBatchCfg.handleError = ON_ERROR_STOP; updateGui(); } - virtual void OnHelpScheduleBatch(wxHyperlinkEvent& event) { displayHelpEntry(L"html/Schedule a Batch Job.html", this); } - - virtual void OnToggleGenerateLogfile(wxCommandEvent& event) { updateGui(); } - virtual void OnToggleLogfilesLimit (wxCommandEvent& event) { updateGui(); } + void OnClose (wxCloseEvent& event) override { EndModal(BUTTON_CANCEL); } + void OnCancel (wxCommandEvent& event) override { EndModal(BUTTON_CANCEL); } + void OnSaveBatchJob(wxCommandEvent& event) override; + void OnErrorPopup (wxCommandEvent& event) override { localBatchCfg.handleError = ON_ERROR_POPUP; updateGui(); } + void OnErrorIgnore (wxCommandEvent& event) override { localBatchCfg.handleError = ON_ERROR_IGNORE; updateGui(); } + void OnErrorStop (wxCommandEvent& event) override { localBatchCfg.handleError = ON_ERROR_STOP; updateGui(); } + void OnHelpScheduleBatch(wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Schedule a Batch Job.html", this); } + + void OnToggleGenerateLogfile(wxCommandEvent& event) override { updateGui(); } + void OnToggleLogfilesLimit (wxCommandEvent& event) override { updateGui(); } void updateGui(); //re-evaluate gui after config changes diff --git a/FreeFileSync/Source/ui/batch_status_handler.cpp b/FreeFileSync/Source/ui/batch_status_handler.cpp index b40941fa..5f021484 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.cpp +++ b/FreeFileSync/Source/ui/batch_status_handler.cpp @@ -5,7 +5,7 @@ // ************************************************************************** #include "batch_status_handler.h" -#include +#include #include #include #include @@ -39,30 +39,34 @@ Zstring addStatusToLogfilename(const Zstring& logfilepath, const std::wstring& s class FindLogfiles : public TraverseCallback { public: - FindLogfiles(const Zstring& prefix, std::vector& logfiles) : prefix_(prefix), logfiles_(logfiles) {} + FindLogfiles(const Zstring& prefix, std::vector& logfiles, const std::function& onUpdateStatus) : prefix_(prefix), logfiles_(logfiles), onUpdateStatus_(onUpdateStatus) {} private: - virtual void onFile(const Zchar* shortName, const Zstring& filePath, const FileInfo& details) + void onFile(const Zchar* shortName, const Zstring& filePath, const FileInfo& details) override { const Zstring fileName(shortName); if (startsWith(fileName, prefix_) && endsWith(fileName, Zstr(".log"))) logfiles_.push_back(filePath); + + if (onUpdateStatus_) + onUpdateStatus_(); } - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) { return nullptr; } //DON'T traverse into subdirs - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) { return LINK_SKIP; } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { assert(false); return ON_ERROR_IGNORE; } // + TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) override { return nullptr; } //DON'T traverse into subdirs + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { return LINK_SKIP; } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { assert(false); return ON_ERROR_IGNORE; } //errors are not really critical in this context + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { assert(false); return ON_ERROR_IGNORE; } // const Zstring prefix_; std::vector& logfiles_; + const std::function onUpdateStatus_; }; -void limitLogfileCount(const Zstring& logdir, const std::wstring& jobname, size_t maxCount) //throw() +void limitLogfileCount(const Zstring& logdir, const std::wstring& jobname, size_t maxCount, const std::function& onUpdateStatus) //throw() { std::vector logFiles; - FindLogfiles traverseCallback(utfCvrtTo(jobname), logFiles); //throw()! + FindLogfiles traverseCallback(utfCvrtTo(jobname), logFiles, onUpdateStatus); //throw()! traverseFolder(logdir, traverseCallback); if (logFiles.size() <= maxCount) @@ -71,8 +75,12 @@ void limitLogfileCount(const Zstring& logdir, const std::wstring& jobname, size_ //delete oldest logfiles: take advantage of logfile naming convention to find them std::nth_element(logFiles.begin(), logFiles.end() - maxCount, logFiles.end(), LessFilename()); - std::for_each(logFiles.begin(), logFiles.end() - maxCount, - [](const Zstring& filepath) { try { removeFile(filepath); } catch (FileError&) {} }); + std::for_each(logFiles.begin(), logFiles.end() - maxCount, [&](const Zstring& filepath) + { + try { removeFile(filepath); } + catch (FileError&) {}; + onUpdateStatus(); + }); } @@ -95,7 +103,7 @@ std::unique_ptr prepareNewLogfile(const Zstring& logfileDirectory, / for (int i = 0;; ++i) try { - return make_unique(filepath, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetExisting + return zen::make_unique(filepath, FileOutput::ACC_CREATE_NEW); //throw FileError, ErrorTargetExisting //*no* file system race-condition! } catch (const ErrorTargetExisting&) @@ -161,7 +169,7 @@ BatchStatusHandler::~BatchStatusHandler() { switchBatchToGui_.execute(); //open FreeFileSync GUI } - catch (...) {} + catch (...) { assert(false); } showFinalResults = false; } else if (progressDlg) @@ -181,7 +189,7 @@ BatchStatusHandler::~BatchStatusHandler() try { //use EXEC_TYPE_ASYNC until there is reason no to: https://sourceforge.net/p/freefilesync/discussion/help/thread/828dca52 - tryReportingError([&] { shellExecute2(expandMacros(finalCommand), EXEC_TYPE_ASYNC); }, //throw FileError, throw X? + tryReportingError([&] { shellExecute(expandMacros(finalCommand), EXEC_TYPE_ASYNC); }, //throw FileError, throw X? *this); } catch (...) {} @@ -232,23 +240,19 @@ BatchStatusHandler::~BatchStatusHandler() (wxGetUTCTimeMillis().GetValue() - startTime_) / 1000 }; - //print the results list: logfile + //----------------- write results into user-specified logfile ------------------------ if (logFile.get()) { - try + if (logfilesCountLimit_ > 0) { - //saving log file below may take a *long* time, so report (without logging) - reportStatus(replaceCpy(_("Saving log file %x..."), L"%x", fmtFileName(logFile->getFilename()))); //throw? - forceUiRefresh(); // + try { reportStatus(_("Cleaning up old log files exceeding limit...")); } + catch (...) {} + limitLogfileCount(beforeLast(logFile->getFilename(), FILE_NAME_SEPARATOR), jobName_, logfilesCountLimit_, [&] { try { requestUiRefresh(); } catch (...) {} }); //throw() } - catch (...) {} - - if (logfilesCountLimit_ > 0) - limitLogfileCount(beforeLast(logFile->getFilename(), FILE_NAME_SEPARATOR), jobName_, logfilesCountLimit_); //throw() try { - saveLogToFile(summary, errorLog, *logFile); //throw FileError + saveLogToFile(summary, errorLog, *logFile, OnUpdateLogfileStatusNoThrow(*this, logFile->getFilename())); //throw FileError //additionally notify errors by showing in log file name const Zstring oldLogfilepath = logFile->getFilename(); @@ -262,9 +266,10 @@ BatchStatusHandler::~BatchStatusHandler() } catch (FileError&) {} } + //----------------- write results into LastSyncs.log------------------------ try { - saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError + saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, getLastSyncsLogfilePath())); //throw FileError } catch (FileError&) {} diff --git a/FreeFileSync/Source/ui/batch_status_handler.h b/FreeFileSync/Source/ui/batch_status_handler.h index 2fc64148..214a367b 100644 --- a/FreeFileSync/Source/ui/batch_status_handler.h +++ b/FreeFileSync/Source/ui/batch_status_handler.h @@ -40,16 +40,16 @@ public: std::vector& onCompletionHistory); ~BatchStatusHandler(); - virtual void initNewPhase (int objectsTotal, std::int64_t dataTotal, Phase phaseID); - virtual void updateProcessedData(int objectsDelta, std::int64_t dataDelta); - virtual void reportInfo(const std::wstring& text); - virtual void forceUiRefresh(); + void initNewPhase (int objectsTotal, std::int64_t dataTotal, Phase phaseID) override; + void updateProcessedData(int objectsDelta, std::int64_t dataDelta) override; + void reportInfo (const std::wstring& text) override; + void forceUiRefresh () override; - virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); - virtual Response reportError(const std::wstring& errorMessage, size_t retryNumber); - virtual void reportFatalError(const std::wstring& errorMessage); + void reportWarning (const std::wstring& warningMessage, bool& warningActive) override; + Response reportError (const std::wstring& errorMessage, size_t retryNumber ) override; + void reportFatalError(const std::wstring& errorMessage ) override; - virtual void abortProcessNow(); //throw BatchAbortProcess + void abortProcessNow() override; //throw BatchAbortProcess private: void onProgressDialogTerminate(); diff --git a/FreeFileSync/Source/ui/check_version.cpp b/FreeFileSync/Source/ui/check_version.cpp index 91ac2bf0..ce80753f 100644 --- a/FreeFileSync/Source/ui/check_version.cpp +++ b/FreeFileSync/Source/ui/check_version.cpp @@ -28,6 +28,26 @@ using namespace zen; namespace { +std::wstring getUserAgentName() +{ + std::wstring agentName = std::wstring(L"FreeFileSync ") + zen::currentVersion; +#ifdef ZEN_WIN + agentName += L" Windows"; +#elif defined ZEN_LINUX + agentName += L" Linux"; +#elif defined ZEN_MAC + agentName += L" Mac"; +#else +#error wtf +#endif + const std::wstring localeName(wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage())); + if (!localeName.empty()) + agentName += L" " + localeName; + + return agentName; +} + + #ifdef ZEN_WIN class InternetConnectionError {}; @@ -38,7 +58,7 @@ public: { //::InternetAttemptConnect(0) -> not working as expected: succeeds even when there is no internet connection! - hInternet = ::InternetOpen(L"FreeFileSync", //_In_ LPCTSTR lpszAgent, + hInternet = ::InternetOpen(getUserAgentName().c_str(), //_In_ LPCTSTR lpszAgent, INTERNET_OPEN_TYPE_PRECONFIG, //_In_ DWORD dwAccessType, nullptr, //_In_ LPCTSTR lpszProxyName, nullptr, //_In_ LPCTSTR lpszProxyBypass, @@ -129,7 +149,7 @@ OutputIterator readBytesUrl(const wchar_t* url, OutputIterator result) //throw I enum GetVerResult { GET_VER_SUCCESS, - GET_VER_NO_CONNECTION, //no internet connection or just Sourceforge down? + GET_VER_NO_CONNECTION, //no internet connection or homepage down? GET_VER_PAGE_NOT_FOUND //version file seems to have moved! => trigger an update! }; @@ -140,11 +160,11 @@ GetVerResult getOnlineVersion(wxString& version) //empty string on error; std::vector output; try { - readBytesUrl(L"http://freefilesync.sourceforge.net/latest_version.txt", std::back_inserter(output)); //throw InternetConnectionError + readBytesUrl(L"http://www.freefilesync.org/latest_version.txt", std::back_inserter(output)); //throw InternetConnectionError } catch (const InternetConnectionError&) { - return canAccessUrl(L"http://sourceforge.net/") ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION; + return canAccessUrl(L"http://www.freefilesync.org/") ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION; } output.push_back('\0'); @@ -158,6 +178,8 @@ GetVerResult getOnlineVersion(wxString& version) //empty string on error; { wxHTTP webAccess; webAccess.SetHeader(L"content-type", L"text/html; charset=utf-8"); + webAccess.SetHeader(L"USER-AGENT", getUserAgentName()); + webAccess.SetTimeout(timeout); //default: 10 minutes(WTF are these wxWidgets people thinking???)... if (webAccess.Connect(server)) //will *not* fail for non-reachable url here! @@ -181,10 +203,10 @@ GetVerResult getOnlineVersion(wxString& version) //empty string on error; return false; }; - if (getStringFromUrl(L"freefilesync.sourceforge.net", L"/latest_version.txt", 5, &version)) + if (getStringFromUrl(L"freefilesync.org", L"/latest_version.txt", 5, &version)) return GET_VER_SUCCESS; - const bool canConnectToSf = getStringFromUrl(L"sourceforge.net", L"/", 1, nullptr); + const bool canConnectToSf = getStringFromUrl(L"freefilesync.org", L"/", 1, nullptr); return canConnectToSf ? GET_VER_PAGE_NOT_FOUND : GET_VER_NO_CONNECTION; #endif } @@ -230,7 +252,7 @@ void zen::checkForUpdateNow(wxWindow* parent) _("&Download"))) { case ConfirmationButton::DO_IT: - wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/get_latest.php"); + wxLaunchDefaultBrowser(L"http://freefilesync.org/get_latest.php"); break; case ConfirmationButton::CANCEL: break; @@ -245,7 +267,7 @@ void zen::checkForUpdateNow(wxWindow* parent) case GET_VER_NO_CONNECTION: showNotificationDialog(parent, DialogInfoType::ERROR2, PopupDialogCfg(). setTitle(("Check for Program Updates")). - setMainInstructions(_("Unable to connect to sourceforge.net."))); + setMainInstructions(_("Unable to connect to freefilesync.org."))); break; case GET_VER_PAGE_NOT_FOUND: @@ -255,7 +277,7 @@ void zen::checkForUpdateNow(wxWindow* parent) _("&Check"))) { case ConfirmationButton::DO_IT: - wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/"); + wxLaunchDefaultBrowser(L"http://www.freefilesync.org/"); break; case ConfirmationButton::CANCEL: break; @@ -286,7 +308,7 @@ void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, co _("&Download"))) { case ConfirmationButton::DO_IT: - wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/get_latest.php"); + wxLaunchDefaultBrowser(L"http://www.freefilesync.org/get_latest.php"); break; case ConfirmationButton::CANCEL: break; @@ -304,7 +326,7 @@ void zen::checkForUpdatePeriodically(wxWindow* parent, long& lastUpdateCheck, co _("&Check"))) { case ConfirmationButton::DO_IT: - wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/"); + wxLaunchDefaultBrowser(L"http://www.freefilesync.org/"); break; case ConfirmationButton::CANCEL: break; diff --git a/FreeFileSync/Source/ui/column_attr.h b/FreeFileSync/Source/ui/column_attr.h index 2bc2d8e4..42dc458d 100644 --- a/FreeFileSync/Source/ui/column_attr.h +++ b/FreeFileSync/Source/ui/column_attr.h @@ -38,13 +38,13 @@ inline std::vector getDefaultColumnAttributesLeft() { std::vector attr; - attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_BASE_DIRECTORY, 200, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_REL_FOLDER, -280, 1, true)); //stretch to full width and substract sum of fixed size widths! - attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, 200, 0, true)); - attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true)); - attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, 0, false)); + attr.emplace_back(COL_TYPE_FULL_PATH, 250, 0, false); + attr.emplace_back(COL_TYPE_BASE_DIRECTORY, 200, 0, false); + attr.emplace_back(COL_TYPE_REL_FOLDER, -280, 1, true); //stretch to full width and substract sum of fixed size widths! + attr.emplace_back(COL_TYPE_FILENAME, 200, 0, true); + attr.emplace_back(COL_TYPE_DATE, 112, 0, false); + attr.emplace_back(COL_TYPE_SIZE, 80, 0, true); + attr.emplace_back(COL_TYPE_EXTENSION, 60, 0, false); return attr; } @@ -52,13 +52,13 @@ inline std::vector getDefaultColumnAttributesRight() { std::vector attr; - attr.push_back(ColumnAttributeRim(COL_TYPE_FULL_PATH, 250, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_BASE_DIRECTORY, 200, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_REL_FOLDER , -280, 1, false)); //already shown on left side - attr.push_back(ColumnAttributeRim(COL_TYPE_FILENAME, 200, 0, true)); - attr.push_back(ColumnAttributeRim(COL_TYPE_DATE, 112, 0, false)); - attr.push_back(ColumnAttributeRim(COL_TYPE_SIZE, 80, 0, true)); - attr.push_back(ColumnAttributeRim(COL_TYPE_EXTENSION, 60, 0, false)); + attr.emplace_back(COL_TYPE_FULL_PATH, 250, 0, false); + attr.emplace_back(COL_TYPE_BASE_DIRECTORY, 200, 0, false); + attr.emplace_back(COL_TYPE_REL_FOLDER , -280, 1, false); //already shown on left side + attr.emplace_back(COL_TYPE_FILENAME, 200, 0, true); + attr.emplace_back(COL_TYPE_DATE, 112, 0, false); + attr.emplace_back(COL_TYPE_SIZE, 80, 0, true); + attr.emplace_back(COL_TYPE_EXTENSION, 60, 0, false); return attr; } @@ -101,9 +101,9 @@ inline std::vector getDefaultColumnAttributesNavi() { std::vector attr; - attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_DIRECTORY, -120, 1, true)); //stretch to full width and substract sum of fixed size widths - attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_ITEM_COUNT, 60, 0, true)); - attr.push_back(ColumnAttributeNavi(COL_TYPE_NAVI_BYTES, 60, 0, true)); //GTK needs a few pixels width more + attr.emplace_back(COL_TYPE_NAVI_DIRECTORY, -120, 1, true); //stretch to full width and substract sum of fixed size widths + attr.emplace_back(COL_TYPE_NAVI_ITEM_COUNT, 60, 0, true); + attr.emplace_back(COL_TYPE_NAVI_BYTES, 60, 0, true); //GTK needs a few pixels width more return attr; } } diff --git a/FreeFileSync/Source/ui/custom_grid.cpp b/FreeFileSync/Source/ui/custom_grid.cpp index 98f9b37f..fdd32ff2 100644 --- a/FreeFileSync/Source/ui/custom_grid.cpp +++ b/FreeFileSync/Source/ui/custom_grid.cpp @@ -193,7 +193,7 @@ protected: } private: - virtual size_t getRowCount() const + size_t getRowCount() const override { if (gridDataView_) { @@ -273,7 +273,7 @@ public: const IconInfo ii = getIconInfo(currentRow); if (!ii.iconPath.empty()) if (!iconMgr_->refIconBuffer().readyForRetrieval(ii.iconPath)) - newLoad.push_back(std::make_pair(i, ii.iconPath)); //insert least-important items on outer rim first + newLoad.emplace_back(i, ii.iconPath); //insert least-important items on outer rim first } } } @@ -298,7 +298,7 @@ private: } protected: - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override + void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override { if (enabled) { @@ -375,12 +375,12 @@ private: { GetRowType(DisplayType& result) : result_(result) {} - virtual void visit(const FilePair& fileObj) {} - virtual void visit(const SymlinkPair& linkObj) + void visit(const FilePair& fileObj) override {} + void visit(const SymlinkPair& linkObj) override { result_ = DISP_TYPE_SYMLINK; } - virtual void visit(const DirPair& dirObj) + void visit(const DirPair& dirObj) override { result_ = DISP_TYPE_FOLDER; } @@ -391,7 +391,7 @@ private: return output; } - virtual wxString getValue(size_t row, ColumnType colType) const + wxString getValue(size_t row, ColumnType colType) const override { if (const FileSystemObject* fsObj = getRawData(row)) { @@ -399,7 +399,7 @@ private: { GetTextValue(ColumnTypeRim colType, const FileSystemObject& fso) : colType_(colType), fsObj_(fso) {} - virtual void visit(const FilePair& fileObj) + void visit(const FilePair& fileObj) override { switch (colType_) { @@ -433,7 +433,7 @@ private: } } - virtual void visit(const SymlinkPair& linkObj) + void visit(const SymlinkPair& linkObj) override { switch (colType_) { @@ -463,7 +463,7 @@ private: } } - virtual void visit(const DirPair& dirObj) + void visit(const DirPair& dirObj) override { switch (colType_) { @@ -506,7 +506,7 @@ private: static const int GAP_SIZE = 2; - virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override + void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override { wxRect rectTmp = rect; @@ -599,7 +599,7 @@ private: } } - virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override + int getBestSize(wxDC& dc, size_t row, ColumnType colType) override { // Partitioning: // ________________________________ @@ -615,7 +615,7 @@ private: return bestSize; // + 1 pix for cell border line -> not used anymore! } - virtual wxString getColumnLabel(ColumnType colType) const + wxString getColumnLabel(ColumnType colType) const override { switch (static_cast(colType)) { @@ -637,7 +637,7 @@ private: return wxEmptyString; } - virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override + void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override { wxRect rectInside = drawColumnLabelBorder(dc, rect); drawColumnLabelBackground(dc, rectInside, highlighted); @@ -680,17 +680,17 @@ private: { GetIcon(IconInfo& ii) : ii_(ii) {} - virtual void visit(const FilePair& fileObj) + void visit(const FilePair& fileObj) override { ii_.iconPath = fileObj.getFullPath(); ii_.drawAsLink = fileObj.isFollowedSymlink() || hasLinkExtension(ii_.iconPath); } - virtual void visit(const SymlinkPair& linkObj) + void visit(const SymlinkPair& linkObj) override { ii_.iconPath = linkObj.getFullPath(); ii_.drawAsLink = true; } - virtual void visit(const DirPair& dirObj) + void visit(const DirPair& dirObj) override { ii_.drawAsFolder = true; //todo: if ("is followed symlink") ii_.drawAsLink = true; @@ -703,40 +703,40 @@ private: return out; } - virtual wxString getToolTip(size_t row, ColumnType colType) const override + wxString getToolTip(size_t row, ColumnType colType) const override { wxString toolTip; - const FileSystemObject* fsObj = getRawData(row); - if (fsObj && !fsObj->isEmpty()) - { - toolTip = toWx(getGridDataView() && getGridDataView()->getFolderPairCount() > 1 ? - fsObj->getFullPath() : - fsObj->getRelativePath()); - - struct AssembleTooltip : public FSObjectVisitor + if (const FileSystemObject* fsObj = getRawData(row)) + if (!fsObj->isEmpty()) { - AssembleTooltip(wxString& tipMsg) : tipMsg_(tipMsg) {} + toolTip = toWx(getGridDataView() && getGridDataView()->getFolderPairCount() > 1 ? + fsObj->getFullPath() : + fsObj->getRelativePath()); - virtual void visit(const FilePair& fileObj) + struct AssembleTooltip : public FSObjectVisitor { - tipMsg_ += L"\n" + - _("Size:") + L" " + zen::filesizeToShortString(fileObj.getFileSize()) + L"\n" + - _("Date:") + L" " + zen::utcToLocalTimeString(fileObj.getLastWriteTime()); - } + AssembleTooltip(wxString& tipMsg) : tipMsg_(tipMsg) {} - virtual void visit(const SymlinkPair& linkObj) - { - tipMsg_ += L"\n" + - _("Date:") + L" " + zen::utcToLocalTimeString(linkObj.getLastWriteTime()); - } + void visit(const FilePair& fileObj) override + { + tipMsg_ += L"\n" + + _("Size:") + L" " + zen::filesizeToShortString(fileObj.getFileSize()) + L"\n" + + _("Date:") + L" " + zen::utcToLocalTimeString(fileObj.getLastWriteTime()); + } - virtual void visit(const DirPair& dirObj) {} + void visit(const SymlinkPair& linkObj) override + { + tipMsg_ += L"\n" + + _("Date:") + L" " + zen::utcToLocalTimeString(linkObj.getLastWriteTime()); + } - wxString& tipMsg_; - } assembler(toolTip); - fsObj->accept(assembler); - } + void visit(const DirPair& dirObj) override {} + + wxString& tipMsg_; + } assembler(toolTip); + fsObj->accept(assembler); + } return toolTip; } @@ -753,15 +753,15 @@ class GridDataLeft : public GridDataRim public: GridDataLeft(const std::shared_ptr& gridDataView, Grid& grid) : GridDataRim(gridDataView, grid) {} - void setNavigationMarker(hash_set&& markedFilesAndLinks, - hash_set&& markedContainer) + void setNavigationMarker(std::unordered_set&& markedFilesAndLinks, + std::unordered_set&& markedContainer) { markedFilesAndLinks_.swap(markedFilesAndLinks); markedContainer_ .swap(markedContainer); } private: - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override + void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override { GridDataRim::renderRowBackgound(dc, rect, row, enabled, selected); @@ -809,8 +809,8 @@ private: } } - hash_set markedFilesAndLinks_; //mark files/symlinks directly within a container - hash_set markedContainer_; //mark full container including all child-objects + std::unordered_set markedFilesAndLinks_; //mark files/symlinks directly within a container + std::unordered_set markedContainer_; //mark full container including all child-objects //DO NOT DEREFERENCE!!!! NOT GUARANTEED TO BE VALID!!! }; @@ -941,7 +941,7 @@ public: void highlightSyncAction(bool value) { highlightSyncAction_ = value; } private: - virtual wxString getValue(size_t row, ColumnType colType) const override + wxString getValue(size_t row, ColumnType colType) const override { if (const FileSystemObject* fsObj = getRawData(row)) switch (static_cast(colType)) @@ -956,7 +956,7 @@ private: return wxString(); } - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override + void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override { if (enabled) { @@ -979,7 +979,7 @@ private: clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); } - virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override + void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override { auto drawHighlightBackground = [&](const FileSystemObject& fsObj, const wxColor& col) { @@ -1064,7 +1064,7 @@ private: } } - virtual wxString getColumnLabel(ColumnType colType) const override + wxString getColumnLabel(ColumnType colType) const override { switch (static_cast(colType)) { @@ -1078,9 +1078,9 @@ private: return wxEmptyString; } - virtual wxString getToolTip(ColumnType colType) const override { return getColumnLabel(colType); } + wxString getToolTip(ColumnType colType) const override { return getColumnLabel(colType); } - virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override + void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override { switch (static_cast(colType)) { @@ -1631,9 +1631,9 @@ void gridview::init(Grid& gridLeft, Grid& gridCenter, Grid& gridRight, const std gridCenter.SetSize(widthCategory + widthCheckbox + widthAction, -1); std::vector attribMiddle; - attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_CHECKBOX ), widthCheckbox, 0, true)); - attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_CMP_CATEGORY), widthCategory, 0, true)); - attribMiddle.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_SYNC_ACTION ), widthAction, 0, true)); + attribMiddle.emplace_back(static_cast(COL_TYPE_CHECKBOX ), widthCheckbox, 0, true); + attribMiddle.emplace_back(static_cast(COL_TYPE_CMP_CATEGORY), widthCategory, 0, true); + attribMiddle.emplace_back(static_cast(COL_TYPE_SYNC_ACTION ), widthAction, 0, true); gridCenter.setColumnConfig(attribMiddle); } @@ -1788,8 +1788,8 @@ void gridview::setScrollMaster(Grid& grid) void gridview::setNavigationMarker(Grid& gridLeft, - hash_set&& markedFilesAndLinks, - hash_set&& markedContainer) + std::unordered_set&& markedFilesAndLinks, + std::unordered_set&& markedContainer) { if (auto provLeft = dynamic_cast(gridLeft.getDataProvider())) provLeft->setNavigationMarker(std::move(markedFilesAndLinks), std::move(markedContainer)); diff --git a/FreeFileSync/Source/ui/custom_grid.h b/FreeFileSync/Source/ui/custom_grid.h index 9d7c8856..02c08a6e 100644 --- a/FreeFileSync/Source/ui/custom_grid.h +++ b/FreeFileSync/Source/ui/custom_grid.h @@ -32,8 +32,8 @@ void setScrollMaster(Grid& grid); //mark rows selected in navigation/compressed tree and navigate to leading object void setNavigationMarker(Grid& gridLeft, - hash_set&& markedFilesAndLinks,//mark files/symlinks directly within a container - hash_set&& markedContainer); //mark full container including child-objects + std::unordered_set&& markedFilesAndLinks,//mark files/symlinks directly within a container + std::unordered_set&& markedContainer); //mark full container including child-objects } wxBitmap getSyncOpImage(SyncOperation syncOp); @@ -50,7 +50,7 @@ extern const wxEventType EVENT_GRID_SYNC_DIRECTION; struct CheckRowsEvent : public wxCommandEvent { CheckRowsEvent(size_t rowFirst, size_t rowLast, bool setIncluded) : wxCommandEvent(EVENT_GRID_CHECK_ROWS), rowFirst_(rowFirst), rowLast_(rowLast), setIncluded_(setIncluded) { assert(rowFirst <= rowLast); } - virtual wxEvent* Clone() const { return new CheckRowsEvent(*this); } + wxEvent* Clone() const override { return new CheckRowsEvent(*this); } const size_t rowFirst_; //selected range: [rowFirst_, rowLast_) const size_t rowLast_; //range is empty when clearing selection @@ -61,7 +61,7 @@ struct CheckRowsEvent : public wxCommandEvent struct SyncDirectionEvent : public wxCommandEvent { SyncDirectionEvent(size_t rowFirst, size_t rowLast, SyncDirection direction) : wxCommandEvent(EVENT_GRID_SYNC_DIRECTION), rowFirst_(rowFirst), rowLast_(rowLast), direction_(direction) { assert(rowFirst <= rowLast); } - virtual wxEvent* Clone() const { return new SyncDirectionEvent(*this); } + wxEvent* Clone() const override { return new SyncDirectionEvent(*this); } const size_t rowFirst_; //see CheckRowsEvent const size_t rowLast_; // diff --git a/FreeFileSync/Source/ui/dir_name.cpp b/FreeFileSync/Source/ui/dir_name.cpp index ea8a9386..993e7259 100644 --- a/FreeFileSync/Source/ui/dir_name.cpp +++ b/FreeFileSync/Source/ui/dir_name.cpp @@ -6,7 +6,7 @@ #include "dir_name.h" #include -#include +#include #include #include #include diff --git a/FreeFileSync/Source/ui/folder_history_box.h b/FreeFileSync/Source/ui/folder_history_box.h index e310044f..35a03c69 100644 --- a/FreeFileSync/Source/ui/folder_history_box.h +++ b/FreeFileSync/Source/ui/folder_history_box.h @@ -42,7 +42,7 @@ public: zen::trim(nameTmp); //insert new folder or put it to the front if already existing - vector_remove_if(dirpaths_, [&](const Zstring& item) { return ::EqualFilename()(item, nameTmp); }); + zen::vector_remove_if(dirpaths_, [&](const Zstring& item) { return ::EqualFilename()(item, nameTmp); }); dirpaths_.insert(dirpaths_.begin(), nameTmp); diff --git a/FreeFileSync/Source/ui/grid_view.cpp b/FreeFileSync/Source/ui/grid_view.cpp index 704a5306..007c8040 100644 --- a/FreeFileSync/Source/ui/grid_view.cpp +++ b/FreeFileSync/Source/ui/grid_view.cpp @@ -19,7 +19,7 @@ void addNumbers(const FileSystemObject& fsObj, StatusResult& result) { GetValues(StatusResult& res) : result_(res) {} - virtual void visit(const FilePair& fileObj) + void visit(const FilePair& fileObj) override { if (!fileObj.isEmpty()) { @@ -33,7 +33,7 @@ void addNumbers(const FileSystemObject& fsObj, StatusResult& result) } } - virtual void visit(const SymlinkPair& linkObj) + void visit(const SymlinkPair& linkObj) override { if (!linkObj.isEmpty()) ++result_.filesOnLeftView; @@ -42,7 +42,7 @@ void addNumbers(const FileSystemObject& fsObj, StatusResult& result) ++result_.filesOnRightView; } - virtual void visit(const DirPair& dirObj) + void visit(const DirPair& dirObj) override { if (!dirObj.isEmpty()) ++result_.foldersOnLeftView; @@ -70,14 +70,14 @@ void GridView::updateView(Predicate pred) if (pred(*fsObj)) { //save row position for direct random access to FilePair or DirPair - this->rowPositions.insert(std::make_pair(ref.objId, viewRef.size())); //costs: 0.28 µs per call - MSVC based on std::set + this->rowPositions.emplace(ref.objId, viewRef.size()); //costs: 0.28 µs per call - MSVC based on std::set //"this->" required by two-pass lookup as enforced by GCC 4.7 //save row position to identify first child *on sorted subview* of DirPair or BaseDirPair in case latter are filtered out const HierarchyObject* parent = &fsObj->parent(); for (;;) //map all yet unassociated parents to this row { - const auto rv = this->rowPositionsFirstChild.insert(std::make_pair(parent, viewRef.size())); + const auto rv = this->rowPositionsFirstChild.emplace(parent, viewRef.size()); if (!rv.second) break; @@ -320,12 +320,12 @@ private: void recurse(HierarchyObject& hierObj) { for (FilePair& fileObj : hierObj.refSubFiles()) - sortedRef_.push_back(RefIndex(index_, fileObj.getId())); + sortedRef_.emplace_back(index_, fileObj.getId()); for (SymlinkPair& linkObj : hierObj.refSubLinks()) - sortedRef_.push_back(RefIndex(index_, linkObj.getId())); + sortedRef_.emplace_back(index_, linkObj.getId()); for (DirPair& dirObj : hierObj.refSubDirs()) { - sortedRef_.push_back(RefIndex(index_, dirObj.getId())); + sortedRef_.emplace_back(index_, dirObj.getId()); recurse(dirObj); //add recursion here to list sub-objects directly below parent! } } diff --git a/FreeFileSync/Source/ui/grid_view.h b/FreeFileSync/Source/ui/grid_view.h index 9c0dd32b..9250e674 100644 --- a/FreeFileSync/Source/ui/grid_view.h +++ b/FreeFileSync/Source/ui/grid_view.h @@ -8,6 +8,7 @@ #define GRIDVIEW_H_INCLUDED #include +#include #include "column_attr.h" #include "../file_hierarchy.h" @@ -136,8 +137,8 @@ private: template void updateView(Predicate pred); - zen::hash_map rowPositions; //find row positions on sortedRef directly - zen::hash_map rowPositionsFirstChild; //find first child on sortedRef of a hierarchy object + std::unordered_map rowPositions; //find row positions on sortedRef directly + std::unordered_map rowPositionsFirstChild; //find first child on sortedRef of a hierarchy object //void* instead of HierarchyObject*: these are weak pointers and should *never be dereferenced*! std::vector viewRef; //partial view on sortedRef diff --git a/FreeFileSync/Source/ui/gui_generated.cpp b/FreeFileSync/Source/ui/gui_generated.cpp index d3fbc6e4..2bdd6aca 100644 --- a/FreeFileSync/Source/ui/gui_generated.cpp +++ b/FreeFileSync/Source/ui/gui_generated.cpp @@ -1325,12 +1325,18 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w wxBoxSizer* bSizer280; bSizer280 = new wxBoxSizer( wxHORIZONTAL ); - m_buttonClear = new wxButton( m_panelFilterSettingsHolder, wxID_DEFAULT, _("C&lear"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer280->Add( m_buttonClear, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - m_staticText44 = new wxStaticText( m_panelFilterSettingsHolder, wxID_ANY, _("Select filter rules to exclude certain files from synchronization. Enter file paths relative to their corresponding folder pair."), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - m_staticText44->Wrap( 600 ); - bSizer280->Add( m_staticText44, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 10 ); + m_staticText44->Wrap( 590 ); + bSizer280->Add( m_staticText44, 0, wxALIGN_CENTER_VERTICAL|wxALL, 10 ); + + + bSizer280->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_staticline46 = new wxStaticLine( m_panelFilterSettingsHolder, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL ); + bSizer280->Add( m_staticline46, 0, wxEXPAND, 5 ); + + m_buttonClear = new wxButton( m_panelFilterSettingsHolder, wxID_DEFAULT, _("C&lear"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer280->Add( m_buttonClear, 0, wxALL|wxALIGN_CENTER_VERTICAL, 10 ); bSizer278->Add( bSizer280, 0, wxEXPAND, 5 ); @@ -1339,7 +1345,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_panelFilterSettingsHolder->SetSizer( bSizer278 ); m_panelFilterSettingsHolder->Layout(); bSizer278->Fit( m_panelFilterSettingsHolder ); - m_notebook->AddPage( m_panelFilterSettingsHolder, _("dummy"), true ); + m_notebook->AddPage( m_panelFilterSettingsHolder, _("dummy"), false ); m_panelSyncSettingsHolder = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); m_panelSyncSettingsHolder->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); @@ -1401,7 +1407,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_checkBoxDetectMove = new wxCheckBox( m_panelSyncSettings, wxID_ANY, _("Detect moved files"), wxDefaultPosition, wxDefaultSize, 0 ); m_checkBoxDetectMove->SetValue(true); - m_checkBoxDetectMove->SetToolTip( _("- Requires and creates database files\n- Detection active after initial sync\n- Not supported by all file systems") ); + m_checkBoxDetectMove->SetToolTip( _("- Detection active after initial sync\n- Requires and creates database files\n- Not supported by all file systems") ); bSizer235->Add( m_checkBoxDetectMove, 0, wxALIGN_CENTER_VERTICAL|wxEXPAND|wxALL, 5 ); @@ -1701,7 +1707,7 @@ ConfigDlgGenerated::ConfigDlgGenerated( wxWindow* parent, wxWindowID id, const w m_panelSyncSettingsHolder->SetSizer( bSizer276 ); m_panelSyncSettingsHolder->Layout(); bSizer276->Fit( m_panelSyncSettingsHolder ); - m_notebook->AddPage( m_panelSyncSettingsHolder, _("dummy"), false ); + m_notebook->AddPage( m_panelSyncSettingsHolder, _("dummy"), true ); bSizer7->Add( m_notebook, 1, wxEXPAND|wxTOP|wxRIGHT|wxLEFT, 5 ); @@ -3319,7 +3325,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS m_buttonDonate = new wxButton( m_panel39, wxID_ANY, _("Donate with PayPal"), wxDefaultPosition, wxDefaultSize, 0 ); m_buttonDonate->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 90, false, wxEmptyString ) ); - m_buttonDonate->SetToolTip( _("http://freefilesync.sourceforge.net/donate.php") ); + m_buttonDonate->SetToolTip( _("http://www.freefilesync.org/donate.php") ); bSizer178->Add( m_buttonDonate, 0, wxBOTTOM|wxRIGHT|wxLEFT|wxALIGN_CENTER_HORIZONTAL, 5 ); @@ -3359,7 +3365,7 @@ AboutDlgGenerated::AboutDlgGenerated( wxWindow* parent, wxWindowID id, const wxS bSizer166->Add( m_bitmap9, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 5 ); - m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("freefilesync.sf.net"), wxT("http://freefilesync.sf.net/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); + m_hyperlink1 = new wxHyperlinkCtrl( m_panel41, wxID_ANY, _("freefilesync.org"), wxT("http://www.freefilesync.org/"), wxDefaultPosition, wxDefaultSize, wxHL_DEFAULT_STYLE ); m_hyperlink1->SetFont( wxFont( wxNORMAL_FONT->GetPointSize(), 70, 90, 92, true, wxEmptyString ) ); m_hyperlink1->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); diff --git a/FreeFileSync/Source/ui/gui_generated.h b/FreeFileSync/Source/ui/gui_generated.h index 4b238ba9..dc3dd4c7 100644 --- a/FreeFileSync/Source/ui/gui_generated.h +++ b/FreeFileSync/Source/ui/gui_generated.h @@ -300,8 +300,9 @@ protected: wxSpinCtrl* m_spinCtrlMaxSize; wxChoice* m_choiceUnitMaxSize; wxStaticLine* m_staticline62; - wxButton* m_buttonClear; wxStaticText* m_staticText44; + wxStaticLine* m_staticline46; + wxButton* m_buttonClear; wxPanel* m_panelSyncSettingsHolder; wxBoxSizer* bSizerLocalSyncSettings; wxCheckBox* m_checkBoxUseLocalSyncOptions; diff --git a/FreeFileSync/Source/ui/gui_status_handler.cpp b/FreeFileSync/Source/ui/gui_status_handler.cpp index de75eb3f..f9f63fa6 100644 --- a/FreeFileSync/Source/ui/gui_status_handler.cpp +++ b/FreeFileSync/Source/ui/gui_status_handler.cpp @@ -271,7 +271,7 @@ SyncStatusHandler::~SyncStatusHandler() try { //use EXEC_TYPE_ASYNC until there is reason not to: https://sourceforge.net/p/freefilesync/discussion/help/thread/828dca52 - tryReportingError([&] { shellExecute2(expandMacros(finalCommand), EXEC_TYPE_ASYNC); }, //throw FileError, throw X? + tryReportingError([&] { shellExecute(expandMacros(finalCommand), EXEC_TYPE_ASYNC); }, //throw FileError, throw X? *this); } catch (...) {} @@ -318,9 +318,10 @@ SyncStatusHandler::~SyncStatusHandler() (wxGetUTCTimeMillis().GetValue() - startTime_) / 1000 }; + //----------------- write results into LastSyncs.log------------------------ try { - saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_); //throw FileError + saveToLastSyncsLog(summary, errorLog, lastSyncsLogFileSizeMax_, OnUpdateLogfileStatusNoThrow(*this, getLastSyncsLogfilePath())); //throw FileError } catch (FileError&) {} diff --git a/FreeFileSync/Source/ui/gui_status_handler.h b/FreeFileSync/Source/ui/gui_status_handler.h index 47c6e431..7f58afb8 100644 --- a/FreeFileSync/Source/ui/gui_status_handler.h +++ b/FreeFileSync/Source/ui/gui_status_handler.h @@ -27,14 +27,14 @@ public: CompareStatusHandler(MainDialog& dlg); ~CompareStatusHandler(); - virtual void initNewPhase(int objectsTotal, std::int64_t dataTotal, Phase phaseID); - virtual void forceUiRefresh(); + void initNewPhase(int objectsTotal, std::int64_t dataTotal, Phase phaseID) override; + void forceUiRefresh() override; - virtual Response reportError(const std::wstring& text, size_t retryNumber); - virtual void reportFatalError(const std::wstring& errorMessage); - virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); + Response reportError (const std::wstring& text, size_t retryNumber) override; + void reportFatalError(const std::wstring& errorMessage) override; + void reportWarning (const std::wstring& warningMessage, bool& warningActive) override; - virtual void abortProcessNow(); //throw GuiAbortProcess + void abortProcessNow() override; //throw GuiAbortProcess private: void OnKeyPressed(wxKeyEvent& event); @@ -59,16 +59,16 @@ public: std::vector& onCompletionHistory); ~SyncStatusHandler(); - virtual void initNewPhase (int objectsTotal, std::int64_t dataTotal, Phase phaseID); - virtual void updateProcessedData(int objectsDelta, std::int64_t dataDelta); - virtual void reportInfo(const std::wstring& text); - virtual void forceUiRefresh(); + void initNewPhase (int objectsTotal, std::int64_t dataTotal, Phase phaseID) override; + void updateProcessedData(int objectsDelta, std::int64_t dataDelta ) override; + void reportInfo (const std::wstring& text ) override; + void forceUiRefresh () override; - virtual Response reportError(const std::wstring& text, size_t retryNumber); - virtual void reportFatalError(const std::wstring& errorMessage); - virtual void reportWarning(const std::wstring& warningMessage, bool& warningActive); + Response reportError (const std::wstring& text, size_t retryNumber ) override; + void reportFatalError(const std::wstring& errorMessage ) override; + void reportWarning (const std::wstring& warningMessage, bool& warningActive) override; - virtual void abortProcessNow(); //throw GuiAbortProcess + void abortProcessNow() override; //throw GuiAbortProcess private: void onProgressDialogTerminate(); diff --git a/FreeFileSync/Source/ui/main_dlg.cpp b/FreeFileSync/Source/ui/main_dlg.cpp index 69aa6fb6..918da099 100644 --- a/FreeFileSync/Source/ui/main_dlg.cpp +++ b/FreeFileSync/Source/ui/main_dlg.cpp @@ -6,7 +6,7 @@ #include "main_dlg.h" #include -#include +#include #include #include #include @@ -94,7 +94,7 @@ public: DirectoryName(dropWindow1, dirSelectButton, dirpath, &staticText, &dropGrid.getMainWin()), mainDlg_(mainDlg) {} - virtual bool acceptDrop(const std::vector& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd) override + bool acceptDrop(const std::vector& droppedFiles, const wxPoint& clientPos, const wxWindow& wnd) override { if (std::any_of(droppedFiles.begin(), droppedFiles.end(), [](const wxString& filepath) { @@ -155,13 +155,13 @@ public: mainDlg(mainDialog) {} private: - virtual MainConfiguration getMainConfig() const override { return mainDlg.getConfig().mainCfg; } - virtual wxWindow* getParentWindow() override { return &mainDlg; } - virtual std::unique_ptr& getFilterCfgOnClipboardRef() override { return mainDlg.filterCfgOnClipboard; } + MainConfiguration getMainConfig() const override { return mainDlg.getConfig().mainCfg; } + wxWindow* getParentWindow() override { return &mainDlg; } + std::unique_ptr& getFilterCfgOnClipboardRef() override { return mainDlg.filterCfgOnClipboard; } - virtual void onAltCompCfgChange () override { mainDlg.applyCompareConfig(); } - virtual void onAltSyncCfgChange () override { mainDlg.applySyncConfig(); } - virtual void onLocalFilterCfgChange() override { mainDlg.applyFilterConfig(); } //re-apply filter + void onAltCompCfgChange () override { mainDlg.applyCompareConfig(); } + void onAltSyncCfgChange () override { mainDlg.applySyncConfig(); } + void onLocalFilterCfgChange() override { mainDlg.applyFilterConfig(); } //re-apply filter MainDialog& mainDlg; }; @@ -260,7 +260,7 @@ public: MouseMoveWindow(mainDlg, false), //don't include main dialog itself, thereby prevent various mouse capture lost issues mainDlg_(mainDlg) {} - virtual bool allowMove(const wxMouseEvent& event) override + bool allowMove(const wxMouseEvent& event) override { if (wxPanel* panel = dynamic_cast(event.GetEventObject())) { @@ -537,11 +537,11 @@ MainDialog::MainDialog(const Zstring& globalConfigFile, auiMgr.AddPane(m_panelViewFilter, wxAuiPaneInfo().Name(L"PanelView").Layer(2).Bottom().Row(1).Caption(_("View Settings")).CaptionVisible(false).PaneBorder(false).Gripper().MinSize(m_bpButtonViewTypeSyncAction->GetSize().GetWidth(), m_panelViewFilter->GetSize().GetHeight())); - auiMgr.AddPane(m_gridNavi, - wxAuiPaneInfo().Name(L"PanelOverview").Layer(3).Left().Position(1).Caption(_("Overview")).MinSize(300, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below - auiMgr.AddPane(m_panelConfig, - wxAuiPaneInfo().Name(L"PanelConfig").Layer(3).Left().Position(2).Caption(_("Configuration")).MinSize(m_listBoxHistory->GetSize().GetWidth(), m_panelConfig->GetSize().GetHeight())); + wxAuiPaneInfo().Name(L"PanelConfig").Layer(3).Left().Position(1).Caption(_("Configuration")).MinSize(m_listBoxHistory->GetSize().GetWidth(), m_panelConfig->GetSize().GetHeight())); + + auiMgr.AddPane(m_gridNavi, + wxAuiPaneInfo().Name(L"PanelOverview").Layer(3).Left().Position(2).Caption(_("Overview")).MinSize(300, m_gridNavi->GetSize().GetHeight())); //MinSize(): just default size, see comment below //set comparison button label tentatively for m_panelTopButtons to receive final height: updateTopButton(*m_buttonCompare, getResourceImage(L"compare"), L"Dummy", false); @@ -672,7 +672,7 @@ MainDialog::MainDialog(const Zstring& globalConfigFile, newItem->SetBitmap(getResourceImage(entry.languageFlag)); //map menu item IDs with language IDs: evaluated when processing event handler - languageMenuItemMap.insert(std::make_pair(newItem->GetId(), entry.languageID)); + languageMenuItemMap.emplace(newItem->GetId(), entry.languageID); //connect event this->Connect(newItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainDialog::OnMenuLanguageSwitch)); @@ -917,7 +917,7 @@ void MainDialog::setGlobalCfgOnInit(const xmlAccess::XmlGlobalSettings& globalSe CaptionNameMapping captionNameMap; const wxAuiPaneInfoArray& paneArray = auiMgr.GetAllPanes(); for (size_t i = 0; i < paneArray.size(); ++i) - captionNameMap.push_back(std::make_pair(paneArray[i].caption, paneArray[i].name)); + captionNameMap.emplace_back(paneArray[i].caption, paneArray[i].name); auiMgr.LoadPerspective(globalSettings.gui.guiPerspectiveLast); @@ -961,7 +961,7 @@ xmlAccess::XmlGlobalSettings MainDialog::getGlobalCfgBeforeExit() std::map historyDetail; //(cfg-file/last use index) for (unsigned int i = 0; i < m_listBoxHistory->GetCount(); ++i) if (auto clientString = dynamic_cast(m_listBoxHistory->GetClientObject(i))) - historyDetail.insert(std::make_pair(clientString->lastUseIndex_, clientString->cfgFile_)); + historyDetail.emplace(clientString->lastUseIndex_, clientString->cfgFile_); else assert(false); @@ -1162,7 +1162,7 @@ public: mainDlg.enableAllElements(); } - virtual Response reportError(const std::wstring& msg) override + Response reportError(const std::wstring& msg) override { if (ignoreErrors) return DeleteFilesHandler::IGNORE_ERROR; @@ -1189,7 +1189,7 @@ public: return DeleteFilesHandler::IGNORE_ERROR; //dummy return value } - virtual void reportWarning(const std::wstring& msg, bool& warningActive) override + void reportWarning(const std::wstring& msg, bool& warningActive) override { if (!warningActive || ignoreErrors) return; @@ -1208,7 +1208,7 @@ public: } } - virtual void reportStatus (const std::wstring& msg) override + void reportStatus (const std::wstring& msg) override { statusMsg = msg; requestUiRefresh(); @@ -1363,11 +1363,11 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: try { #ifdef ZEN_WIN - shellExecute2(L"\"" + fallbackDir + L"\"", EXEC_TYPE_ASYNC); //throw FileError + shellExecute(L"\"" + fallbackDir + L"\"", EXEC_TYPE_ASYNC); //throw FileError #elif defined ZEN_LINUX - shellExecute2("xdg-open \"" + fallbackDir + "\"", EXEC_TYPE_ASYNC); // + shellExecute("xdg-open \"" + fallbackDir + "\"", EXEC_TYPE_ASYNC); // #elif defined ZEN_MAC - shellExecute2("open \"" + fallbackDir + "\"", EXEC_TYPE_ASYNC); // + shellExecute("open \"" + fallbackDir + "\"", EXEC_TYPE_ASYNC); // #endif } catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } @@ -1422,7 +1422,7 @@ void MainDialog::openExternalApplication(const wxString& commandline, const std: try { //caveat: spawning too many threads asynchronously can easily kill a user's desktop session on Ubuntu! - shellExecute2(cmdExp, selectionTmp.size() > massInvokeThreshold ? EXEC_TYPE_SYNC : EXEC_TYPE_ASYNC); //throw FileError + shellExecute(cmdExp, selectionTmp.size() > massInvokeThreshold ? EXEC_TYPE_SYNC : EXEC_TYPE_ASYNC); //throw FileError } catch (const FileError& e) { showNotificationDialog(this, DialogInfoType::ERROR2, PopupDialogCfg().setDetailInstructions(e.toString())); } } @@ -1986,8 +1986,8 @@ void MainDialog::onNaviSelection(GridRangeSelectEvent& event) } //get selection on navigation tree and set corresponding markers on main grid - hash_set markedFilesAndLinks; //mark files/symlinks directly - hash_set markedContainer; //mark full container including child-objects + std::unordered_set markedFilesAndLinks; //mark files/symlinks directly + std::unordered_set markedContainer; //mark full container including child-objects const std::vector& selection = m_gridNavi->getSelectedRows(); std::for_each(selection.begin(), selection.end(), @@ -2054,13 +2054,13 @@ void MainDialog::onNaviGridContext(GridClickEvent& event) //by short name Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getPairShortName(); if (isDir) - labelShort += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); + labelShort += FILE_NAME_SEPARATOR + Zstring(Zstr("*")); submenu.addItem(utfCvrtTo(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); }); //by relative path Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getPairRelativePath(); if (isDir) - labelRel += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); + labelRel += FILE_NAME_SEPARATOR + Zstring(Zstr("*")); submenu.addItem(utfCvrtTo(labelRel), [this, &selection, include] { filterItems(selection, include); }); menu.addSubmenu(label, submenu, &getResourceImage(iconName)); @@ -2182,13 +2182,13 @@ void MainDialog::onMainGridContextRim(bool leftSide) //by short name Zstring labelShort = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + selection[0]->getPairShortName(); if (isDir) - labelShort += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); + labelShort += FILE_NAME_SEPARATOR + Zstring(Zstr("*")); submenu.addItem(utfCvrtTo(labelShort), [this, &selection, include] { filterShortname(*selection[0], include); }); //by relative path Zstring labelRel = FILE_NAME_SEPARATOR + selection[0]->getPairRelativePath(); if (isDir) - labelRel += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); + labelRel += FILE_NAME_SEPARATOR + Zstring(Zstr("*")); submenu.addItem(utfCvrtTo(labelRel), [this, &selection, include] { filterItems(selection, include); }); menu.addSubmenu(label, submenu, &getResourceImage(iconName)); @@ -2306,7 +2306,7 @@ void MainDialog::filterShortname(const FileSystemObject& fsObj, bool include) Zstring phrase = Zstring(Zstr("*")) + FILE_NAME_SEPARATOR + fsObj.getPairShortName(); const bool isDir = dynamic_cast(&fsObj) != nullptr; if (isDir) - phrase += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); //include filter: * required; exclude filter: * optional, but let's still apply it! + phrase += FILE_NAME_SEPARATOR + Zstring(Zstr("*")); //include filter: * required; exclude filter: * optional, but let's still apply it! filterPhrase(phrase, include, true); } @@ -2329,7 +2329,7 @@ void MainDialog::filterItems(const std::vector& selection, bo const bool isDir = dynamic_cast(fsObj) != nullptr; if (isDir) - phrase += Zstring(FILE_NAME_SEPARATOR) + Zstr("*"); //include filter: * required; exclude filter: * optional, but let's still apply it! + phrase += FILE_NAME_SEPARATOR + Zstring(Zstr("*")); //include filter: * required; exclude filter: * optional, but let's still apply it! } filterPhrase(phrase, include, true); } @@ -2486,8 +2486,8 @@ void MainDialog::OnCompSettingsContext(wxMouseEvent& event) auto currentVar = getConfig().mainCfg.cmpConfig.compareVar; - menu.addRadio(_("File time and size"), [&] { setVariant(CMP_BY_TIME_SIZE); }, currentVar == CMP_BY_TIME_SIZE); - menu.addRadio(_("File content" ), [&] { setVariant(CMP_BY_CONTENT); }, currentVar == CMP_BY_CONTENT); + menu.addRadio(getVariantName(CMP_BY_TIME_SIZE), [&] { setVariant(CMP_BY_TIME_SIZE); }, currentVar == CMP_BY_TIME_SIZE); + menu.addRadio(getVariantName(CMP_BY_CONTENT ), [&] { setVariant(CMP_BY_CONTENT); }, currentVar == CMP_BY_CONTENT); menu.popup(*this); } @@ -2505,10 +2505,10 @@ void MainDialog::OnSyncSettingsContext(wxMouseEvent& event) const auto currentVar = getConfig().mainCfg.syncCfg.directionCfg.var; - menu.addRadio(L"<- " + _("Two way") + L" ->" , [&] { setVariant(DirectionConfig::TWOWAY); }, currentVar == DirectionConfig::TWOWAY); - menu.addRadio( _("Mirror") + L" ->>", [&] { setVariant(DirectionConfig::MIRROR); }, currentVar == DirectionConfig::MIRROR); - menu.addRadio( _("Update") + L" ->" , [&] { setVariant(DirectionConfig::UPDATE); }, currentVar == DirectionConfig::UPDATE); - menu.addRadio( _("Custom") , [&] { setVariant(DirectionConfig::CUSTOM); }, currentVar == DirectionConfig::CUSTOM); + menu.addRadio(getVariantName(DirectionConfig::TWOWAY), [&] { setVariant(DirectionConfig::TWOWAY); }, currentVar == DirectionConfig::TWOWAY); + menu.addRadio(getVariantName(DirectionConfig::MIRROR), [&] { setVariant(DirectionConfig::MIRROR); }, currentVar == DirectionConfig::MIRROR); + menu.addRadio(getVariantName(DirectionConfig::UPDATE), [&] { setVariant(DirectionConfig::UPDATE); }, currentVar == DirectionConfig::UPDATE); + menu.addRadio(getVariantName(DirectionConfig::CUSTOM), [&] { setVariant(DirectionConfig::CUSTOM); }, currentVar == DirectionConfig::CUSTOM); menu.popup(*this); } @@ -2632,7 +2632,7 @@ void MainDialog::removeObsoleteCfgHistoryItems(const std::vector& filep std::list> fileEx; for (const Zstring& filepath : filepaths) - fileEx.push_back(zen::async2([=] { return fileExists(filepath); })); + fileEx.push_back(zen::async([=] { return fileExists(filepath); })); //potentially slow network access => limit maximum wait time! wait_for_all_timed(fileEx.begin(), fileEx.end(), boost::posix_time::milliseconds(1000)); @@ -3727,7 +3727,7 @@ void MainDialog::OnStartSync(wxCommandEvent& event) if (it->isExisting()) dirpathsExisting.insert(it->getBaseDirPf()); } - dirLocks = make_unique(dirpathsExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); + dirLocks = zen::make_unique(dirpathsExisting, globalCfg.optDialogs.warningDirectoryLockFailed, statusHandler); } //START SYNCHRONIZATION @@ -4433,18 +4433,16 @@ void MainDialog::OnMenuExportFileList(wxCommandEvent& event) if (provLeft && provMiddle && provRight) { - std::for_each(colAttrLeft.begin(), colAttrLeft.end(), - [&](const Grid::ColumnAttribute& ca) + for (const Grid::ColumnAttribute& ca : colAttrLeft) { header += fmtValue(provLeft->getColumnLabel(ca.type_)); header += CSV_SEP; - }); - std::for_each(colAttrMiddle.begin(), colAttrMiddle.end(), - [&](const Grid::ColumnAttribute& ca) + } + for (const Grid::ColumnAttribute& ca : colAttrMiddle) { header += fmtValue(provMiddle->getColumnLabel(ca.type_)); header += CSV_SEP; - }); + } if (!colAttrRight.empty()) { std::for_each(colAttrRight.begin(), colAttrRight.end() - 1, diff --git a/FreeFileSync/Source/ui/main_dlg.h b/FreeFileSync/Source/ui/main_dlg.h index a9becd95..689bd528 100644 --- a/FreeFileSync/Source/ui/main_dlg.h +++ b/FreeFileSync/Source/ui/main_dlg.h @@ -143,10 +143,10 @@ private: void OnContextSetLayout(wxMouseEvent& event); void onLocalKeyEvent (wxKeyEvent& event); - virtual void OnCompSettingsContext(wxMouseEvent& event) override; - virtual void OnSyncSettingsContext(wxMouseEvent& event) override; - virtual void OnGlobalFilterContext(wxMouseEvent& event) override; - virtual void OnViewButtonRightClick(wxMouseEvent& event) override; + void OnCompSettingsContext (wxMouseEvent& event) override; + void OnSyncSettingsContext (wxMouseEvent& event) override; + void OnGlobalFilterContext (wxMouseEvent& event) override; + void OnViewButtonRightClick(wxMouseEvent& event) override; void applyCompareConfig(bool setDefaultViewType = false); @@ -182,20 +182,20 @@ private: void onGridLabelContextR(zen::GridClickEvent& event); void onGridLabelContext(zen::Grid& grid, zen::ColumnTypeRim type, const std::vector& defaultColumnAttributes); - virtual void OnToggleViewType (wxCommandEvent& event) override; - virtual void OnToggleViewButton(wxCommandEvent& event) override; + void OnToggleViewType (wxCommandEvent& event) override; + void OnToggleViewButton(wxCommandEvent& event) override; - virtual void OnConfigNew (wxCommandEvent& event) override; - virtual void OnConfigSave (wxCommandEvent& event) override; - virtual void OnConfigSaveAs (wxCommandEvent& event) override; - virtual void OnSaveAsBatchJob (wxCommandEvent& event) override; - virtual void OnConfigLoad (wxCommandEvent& event) override; - virtual void OnLoadFromHistory(wxCommandEvent& event) override; - virtual void OnLoadFromHistoryDoubleClick(wxCommandEvent& event); + void OnConfigNew (wxCommandEvent& event) override; + void OnConfigSave (wxCommandEvent& event) override; + void OnConfigSaveAs (wxCommandEvent& event) override; + void OnSaveAsBatchJob (wxCommandEvent& event) override; + void OnConfigLoad (wxCommandEvent& event) override; + void OnLoadFromHistory(wxCommandEvent& event) override; + void OnLoadFromHistoryDoubleClick(wxCommandEvent& event) override; void deleteSelectedCfgHistoryItems(); - virtual void OnCfgHistoryRightClick(wxMouseEvent& event) override; + void OnCfgHistoryRightClick(wxMouseEvent& event) override; void OnCfgHistoryKeyEvent (wxKeyEvent& event) override; void OnRegularUpdateCheck (wxIdleEvent& event); void OnLayoutWindowAsync (wxIdleEvent& event); @@ -204,10 +204,10 @@ private: void OnResizeTopButtonPanel (wxEvent& event); void OnResizeConfigPanel (wxEvent& event); void OnResizeViewPanel (wxEvent& event); - virtual void OnCompare (wxCommandEvent& event) override; - virtual void OnStartSync (wxCommandEvent& event) override; - virtual void OnSwapSides (wxCommandEvent& event) override; - virtual void OnClose (wxCloseEvent& event) override; + void OnCompare (wxCommandEvent& event) override; + void OnStartSync (wxCommandEvent& event) override; + void OnSwapSides (wxCommandEvent& event) override; + void OnClose (wxCloseEvent& event) override; void OnCmpSettings (wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::COMPARISON); } void OnConfigureFilter(wxCommandEvent& event) override { showConfigDialog(zen::SyncConfigPanel::FILTER ); } @@ -220,9 +220,9 @@ private: void filterItems(const std::vector& selection, bool include); void filterPhrase(const Zstring& phrase, bool include, bool addNewLine); - virtual void OnAddFolderPair (wxCommandEvent& event) override; + void OnAddFolderPair (wxCommandEvent& event) override; void OnRemoveFolderPair (wxCommandEvent& event); - virtual void OnRemoveTopFolderPair(wxCommandEvent& event) override; + void OnRemoveTopFolderPair(wxCommandEvent& event) override; void applyFilterConfig(); void applySyncConfig(); @@ -233,20 +233,20 @@ private: void resetLayout(); - virtual void OnSearchGridEnter(wxCommandEvent& event) override; - virtual void OnHideSearchPanel(wxCommandEvent& event) override; + void OnSearchGridEnter(wxCommandEvent& event) override; + void OnHideSearchPanel(wxCommandEvent& event) override; void OnSearchPanelKeyPressed(wxKeyEvent& event); //menu events - virtual void OnMenuOptions (wxCommandEvent& event) override; - virtual void OnMenuExportFileList(wxCommandEvent& event) override; - virtual void OnMenuResetLayout (wxCommandEvent& event) override { resetLayout(); } - virtual void OnMenuFindItem (wxCommandEvent& event) override; - virtual void OnMenuCheckVersion (wxCommandEvent& event) override; - virtual void OnMenuCheckVersionAutomatically(wxCommandEvent& event) override; - virtual void OnMenuAbout (wxCommandEvent& event) override; - virtual void OnShowHelp (wxCommandEvent& event) override; - virtual void OnMenuQuit (wxCommandEvent& event) override { Close(); } + void OnMenuOptions (wxCommandEvent& event) override; + void OnMenuExportFileList(wxCommandEvent& event) override; + void OnMenuResetLayout (wxCommandEvent& event) override { resetLayout(); } + void OnMenuFindItem (wxCommandEvent& event) override; + void OnMenuCheckVersion (wxCommandEvent& event) override; + void OnMenuCheckVersionAutomatically(wxCommandEvent& event) override; + void OnMenuAbout (wxCommandEvent& event) override; + void OnShowHelp (wxCommandEvent& event) override; + void OnMenuQuit (wxCommandEvent& event) override { Close(); } void OnMenuLanguageSwitch(wxCommandEvent& event); diff --git a/FreeFileSync/Source/ui/on_completion_box.cpp b/FreeFileSync/Source/ui/on_completion_box.cpp index 51b4dda1..274d0a30 100644 --- a/FreeFileSync/Source/ui/on_completion_box.cpp +++ b/FreeFileSync/Source/ui/on_completion_box.cpp @@ -28,7 +28,7 @@ std::vector> getDefaultCommands() //(gui name/c { std::vector> output; - auto addEntry = [&](const std::wstring& name, const Zstring& value) { output.push_back(std::make_pair(name, value)); }; + auto addEntry = [&](const std::wstring& name, const Zstring& value) { output.emplace_back(name, value); }; #ifdef ZEN_WIN if (zen::vistaOrLater()) @@ -36,7 +36,7 @@ std::vector> getDefaultCommands() //(gui name/c addEntry(_("Standby" ), Zstr("rundll32.exe powrprof.dll,SetSuspendState Sleep")); //suspend/Suspend to RAM/sleep addEntry(_("Log off" ), Zstr("shutdown /l")); addEntry(_("Shut down"), Zstr("shutdown /s /t 60")); - //addEntry(_("Hibernate"), L"shutdown /h"); //Suspend to disk -> Standby is better anyway + //addEntry(_"Hibernate", L"shutdown /h"); //Suspend to disk -> Standby is better anyway } else //XP { diff --git a/FreeFileSync/Source/ui/osx_dock.h b/FreeFileSync/Source/ui/osx_dock.h deleted file mode 100644 index 62237524..00000000 --- a/FreeFileSync/Source/ui/osx_dock.h +++ /dev/null @@ -1,17 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_DOCK_837210847312534 -#define OSX_DOCK_837210847312534 - -#include - -namespace osx -{ -void dockIconSetText(const char* str); //throw SysError -} - -#endif //OSX_DOCK_837210847312534 diff --git a/FreeFileSync/Source/ui/osx_dock.mm b/FreeFileSync/Source/ui/osx_dock.mm deleted file mode 100644 index 8f42ae88..00000000 --- a/FreeFileSync/Source/ui/osx_dock.mm +++ /dev/null @@ -1,24 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "osx_dock.h" -#include -#include - - -void osx::dockIconSetText(const char* str) //throw SysError -{ - @try - { - NSString* label = [NSString stringWithCString:str encoding:NSUTF8StringEncoding]; - //stringWithCString returns string which is already set to autorelease! - [[NSApp dockTile] setBadgeLabel:label]; //label may be nil - } - @catch (NSException* e) - { - throwSysError(e); //throw SysError - } -} diff --git a/FreeFileSync/Source/ui/progress_indicator.cpp b/FreeFileSync/Source/ui/progress_indicator.cpp index dc1f42a6..123e2649 100644 --- a/FreeFileSync/Source/ui/progress_indicator.cpp +++ b/FreeFileSync/Source/ui/progress_indicator.cpp @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include #include "gui_generated.h" #include "../lib/ffs_paths.h" @@ -410,7 +410,7 @@ public: for (auto it = log_.begin(); it != log_.end(); ++it) if (it->type & includedTypes) { - assert_static((IsSameType::Type, wchar_t>::value)); + static_assert(IsSameType::Type, wchar_t>::value, ""); assert(!startsWith(it->message, L'\n')); size_t rowNumber = 0; @@ -419,7 +419,7 @@ public: if (c == L'\n') { if (!lastCharNewline) //do not reference empty lines! - viewRef.push_back(Line(it, rowNumber)); + viewRef.emplace_back(it, rowNumber); ++rowNumber; lastCharNewline = true; } @@ -427,7 +427,7 @@ public: lastCharNewline = false; if (!lastCharNewline) - viewRef.push_back(Line(it, rowNumber)); + viewRef.emplace_back(it, rowNumber); } } @@ -481,9 +481,9 @@ class GridDataMessages : public GridData public: GridDataMessages(const std::shared_ptr& msgView) : msgView_(msgView) {} - virtual size_t getRowCount() const override { return msgView_ ? msgView_->rowsOnView() : 0; } + size_t getRowCount() const override { return msgView_ ? msgView_->rowsOnView() : 0; } - virtual wxString getValue(size_t row, ColumnType colType) const override + wxString getValue(size_t row, ColumnType colType) const override { if (msgView_) if (Opt entry = msgView_->getEntry(row)) @@ -515,7 +515,7 @@ public: return wxEmptyString; } - virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override + void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override { wxRect rectTmp = rect; @@ -572,7 +572,7 @@ public: } } - virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override + int getBestSize(wxDC& dc, size_t row, ColumnType colType) override { // -> synchronize renderCell() <-> getBestSize() @@ -609,7 +609,7 @@ public: return std::max(getResourceImage(L"msg_info_small").GetHeight(), grid.getMainWin().GetCharHeight() + 2) + 1; //+ some space + bottom border } - virtual wxString getToolTip(size_t row, ColumnType colType) const override + wxString getToolTip(size_t row, ColumnType colType) const override { switch (static_cast(colType)) { @@ -623,7 +623,7 @@ public: return wxEmptyString; } - virtual wxString getColumnLabel(ColumnType colType) const override { return wxEmptyString; } + wxString getColumnLabel(ColumnType colType) const override { return wxEmptyString; } private: const std::shared_ptr msgView_; @@ -665,9 +665,9 @@ public: m_gridMessages->showRowLabel(false); m_gridMessages->setRowHeight(rowHeight); std::vector attr; - attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_TIME ), colMsgTimeWidth, 0)); - attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_CATEGORY), colMsgCategoryWidth, 0)); - attr.push_back(Grid::ColumnAttribute(static_cast(COL_TYPE_MSG_TEXT ), -colMsgTimeWidth - colMsgCategoryWidth, 1)); + attr.emplace_back(static_cast(COL_TYPE_MSG_TIME ), colMsgTimeWidth, 0); + attr.emplace_back(static_cast(COL_TYPE_MSG_CATEGORY), colMsgCategoryWidth, 0); + attr.emplace_back(static_cast(COL_TYPE_MSG_TEXT ), -colMsgTimeWidth - colMsgCategoryWidth, 1); m_gridMessages->setColumnConfig(attr); //support for CTRL + C @@ -682,19 +682,19 @@ public: } private: - virtual void OnErrors(wxCommandEvent& event) override + void OnErrors(wxCommandEvent& event) override { m_bpButtonErrors->toggle(); updateGrid(); } - virtual void OnWarnings(wxCommandEvent& event) override + void OnWarnings(wxCommandEvent& event) override { m_bpButtonWarnings->toggle(); updateGrid(); } - virtual void OnInfo(wxCommandEvent& event) override + void OnInfo(wxCommandEvent& event) override { m_bpButtonInfo->toggle(); updateGrid(); @@ -871,10 +871,10 @@ public: assert((!samples.empty() || lastSample == std::pair(0, 0))); //samples.clear(); - //samples.insert(std::make_pair(-1000, 0)); - //samples.insert(std::make_pair(0, 0)); - //samples.insert(std::make_pair(1, 1)); - //samples.insert(std::make_pair(1000, 0)); + //samples.emplace(-1000, 0); + //samples.emplace(0, 0); + //samples.emplace(1, 1); + //samples.emplace(1000, 0); //return; lastSample = std::make_pair(timeNowMs, value); @@ -893,7 +893,7 @@ public: } private: - virtual std::pair getRangeX() const override + std::pair getRangeX() const override { if (samples.empty()) return std::make_pair(0.0, 0.0); @@ -911,7 +911,7 @@ private: upperEndMs / 1000.0); } - virtual Opt getLessEq(double x) const override //x: seconds since begin + Opt getLessEq(double x) const override //x: seconds since begin { const int64_t timex = std::floor(x * 1000); //------ add artifical last sample value ------- @@ -929,7 +929,7 @@ private: return CurvePoint(it->first / 1000.0, it->second); } - virtual Opt getGreaterEq(double x) const override + Opt getGreaterEq(double x) const override { const int64_t timex = std::ceil(x * 1000); //------ add artifical last sample value ------- @@ -961,13 +961,13 @@ public: double getValueX() const { return x_; } private: - virtual std::pair getRangeX() const override { return std::make_pair(x_, x_); } //conceptually just a vertical line! + std::pair getRangeX() const override { return std::make_pair(x_, x_); } //conceptually just a vertical line! - virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override + void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override { - points.push_back(CurvePoint(0, y_)); - points.push_back(CurvePoint(x_, y_)); - points.push_back(CurvePoint(x_, 0)); + points.emplace_back(0, y_); + points.emplace_back(x_, y_); + points.emplace_back(x_, 0); } double x_; //time elapsed in seconds @@ -980,7 +980,7 @@ const double stretchDefaultBlockSize = 1.4; //enlarge block default size struct LabelFormatterBytes : public LabelFormatter { - virtual double getOptimalBlockSize(double bytesProposed) const + double getOptimalBlockSize(double bytesProposed) const override { bytesProposed *= stretchDefaultBlockSize; //enlarge block default size @@ -998,13 +998,13 @@ struct LabelFormatterBytes : public LabelFormatter return e * numeric::nearMatch(a, std::begin(steps), std::end(steps)); } - virtual wxString formatText(double value, double optimalBlockSize) const { return filesizeToShortString(static_cast(value)); }; + wxString formatText(double value, double optimalBlockSize) const override { return filesizeToShortString(static_cast(value)); }; }; struct LabelFormatterItemCount : public LabelFormatter { - virtual double getOptimalBlockSize(double itemsProposed) const + double getOptimalBlockSize(double itemsProposed) const override { itemsProposed *= stretchDefaultBlockSize; //enlarge block default size @@ -1014,7 +1014,7 @@ struct LabelFormatterItemCount : public LabelFormatter return nextNiceNumber(itemsProposed); } - virtual wxString formatText(double value, double optimalBlockSize) const + wxString formatText(double value, double optimalBlockSize) const override { return toGuiString(numeric::round(value)); //not enough room for a "%x items" representation }; @@ -1025,7 +1025,7 @@ struct LabelFormatterTimeElapsed : public LabelFormatter { LabelFormatterTimeElapsed(bool drawLabel) : drawLabel_(drawLabel) {} - virtual double getOptimalBlockSize(double secProposed) const + double getOptimalBlockSize(double secProposed) const override { //5 sec minimum block size const double stepsSec[] = { 5, 10, 20, 30, 60 }; //nice numbers for seconds @@ -1034,7 +1034,7 @@ struct LabelFormatterTimeElapsed : public LabelFormatter const double stepsMin[] = { 1, 2, 5, 10, 15, 20, 30, 60 }; //nice numbers for minutes if (secProposed <= 3600) - return 60.0 * numeric::nearMatch(secProposed / 60, std::begin(stepsMin), std::end(stepsMin)); + return 60 * numeric::nearMatch(secProposed / 60, std::begin(stepsMin), std::end(stepsMin)); if (secProposed <= 3600 * 24) return 3600 * nextNiceNumber(secProposed / 3600); //round up to full hours @@ -1042,7 +1042,7 @@ struct LabelFormatterTimeElapsed : public LabelFormatter return 24 * 3600 * nextNiceNumber(secProposed / (24 * 3600)); //round to full days } - virtual wxString formatText(double timeElapsed, double optimalBlockSize) const + wxString formatText(double timeElapsed, double optimalBlockSize) const override { if (!drawLabel_) return wxString(); @@ -1078,27 +1078,27 @@ public: const wxString& jobName, const Zstring& onCompletion, std::vector& onCompletionHistory); - virtual ~SyncProgressDialogImpl(); + ~SyncProgressDialogImpl() override; //call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater - virtual void processHasFinished(SyncResult resultId, const ErrorLog& log); - virtual void closeWindowDirectly(); + void processHasFinished(SyncResult resultId, const ErrorLog& log) override; + void closeWindowDirectly() override; - virtual wxWindow* getWindowIfVisible() { return this->IsShown() ? this : nullptr; } + wxWindow* getWindowIfVisible() override { return this->IsShown() ? this : nullptr; } //workaround OS X bug: if "this" is used as parent window for a modal dialog then this dialog will erroneously un-hide its parent! - virtual void initNewPhase(); - virtual void notifyProgressChange(); - virtual void updateGui() { updateGuiInt(true); } + void initNewPhase () override; + void notifyProgressChange() override; + void updateGui () override { updateGuiInt(true); } - virtual Zstring getExecWhenFinishedCommand() const { return pnl.m_comboBoxOnCompletion->getValue(); } + Zstring getExecWhenFinishedCommand() const override { return pnl.m_comboBoxOnCompletion->getValue(); } - virtual void stopTimer() //halt all internal counters! + void stopTimer() override //halt all internal counters! { pnl.m_animCtrlSyncing->Stop(); timeElapsed.pause(); } - virtual void resumeTimer() + void resumeTimer() override { pnl.m_animCtrlSyncing->Play(); timeElapsed.resume(); @@ -1182,8 +1182,8 @@ SyncProgressDialogImpl::SyncProgressDialogImpl(long style, //wxF timeLastSpeedEstimateMs (-1000000), //some big number phaseStartMs(0) { - assert_static((IsSameType::value || - IsSameType::value)); + static_assert(IsSameType::value || + IsSameType::value, ""); assert((IsSameType::value == !parentFrame)); //finish construction of this dialog: diff --git a/FreeFileSync/Source/ui/small_dlgs.cpp b/FreeFileSync/Source/ui/small_dlgs.cpp index 66692f7a..b142e8d8 100644 --- a/FreeFileSync/Source/ui/small_dlgs.cpp +++ b/FreeFileSync/Source/ui/small_dlgs.cpp @@ -40,9 +40,9 @@ public: AboutDlg(wxWindow* parent); private: - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnOK (wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_OKAY); } - virtual void OnDonate(wxCommandEvent& event) { wxLaunchDefaultBrowser(L"http://freefilesync.sourceforge.net/donate.php"); } + void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnOK (wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_OKAY); } + void OnDonate(wxCommandEvent& event) override { wxLaunchDefaultBrowser(L"http://www.freefilesync.org/donate.php"); } }; @@ -91,7 +91,7 @@ AboutDlg::AboutDlg(wxWindow* parent) : AboutDlgGenerated(parent) #endif build += zen::is64BitBuild ? L" x64" : L" x86"; - assert_static(zen::is32BitBuild || zen::is64BitBuild); + static_assert(zen::is32BitBuild || zen::is64BitBuild, ""); GetSizer()->SetSizeHints(this); //~=Fit() + SetMinSize() @@ -151,11 +151,10 @@ public: bool& useRecycleBin); private: - virtual void OnOK(wxCommandEvent& event); - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnDelOnBothSides(wxCommandEvent& event); - virtual void OnUseRecycler(wxCommandEvent& event); + void OnOK (wxCommandEvent& event) override; + void OnCancel(wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnUseRecycler (wxCommandEvent& event) override; void updateGui(); @@ -256,10 +255,6 @@ void DeleteDialog::OnOK(wxCommandEvent& event) EndModal(ReturnSmallDlg::BUTTON_OKAY); } -void DeleteDialog::OnDelOnBothSides(wxCommandEvent& event) -{ - updateGui(); -} void DeleteDialog::OnUseRecycler(wxCommandEvent& event) { @@ -289,9 +284,9 @@ public: const zen::SyncStatistics& st, bool& dontShowAgain); private: - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnStartSync(wxCommandEvent& event); + void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnCancel (wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnStartSync(wxCommandEvent& event) override; bool& m_dontShowAgain; }; @@ -379,18 +374,18 @@ public: OptionsDlg(wxWindow* parent, xmlAccess::XmlGlobalSettings& globalSettings); private: - virtual void OnOkay(wxCommandEvent& event); - virtual void OnResetDialogs(wxCommandEvent& event); - virtual void OnDefault(wxCommandEvent& event); - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnAddRow(wxCommandEvent& event); - virtual void OnRemoveRow(wxCommandEvent& event); - virtual void OnHelpShowExamples(wxHyperlinkEvent& event) { displayHelpEntry(L"html/External Applications.html", this); } + void OnOkay (wxCommandEvent& event) override; + void OnResetDialogs(wxCommandEvent& event) override; + void OnDefault (wxCommandEvent& event) override; + void OnCancel (wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnAddRow (wxCommandEvent& event) override; + void OnRemoveRow (wxCommandEvent& event) override; + void OnHelpShowExamples(wxHyperlinkEvent& event) override { displayHelpEntry(L"html/External Applications.html", this); } void onResize(wxSizeEvent& event); void updateGui(); - virtual void OnToggleAutoRetryCount(wxCommandEvent& event) { updateGui(); } + void OnToggleAutoRetryCount(wxCommandEvent& event) override { updateGui(); } void setExtApp(const xmlAccess::ExternalApps& extApp); xmlAccess::ExternalApps getExtApp() const; @@ -578,7 +573,7 @@ xmlAccess::ExternalApps OptionsDlg::getExtApp() const description = it->second; if (!description.empty() || !commandline.empty()) - output.push_back(std::make_pair(description, commandline)); + output.emplace_back(description, commandline); } return output; } @@ -629,16 +624,16 @@ public: SelectTimespanDlg(wxWindow* parent, std::int64_t& timeFrom, std::int64_t& timeTo); private: - virtual void OnOkay(wxCommandEvent& event); - virtual void OnCancel(wxCommandEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnClose (wxCloseEvent& event) { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnOkay (wxCommandEvent& event) override; + void OnCancel(wxCommandEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } + void OnClose (wxCloseEvent& event) override { EndModal(ReturnSmallDlg::BUTTON_CANCEL); } - virtual void OnChangeSelectionFrom(wxCalendarEvent& event) + void OnChangeSelectionFrom(wxCalendarEvent& event) override { if (m_calendarFrom->GetDate() > m_calendarTo->GetDate()) m_calendarTo->SetDate(m_calendarFrom->GetDate()); } - virtual void OnChangeSelectionTo(wxCalendarEvent& event) + void OnChangeSelectionTo(wxCalendarEvent& event) override { if (m_calendarFrom->GetDate() > m_calendarTo->GetDate()) m_calendarFrom->SetDate(m_calendarTo->GetDate()); diff --git a/FreeFileSync/Source/ui/sorting.h b/FreeFileSync/Source/ui/sorting.h index 1493faa4..d022aeb7 100644 --- a/FreeFileSync/Source/ui/sorting.h +++ b/FreeFileSync/Source/ui/sorting.h @@ -7,7 +7,6 @@ #ifndef SORTING_H_82574232452345 #define SORTING_H_82574232452345 -#include #include #include "../file_hierarchy.h" @@ -17,9 +16,9 @@ namespace { struct CompileTimeReminder : public FSObjectVisitor { - virtual void visit(const FilePair& fileObj) {} - virtual void visit(const SymlinkPair& linkObj) {} - virtual void visit(const DirPair& dirObj ) {} + void visit(const FilePair& fileObj) override {} + void visit(const SymlinkPair& linkObj) override {} + void visit(const DirPair& dirObj ) override {} } checkDymanicCasts; //just a compile-time reminder to manually check dynamic casts in this file when needed } diff --git a/FreeFileSync/Source/ui/sync_cfg.cpp b/FreeFileSync/Source/ui/sync_cfg.cpp index 2973f194..88424ccb 100644 --- a/FreeFileSync/Source/ui/sync_cfg.cpp +++ b/FreeFileSync/Source/ui/sync_cfg.cpp @@ -48,9 +48,9 @@ public: const wxString& title); private: - virtual void OnOkay (wxCommandEvent& event) override; - virtual void OnCancel(wxCommandEvent& event) override { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } - virtual void OnClose (wxCloseEvent& event) override { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } + void OnOkay (wxCommandEvent& event) override; + void OnCancel(wxCommandEvent& event) override { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } + void OnClose (wxCloseEvent& event) override { EndModal(ReturnSyncConfig::BUTTON_CANCEL); } void onLocalKeyEvent(wxKeyEvent& event); @@ -65,15 +65,15 @@ private: }; //------------- comparison panel ---------------------- - virtual void OnHelpComparisonSettings(wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Comparison Settings.html" , this); } - virtual void OnHelpTimeShift (wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Daylight Saving Time.html", this); } + void OnHelpComparisonSettings(wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Comparison Settings.html" , this); } + void OnHelpTimeShift (wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Daylight Saving Time.html", this); } - virtual void OnToggleLocalCompSettings(wxCommandEvent& event) override { updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ } - virtual void OnTimeSize(wxCommandEvent& event) override { localCmpVar = CMP_BY_TIME_SIZE; updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ } - virtual void OnContent (wxCommandEvent& event) override { localCmpVar = CMP_BY_CONTENT; updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ } - virtual void OnTimeSizeDouble(wxMouseEvent& event) override; - virtual void OnContentDouble (wxMouseEvent& event) override; - virtual void OnChangeCompOption(wxCommandEvent& event) override { updateCompGui(); } + void OnToggleLocalCompSettings(wxCommandEvent& event) override { updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ } + void OnTimeSize (wxCommandEvent& event) override { localCmpVar = CMP_BY_TIME_SIZE; updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ } + void OnContent (wxCommandEvent& event) override { localCmpVar = CMP_BY_CONTENT; updateCompGui(); updateSyncGui(); /*affects sync settings, too!*/ } + void OnTimeSizeDouble (wxMouseEvent& event) override; + void OnContentDouble (wxMouseEvent& event) override; + void OnChangeCompOption (wxCommandEvent& event) override { updateCompGui(); } void updateCompGui(); @@ -83,9 +83,9 @@ private: EnumDescrList enumDescrHandleSyml; //------------- filter panel -------------------------- - virtual void OnHelpShowExamples(wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Exclude Items.html", this); } - virtual void OnChangeFilterOption(wxCommandEvent& event) override { updateFilterGui(); } - virtual void OnFilterReset (wxCommandEvent& event) override { setFilter(FilterConfig()); } + void OnHelpShowExamples(wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Exclude Items.html", this); } + void OnChangeFilterOption(wxCommandEvent& event) override { updateFilterGui(); } + void OnFilterReset (wxCommandEvent& event) override { setFilter(FilterConfig()); } void onFilterKeyEvent(wxKeyEvent& event); void setFilter(const FilterConfig& filter); @@ -98,35 +98,35 @@ private: EnumDescrList enumSizeDescr; //------------- synchronization panel ----------------- - virtual void OnSyncTwoWay(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::TWOWAY; updateSyncGui(); } - virtual void OnSyncMirror(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::MIRROR; updateSyncGui(); } - virtual void OnSyncUpdate(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::UPDATE; updateSyncGui(); } - virtual void OnSyncCustom(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::CUSTOM; updateSyncGui(); } - - virtual void OnToggleLocalSyncSettings(wxCommandEvent& event) override { updateSyncGui(); } - virtual void OnToggleDetectMovedFiles(wxCommandEvent& event) override { directionCfg.detectMovedFiles = !directionCfg.detectMovedFiles; updateSyncGui(); } - virtual void OnChangeSyncOption (wxCommandEvent& event) override { updateSyncGui(); } - - virtual void OnSyncTwoWayDouble(wxMouseEvent& event) override; - virtual void OnSyncMirrorDouble(wxMouseEvent& event) override; - virtual void OnSyncUpdateDouble(wxMouseEvent& event) override; - virtual void OnSyncCustomDouble(wxMouseEvent& event) override; - - virtual void OnExLeftSideOnly (wxCommandEvent& event) override; - virtual void OnExRightSideOnly(wxCommandEvent& event) override; - virtual void OnLeftNewer (wxCommandEvent& event) override; - virtual void OnRightNewer (wxCommandEvent& event) override; - virtual void OnDifferent (wxCommandEvent& event) override; - virtual void OnConflict (wxCommandEvent& event) override; - - virtual void OnDeletionPermanent (wxCommandEvent& event) override { handleDeletion = DELETE_PERMANENTLY; updateSyncGui(); } - virtual void OnDeletionRecycler (wxCommandEvent& event) override { handleDeletion = DELETE_TO_RECYCLER; updateSyncGui(); } - virtual void OnDeletionVersioning (wxCommandEvent& event) override { handleDeletion = DELETE_TO_VERSIONING; updateSyncGui(); } - - virtual void OnErrorPopup (wxCommandEvent& event) override { onGuiError = ON_GUIERROR_POPUP; updateSyncGui(); } - virtual void OnErrorIgnore(wxCommandEvent& event) override { onGuiError = ON_GUIERROR_IGNORE; updateSyncGui(); } - - virtual void OnHelpVersioning(wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Versioning.html", this); } + void OnSyncTwoWay(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::TWOWAY; updateSyncGui(); } + void OnSyncMirror(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::MIRROR; updateSyncGui(); } + void OnSyncUpdate(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::UPDATE; updateSyncGui(); } + void OnSyncCustom(wxCommandEvent& event) override { directionCfg.var = DirectionConfig::CUSTOM; updateSyncGui(); } + + void OnToggleLocalSyncSettings(wxCommandEvent& event) override { updateSyncGui(); } + void OnToggleDetectMovedFiles (wxCommandEvent& event) override { directionCfg.detectMovedFiles = !directionCfg.detectMovedFiles; updateSyncGui(); } + void OnChangeSyncOption (wxCommandEvent& event) override { updateSyncGui(); } + + void OnSyncTwoWayDouble(wxMouseEvent& event) override; + void OnSyncMirrorDouble(wxMouseEvent& event) override; + void OnSyncUpdateDouble(wxMouseEvent& event) override; + void OnSyncCustomDouble(wxMouseEvent& event) override; + + void OnExLeftSideOnly (wxCommandEvent& event) override; + void OnExRightSideOnly(wxCommandEvent& event) override; + void OnLeftNewer (wxCommandEvent& event) override; + void OnRightNewer (wxCommandEvent& event) override; + void OnDifferent (wxCommandEvent& event) override; + void OnConflict (wxCommandEvent& event) override; + + void OnDeletionPermanent (wxCommandEvent& event) override { handleDeletion = DELETE_PERMANENTLY; updateSyncGui(); } + void OnDeletionRecycler (wxCommandEvent& event) override { handleDeletion = DELETE_TO_RECYCLER; updateSyncGui(); } + void OnDeletionVersioning (wxCommandEvent& event) override { handleDeletion = DELETE_TO_VERSIONING; updateSyncGui(); } + + void OnErrorPopup (wxCommandEvent& event) override { onGuiError = ON_GUIERROR_POPUP; updateSyncGui(); } + void OnErrorIgnore(wxCommandEvent& event) override { onGuiError = ON_GUIERROR_IGNORE; updateSyncGui(); } + + void OnHelpVersioning(wxHyperlinkEvent& event) override { displayHelpEntry(L"html/Versioning.html", this); } struct SyncOptions { diff --git a/FreeFileSync/Source/ui/taskbar.cpp b/FreeFileSync/Source/ui/taskbar.cpp index 9e7cbcde..863eb6d6 100644 --- a/FreeFileSync/Source/ui/taskbar.cpp +++ b/FreeFileSync/Source/ui/taskbar.cpp @@ -9,6 +9,7 @@ #ifdef ZEN_WIN #include #include +#include #include "../dll/Taskbar_Seven/taskbar.h" #elif defined HAVE_UBUNTU_UNITY @@ -169,7 +170,7 @@ public: //######################################################################################################## -Taskbar::Taskbar(const wxFrame& window) : pimpl_(new Pimpl(window)) {} //throw TaskbarNotAvailable +Taskbar::Taskbar(const wxFrame& window) : pimpl_(zen::make_unique(window)) {} //throw TaskbarNotAvailable Taskbar::~Taskbar() {} void Taskbar::setStatus(Status status) { pimpl_->setStatus(status); } diff --git a/FreeFileSync/Source/ui/tray_icon.cpp b/FreeFileSync/Source/ui/tray_icon.cpp index c9debfb1..712e4d51 100644 --- a/FreeFileSync/Source/ui/tray_icon.cpp +++ b/FreeFileSync/Source/ui/tray_icon.cpp @@ -129,12 +129,10 @@ public: //=> the only way to distinguish single left click and double-click is to wait wxSystemSettings::GetMetric(wxSYS_DCLICK_MSEC) (480ms) which is way too long! } - //virtual ~TaskBarImpl(){} - void dontCallbackAnymore() { onRequestResume_ = nullptr; } private: - virtual wxMenu* CreatePopupMenu() + wxMenu* CreatePopupMenu() override { if (!onRequestResume_) return nullptr; diff --git a/FreeFileSync/Source/ui/tree_view.cpp b/FreeFileSync/Source/ui/tree_view.cpp index de952465..6e5d4430 100644 --- a/FreeFileSync/Source/ui/tree_view.cpp +++ b/FreeFileSync/Source/ui/tree_view.cpp @@ -88,7 +88,7 @@ void TreeView::extractVisibleSubtree(HierarchyObject& hierObj, //in { const bool included = pred(subDirObj); - cont.subDirs.push_back(TreeView::DirNodeImpl()); // + cont.subDirs.emplace_back(); // auto& subDirCont = cont.subDirs.back(); TreeView::extractVisibleSubtree(subDirObj, subDirCont, pred); if (included) @@ -310,14 +310,14 @@ void TreeView::getChildren(const Container& cont, unsigned int level, std::vecto for (const DirNodeImpl& subDir : cont.subDirs) { - output.push_back(TreeView::TreeLine(level, 0, &subDir, TreeView::TYPE_DIRECTORY)); - workList.push_back(std::make_pair(subDir.bytesGross, &output.back().percent_)); + output.emplace_back(level, 0, &subDir, TreeView::TYPE_DIRECTORY); + workList.emplace_back(subDir.bytesGross, &output.back().percent_); } if (cont.firstFileId) { - output.push_back(TreeLine(level, 0, &cont, TreeView::TYPE_FILES)); - workList.push_back(std::make_pair(cont.bytesNet, &output.back().percent_)); + output.emplace_back(level, 0, &cont, TreeView::TYPE_FILES); + workList.emplace_back(cont.bytesNet, &output.back().percent_); } calcPercentage(workList); @@ -349,7 +349,7 @@ void TreeView::applySubView(std::vector&& newView) return nullptr; }; - zen::hash_set expandedNodes; + std::unordered_set expandedNodes; if (!flatTree.empty()) { auto it = flatTree.begin(); @@ -380,8 +380,8 @@ void TreeView::applySubView(std::vector&& newView) for (const RootNodeImpl& root : folderCmpView) { - flatTree.push_back(TreeView::TreeLine(0, 0, &root, TreeView::TYPE_ROOT)); - workList.push_back(std::make_pair(root.bytesGross, &flatTree.back().percent_)); + flatTree.emplace_back(0, 0, &root, TreeView::TYPE_ROOT); + workList.emplace_back(root.bytesGross, &flatTree.back().percent_); } calcPercentage(workList); @@ -418,7 +418,7 @@ void TreeView::updateView(Predicate pred) for (const std::shared_ptr& baseObj : folderCmp) { - newView.push_back(TreeView::RootNodeImpl()); + newView.emplace_back(); RootNodeImpl& root = newView.back(); this->extractVisibleSubtree(*baseObj, root, pred); //"this->" is bogus for a static method, but GCC screws this one up @@ -683,7 +683,7 @@ std::unique_ptr TreeView::getLine(size_t row) const case TreeView::TYPE_ROOT: { const auto& root = *static_cast(flatTree[row].node_); - return make_unique(percent, root.bytesGross, root.itemCountGross, getStatus(row), *root.baseDirObj, root.displayName); + return zen::make_unique(percent, root.bytesGross, root.itemCountGross, getStatus(row), *root.baseDirObj, root.displayName); } break; @@ -691,7 +691,7 @@ std::unique_ptr TreeView::getLine(size_t row) const { const auto* dir = static_cast(flatTree[row].node_); if (auto dirObj = dynamic_cast(FileSystemObject::retrieve(dir->objId))) - return make_unique(percent, dir->bytesGross, dir->itemCountGross, level, getStatus(row), *dirObj); + return zen::make_unique(percent, dir->bytesGross, dir->itemCountGross, level, getStatus(row), *dirObj); } break; @@ -712,7 +712,7 @@ std::unique_ptr TreeView::getLine(size_t row) const if (lastViewFilterPred(fsObj)) filesAndLinks.push_back(&fsObj); - return make_unique(percent, parentDir->bytesNet, parentDir->itemCountNet, level, filesAndLinks); + return zen::make_unique(percent, parentDir->bytesNet, parentDir->itemCountNet, level, filesAndLinks); } } break; @@ -775,9 +775,9 @@ public: bool getShowPercentage() const { return showPercentBar; } private: - virtual size_t getRowCount() const { return treeDataView_ ? treeDataView_->linesTotal() : 0; } + size_t getRowCount() const override { return treeDataView_ ? treeDataView_->linesTotal() : 0; } - virtual wxString getToolTip(size_t row, ColumnType colType) const override + wxString getToolTip(size_t row, ColumnType colType) const override { switch (static_cast(colType)) { @@ -803,7 +803,7 @@ private: return wxString(); } - virtual wxString getValue(size_t row, ColumnType colType) const + wxString getValue(size_t row, ColumnType colType) const override { if (treeDataView_) { @@ -829,7 +829,7 @@ private: return wxString(); } - virtual void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override + void renderColumnLabel(Grid& tree, wxDC& dc, const wxRect& rect, ColumnType colType, bool highlighted) override { wxRect rectInside = drawColumnLabelBorder(dc, rect); drawColumnLabelBackground(dc, rectInside, highlighted); @@ -852,7 +852,7 @@ private: static const int GAP_SIZE = 2; - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override + void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected) override { if (enabled) { @@ -866,7 +866,7 @@ private: clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); } - virtual void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override + void renderCell(wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected) override { //wxRect rectTmp= drawCellBorder(dc, rect); wxRect rectTmp = rect; @@ -1036,7 +1036,7 @@ private: } } - virtual int getBestSize(wxDC& dc, size_t row, ColumnType colType) override + int getBestSize(wxDC& dc, size_t row, ColumnType colType) override { // -> synchronize renderCell() <-> getBestSize() <-> onMouseLeft() @@ -1054,7 +1054,7 @@ private: 2 * GAP_SIZE; //include gap from right! } - virtual wxString getColumnLabel(ColumnType colType) const + wxString getColumnLabel(ColumnType colType) const override { switch (static_cast(colType)) { diff --git a/FreeFileSync/Source/ui/triple_splitter.cpp b/FreeFileSync/Source/ui/triple_splitter.cpp index fbdd22d7..f88691ec 100644 --- a/FreeFileSync/Source/ui/triple_splitter.cpp +++ b/FreeFileSync/Source/ui/triple_splitter.cpp @@ -6,6 +6,7 @@ #include "triple_splitter.h" #include +#include using namespace zen; @@ -173,7 +174,7 @@ void TripleSplitter::onMouseLeftDown(wxMouseEvent& event) const int posX = event.GetPosition().x; if (hitOnSashLine(posX)) - activeMove.reset(new SashMove(*this, posX, centerOffset)); + activeMove = zen::make_unique(*this, posX, centerOffset); event.Skip(); } diff --git a/FreeFileSync/Source/version/version.h b/FreeFileSync/Source/version/version.h index 558b09ea..33153cb8 100644 --- a/FreeFileSync/Source/version/version.h +++ b/FreeFileSync/Source/version/version.h @@ -3,7 +3,7 @@ namespace zen { -const wchar_t currentVersion[] = L"6.10"; //internal linkage! +const wchar_t currentVersion[] = L"6.11"; //internal linkage! } #endif diff --git a/wx+/bitmap_button.h b/wx+/bitmap_button.h index 3255ffce..14476324 100644 --- a/wx+/bitmap_button.h +++ b/wx+/bitmap_button.h @@ -60,7 +60,7 @@ void setBitmapTextLabel(wxBitmapButton& btn, const wxImage& img, const wxString& } //SetMinSize() instead of SetSize() is needed here for wxWindows layout determination to work corretly - const int defaultHeight = wxButton::GetDefaultSize().GetHeight(); + const int defaultHeight = wxButton::GetDefaultSize().GetHeight(); btn.SetMinSize(wxSize(dynImage.GetWidth () + 2 * border, std::max(dynImage.GetHeight() + 2 * border, defaultHeight))); diff --git a/wx+/choice_enum.h b/wx+/choice_enum.h index e06931d5..57718158 100644 --- a/wx+/choice_enum.h +++ b/wx+/choice_enum.h @@ -41,7 +41,7 @@ struct EnumDescrList { EnumDescrList& add(Enum value, const wxString& text, const wxString& tooltip = wxEmptyString) { - descrList.push_back(std::make_pair(value, std::make_pair(text, tooltip))); + descrList.emplace_back(value, std::make_pair(text, tooltip)); return *this; } typedef std::vector>> DescrList; diff --git a/wx+/context_menu.h b/wx+/context_menu.h index 2557737a..a498c429 100644 --- a/wx+/context_menu.h +++ b/wx+/context_menu.h @@ -28,7 +28,7 @@ namespace zen class ContextMenu : private wxEvtHandler { public: - ContextMenu() : menu(new wxMenu) {} + ContextMenu() : menu(zen::make_unique()) {} void addItem(const wxString& label, const std::function& command, const wxBitmap* bmp = nullptr, bool enabled = true) { diff --git a/wx+/dc.h b/wx+/dc.h index 059db018..b2f5b584 100644 --- a/wx+/dc.h +++ b/wx+/dc.h @@ -7,6 +7,7 @@ #ifndef DC_3813704987123956832143243214 #define DC_3813704987123956832143243214 +#include #include //for macro: wxALWAYS_NATIVE_DOUBLE_BUFFER namespace zen @@ -47,7 +48,7 @@ public: auto it = refDcToAreaMap().find(&dc); if (it != refDcToAreaMap().end()) { - oldRect.reset(new wxRect(it->second)); + oldRect = zen::make_unique(it->second); wxRect tmp = r; tmp.Intersect(*oldRect); //better safe than sorry @@ -57,7 +58,7 @@ public: else { dc_.SetClippingRegion(r); - refDcToAreaMap().insert(std::make_pair(&dc_, r)); + refDcToAreaMap().emplace(&dc_, r); } } @@ -75,7 +76,7 @@ public: private: //associate "active" clipping area with each DC - static hash_map& refDcToAreaMap() { static hash_map clippingAreas; return clippingAreas; } + static std::unordered_map& refDcToAreaMap() { static std::unordered_map clippingAreas; return clippingAreas; } std::unique_ptr oldRect; wxDC& dc_; @@ -97,7 +98,7 @@ public: { const wxSize clientSize = wnd.GetClientSize(); if (!buffer_ || clientSize != wxSize(buffer->GetWidth(), buffer->GetHeight())) - buffer.reset(new wxBitmap(clientSize.GetWidth(), clientSize.GetHeight())); + buffer = zen::make_unique(clientSize.GetWidth(), clientSize.GetHeight()); SelectObject(*buffer); diff --git a/wx+/file_drop.h b/wx+/file_drop.h index 47019a04..55772a03 100644 --- a/wx+/file_drop.h +++ b/wx+/file_drop.h @@ -59,7 +59,7 @@ public: dropWindow_(dropWindow), dropPos_(dropPos) {} - virtual wxEvent* Clone() const { return new FileDropEvent(*this); } + wxEvent* Clone() const override { return new FileDropEvent(*this); } const std::vector& getFiles() const { return filesDropped_; } const wxWindow& getDropWindow() const { return dropWindow_; } @@ -85,7 +85,7 @@ public: WindowDropTarget(wxWindow& dropWindow) : dropWindow_(dropWindow) {} private: - virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& fileArray) + bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& fileArray) override { std::vector filepaths(fileArray.begin(), fileArray.end()); if (!filepaths.empty()) diff --git a/wx+/graph.cpp b/wx+/graph.cpp index f4e758c1..67f8e354 100644 --- a/wx+/graph.cpp +++ b/wx+/graph.cpp @@ -119,18 +119,19 @@ void widenRange(double& valMin, double& valMax, //in/out { double valRangePerBlock = (valMax - valMin) * optimalBlockSizePx / graphAreaSize; //proposal valRangePerBlock = labelFmt.getOptimalBlockSize(valRangePerBlock); - if (!numeric::isNull(valRangePerBlock)) - { - int blockMin = std::floor(valMin / valRangePerBlock); - int blockMax = std::ceil (valMax / valRangePerBlock); - if (blockMin == blockMax) //handle valMin == valMax == integer - ++blockMax; - - valMin = blockMin * valRangePerBlock; - valMax = blockMax * valRangePerBlock; - blockCount = blockMax - blockMin; - return; - } + if (numeric::isNull(valRangePerBlock)) //handle valMin == valMax + valRangePerBlock = 1; + warn_static("/| arbitrary!?") + + int blockMin = std::floor(valMin / valRangePerBlock); + int blockMax = std::ceil (valMax / valRangePerBlock); + if (blockMin == blockMax) //handle valMin == valMax == integer + ++blockMax; + + valMin = blockMin * valRangePerBlock; + valMax = blockMax * valRangePerBlock; + blockCount = blockMax - blockMin; + return; } blockCount = 0; } @@ -322,7 +323,7 @@ void ContinuousCurveData::getPoints(double minX, double maxX, int pixelWidth, st for (int i = posFrom; i <= posTo; ++i) { const double x = cvrtX.screenToReal(i); - points.push_back(CurvePoint(x, getValue(x))); + points.emplace_back(x, getValue(x)); } } } @@ -343,7 +344,7 @@ void SparseCurveData::getPoints(double minX, double maxX, int pixelWidth, std::v if (addSteps_) if (pt.y != points.back().y) - points.push_back(CurvePoint(pt.x, points.back().y)); + points.emplace_back(CurvePoint(pt.x, points.back().y)); //[!] aliasing parameter not yet supported via emplace_back: VS bug! => make copy } points.push_back(pt); }; @@ -442,7 +443,7 @@ void Graph2D::onPaintEvent(wxPaintEvent& event) void Graph2D::OnMouseLeftDown(wxMouseEvent& event) { - activeSel.reset(new MouseSelection(*this, event.GetPosition())); + activeSel = zen::make_unique(*this, event.GetPosition()); if (!event.ControlDown()) oldSel.clear(); @@ -498,7 +499,7 @@ void Graph2D::addCurve(const std::shared_ptr& data, const CurveAttrib CurveAttributes newAttr = ca; if (newAttr.autoColor) newAttr.setColor(getDefaultColor(curves_.size())); - curves_.push_back(std::make_pair(data, newAttr)); + curves_.emplace_back(data, newAttr); Refresh(); } @@ -699,8 +700,8 @@ void Graph2D::render(wxDC& dc) const std::vector points = drawPoints[it - curves_.begin()]; if (!points.empty()) { - points.push_back(wxPoint(points.back ().x, graphArea.GetBottom())); //add lower right and left corners - points.push_back(wxPoint(points.front().x, graphArea.GetBottom())); // + points.emplace_back(wxPoint(points.back ().x, graphArea.GetBottom())); //add lower right and left corners + points.emplace_back(wxPoint(points.front().x, graphArea.GetBottom())); //[!] aliasing parameter not yet supported via emplace_back: VS bug! => make copy wxDCBrushChanger dummy(dc, it->second.fillColor); wxDCPenChanger dummy2(dc, it->second.fillColor); diff --git a/wx+/graph.h b/wx+/graph.h index bafbe4eb..00b0b469 100644 --- a/wx+/graph.h +++ b/wx+/graph.h @@ -55,7 +55,7 @@ struct ContinuousCurveData : public CurveData virtual double getValue(double x) const = 0; private: - virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override; + void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override; }; struct SparseCurveData : public CurveData @@ -66,19 +66,19 @@ struct SparseCurveData : public CurveData virtual Opt getGreaterEq(double x) const = 0; private: - virtual void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override; + void getPoints(double minX, double maxX, int pixelWidth, std::vector& points) const override; bool addSteps_; }; struct ArrayCurveData : public SparseCurveData { virtual double getValue(size_t pos) const = 0; - virtual size_t getSize() const = 0; + virtual size_t getSize () const = 0; private: - virtual std::pair getRangeX() const override { const size_t sz = getSize(); return std::make_pair(0.0, sz == 0 ? 0.0 : sz - 1.0); } + std::pair getRangeX() const override { const size_t sz = getSize(); return std::make_pair(0.0, sz == 0 ? 0.0 : sz - 1.0); } - virtual Opt getLessEq(double x) const override + Opt getLessEq(double x) const override { const size_t sz = getSize(); const size_t pos = std::min(std::floor(x), sz - 1); //[!] expect unsigned underflow if empty! @@ -87,7 +87,7 @@ private: return NoValue(); } - virtual Opt getGreaterEq(double x) const override + Opt getGreaterEq(double x) const override { const size_t pos = std::max(std::ceil(x), 0); //[!] use std::max with signed type! if (pos < getSize()) @@ -100,8 +100,8 @@ struct VectorCurveData : public ArrayCurveData { std::vector& refData() { return data; } private: - virtual double getValue(size_t pos) const override { return pos < data.size() ? data[pos] : 0; } - virtual size_t getSize() const override { return data.size(); } + double getValue(size_t pos) const override { return pos < data.size() ? data[pos] : 0; } + size_t getSize() const override { return data.size(); } std::vector data; }; @@ -122,8 +122,8 @@ double nextNiceNumber(double blockSize); //round to next number which is conveni struct DecimalNumberFormatter : public LabelFormatter { - virtual double getOptimalBlockSize(double sizeProposed) const { return nextNiceNumber(sizeProposed); } - virtual wxString formatText(double value, double optimalBlockSize) const { return zen::numberTo(value); } + double getOptimalBlockSize(double sizeProposed ) const override { return nextNiceNumber(sizeProposed); } + wxString formatText (double value, double optimalBlockSize) const override { return zen::numberTo(value); } }; //------------------------------------------------------------------------------------------------------------ @@ -144,7 +144,7 @@ class GraphSelectEvent : public wxCommandEvent { public: GraphSelectEvent(const SelectionBlock& selBlock) : wxCommandEvent(wxEVT_GRAPH_SELECTION), selBlock_(selBlock) {} - virtual wxEvent* Clone() const { return new GraphSelectEvent(selBlock_); } + wxEvent* Clone() const override { return new GraphSelectEvent(selBlock_); } SelectionBlock getSelection() { return selBlock_; } diff --git a/wx+/grid.cpp b/wx+/grid.cpp index 5bcac1a5..de50d4c6 100644 --- a/wx+/grid.cpp +++ b/wx+/grid.cpp @@ -235,7 +235,7 @@ void GridData::drawColumnLabelText(wxDC& dc, const wxRect& rect, const wxString& CornerWin RowLabelWin ColLabelWin MainWin */ -class Grid::SubWindow : public wxWindow +class Grid::SubWindow : public wxWindow { public: SubWindow(Grid& parent) : @@ -378,9 +378,9 @@ public: CornerWin(Grid& parent) : SubWindow(parent) {} private: - virtual bool AcceptsFocus() const { return false; } + bool AcceptsFocus() const override { return false; } - virtual void render(wxDC& dc, const wxRect& rect) + void render(wxDC& dc, const wxRect& rect) override { const wxRect& clientRect = GetClientRect(); @@ -466,9 +466,9 @@ public: private: static wxString formatRow(size_t row) { return toGuiString(row + 1); } //convert number to std::wstring including thousands separator - virtual bool AcceptsFocus() const { return false; } + bool AcceptsFocus() const override { return false; } - virtual void render(wxDC& dc, const wxRect& rect) + void render(wxDC& dc, const wxRect& rect) override { /* @@ -537,9 +537,9 @@ private: } } - virtual void onMouseLeftDown(wxMouseEvent& event) { refParent().redirectRowLabelEvent(event); } - virtual void onMouseMovement(wxMouseEvent& event) { refParent().redirectRowLabelEvent(event); } - virtual void onMouseLeftUp (wxMouseEvent& event) { refParent().redirectRowLabelEvent(event); } + void onMouseLeftDown(wxMouseEvent& event) override { refParent().redirectRowLabelEvent(event); } + void onMouseMovement(wxMouseEvent& event) override { refParent().redirectRowLabelEvent(event); } + void onMouseLeftUp (wxMouseEvent& event) override { refParent().redirectRowLabelEvent(event); } int rowHeight; }; @@ -608,9 +608,9 @@ public: ColLabelWin(Grid& parent) : SubWindow(parent) {} private: - virtual bool AcceptsFocus() const { return false; } + bool AcceptsFocus() const override { return false; } - virtual void render(wxDC& dc, const wxRect& rect) + void render(wxDC& dc, const wxRect& rect) override { if (IsThisEnabled()) clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -680,7 +680,7 @@ private: } } - virtual void onMouseLeftDown(wxMouseEvent& event) + void onMouseLeftDown(wxMouseEvent& event) override { if (FindFocus() != &refParent().getMainWin()) refParent().getMainWin().SetFocus(); @@ -702,7 +702,7 @@ private: event.Skip(); } - virtual void onMouseLeftUp(wxMouseEvent& event) + void onMouseLeftUp(wxMouseEvent& event) override { activeResizing.reset(); //nothing else to do, actual work done by onMouseMovement() @@ -734,7 +734,7 @@ private: event.Skip(); } - virtual void onMouseCaptureLost(wxMouseCaptureLostEvent& event) + void onMouseCaptureLost(wxMouseCaptureLostEvent& event) override { activeResizing.reset(); activeMove.reset(); @@ -742,7 +742,7 @@ private: //event.Skip(); -> we DID handle it! } - virtual void onMouseLeftDouble(wxMouseEvent& event) + void onMouseLeftDouble(wxMouseEvent& event) override { if (Opt action = refParent().clientPosToColumnAction(event.GetPosition())) if (action->wantResize) @@ -758,7 +758,7 @@ private: event.Skip(); } - virtual void onMouseMovement(wxMouseEvent& event) + void onMouseMovement(wxMouseEvent& event) override { if (activeResizing) { @@ -820,14 +820,14 @@ private: event.Skip(); } - virtual void onLeaveWindow(wxMouseEvent& event) + void onLeaveWindow(wxMouseEvent& event) override { highlightCol.reset(); //wxEVT_LEAVE_WINDOW does not respect mouse capture! -> however highlight is drawn unconditionally during move/resize! Refresh(); event.Skip(); } - virtual void onMouseRightDown(wxMouseEvent& event) + void onMouseRightDown(wxMouseEvent& event) override { if (const Opt action = refParent().clientPosToColumnAction(event.GetPosition())) { @@ -863,7 +863,7 @@ public: ColLabelWin& colLabelWin) : SubWindow(parent), rowLabelWin_(rowLabelWin), colLabelWin_(colLabelWin), - cursorRow(0), + cursorRow(0), selectionAnchor(0), gridUpdatePending(false) { @@ -883,7 +883,7 @@ public: } private: - virtual void render(wxDC& dc, const wxRect& rect) + void render(wxDC& dc, const wxRect& rect) override { if (IsThisEnabled()) clearArea(dc, rect, wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); @@ -959,12 +959,12 @@ private: return refParent().isSelected(row); } - virtual void onMouseLeftDown (wxMouseEvent& event) { onMouseDown(event); } - virtual void onMouseLeftUp (wxMouseEvent& event) { onMouseUp (event); } - virtual void onMouseRightDown(wxMouseEvent& event) { onMouseDown(event); } - virtual void onMouseRightUp (wxMouseEvent& event) { onMouseUp (event); } + void onMouseLeftDown (wxMouseEvent& event) override { onMouseDown(event); } + void onMouseLeftUp (wxMouseEvent& event) override { onMouseUp (event); } + void onMouseRightDown(wxMouseEvent& event) override { onMouseDown(event); } + void onMouseRightUp (wxMouseEvent& event) override { onMouseUp (event); } - virtual void onMouseLeftDouble(wxMouseEvent& event) + void onMouseLeftDouble(wxMouseEvent& event) override { const wxPoint absPos = refParent().CalcUnscrolledPosition(event.GetPosition()); const auto row = rowLabelWin_.getRowAtPos(absPos.y); //return -1 for invalid position; >= rowCount if out of range @@ -1058,14 +1058,14 @@ private: event.Skip(); //allow changing focus } - virtual void onMouseCaptureLost(wxMouseCaptureLostEvent& event) + void onMouseCaptureLost(wxMouseCaptureLostEvent& event) override { activeSelection.reset(); Refresh(); //event.Skip(); -> we DID handle it! } - virtual void onMouseMovement(wxMouseEvent& event) + void onMouseMovement(wxMouseEvent& event) override { if (activeSelection) activeSelection->evalMousePos(); //eval on both mouse movement + timer event! @@ -1089,7 +1089,7 @@ private: event.Skip(); } - virtual void onFocus(wxFocusEvent& event) { Refresh(); event.Skip(); } + void onFocus(wxFocusEvent& event) override { Refresh(); event.Skip(); } class MouseSelection : private wxEvtHandler { @@ -1188,7 +1188,7 @@ private: const std::int64_t ticksPerSec_; }; - virtual void ScrollWindow(int dx, int dy, const wxRect* rect) + void ScrollWindow(int dx, int dy, const wxRect* rect) override { wxWindow::ScrollWindow(dx, dy, rect); rowLabelWin_.ScrollWindow(0, dy, rect); @@ -1678,7 +1678,7 @@ void Grid::setColumnConfig(const std::vector& attr) std::vector visCols; for (const ColumnAttribute& ca : attr) if (ca.visible_) - visCols.push_back(VisibleColumn(ca.type_, ca.offset_, ca.stretch_)); + visCols.emplace_back(ca.type_, ca.offset_, ca.stretch_); //"ownership" of visible columns is now within Grid visibleCols = visCols; @@ -2202,7 +2202,7 @@ std::vector Grid::getColWidths(int mainWinWidth) const //eval else width = std::max(width, 0); //support smaller width than COLUMN_MIN_WIDTH if set via configuration - output.push_back(ColumnWidth(vc.type_, width)); + output.emplace_back(vc.type_, width); } return output; } diff --git a/wx+/grid.h b/wx+/grid.h index 304932ac..1ac75aa5 100644 --- a/wx+/grid.h +++ b/wx+/grid.h @@ -39,7 +39,7 @@ extern const wxEventType EVENT_GRID_SELECT_RANGE; //generates: GridRangeSelectEv struct GridClickEvent : public wxMouseEvent { GridClickEvent(wxEventType et, const wxMouseEvent& me, ptrdiff_t row, ColumnType colType) : wxMouseEvent(me), row_(row), colType_(colType) { SetEventType(et); } - virtual wxEvent* Clone() const { return new GridClickEvent(*this); } + wxEvent* Clone() const override { return new GridClickEvent(*this); } const ptrdiff_t row_; //-1 for invalid position, >= rowCount if out of range const ColumnType colType_; //may be DUMMY_COLUMN_TYPE @@ -48,7 +48,7 @@ struct GridClickEvent : public wxMouseEvent struct GridColumnResizeEvent : public wxCommandEvent { GridColumnResizeEvent(int offset, ColumnType colType) : wxCommandEvent(EVENT_GRID_COL_RESIZE), colType_(colType), offset_(offset) {} - virtual wxEvent* Clone() const { return new GridColumnResizeEvent(*this); } + wxEvent* Clone() const override { return new GridColumnResizeEvent(*this); } const ColumnType colType_; const int offset_; @@ -57,7 +57,7 @@ struct GridColumnResizeEvent : public wxCommandEvent struct GridRangeSelectEvent : public wxCommandEvent { GridRangeSelectEvent(size_t rowFirst, size_t rowLast, bool positive) : wxCommandEvent(EVENT_GRID_SELECT_RANGE), positive_(positive), rowFirst_(rowFirst), rowLast_(rowLast) { assert(rowFirst <= rowLast); } - virtual wxEvent* Clone() const { return new GridRangeSelectEvent(*this); } + wxEvent* Clone() const override { return new GridRangeSelectEvent(*this); } const bool positive_; //"false" when clearing selection! const size_t rowFirst_; //selected range: [rowFirst_, rowLast_) @@ -92,10 +92,10 @@ public: //grid area virtual wxString getValue(size_t row, ColumnType colType) const = 0; - virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation - virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected); // - virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType); //must correspond to renderCell()! - virtual wxString getToolTip(size_t row, ColumnType colType) const { return wxString(); } + virtual void renderRowBackgound(wxDC& dc, const wxRect& rect, size_t row, bool enabled, bool selected); //default implementation + virtual void renderCell (wxDC& dc, const wxRect& rect, size_t row, ColumnType colType, bool enabled, bool selected); // + virtual int getBestSize (wxDC& dc, size_t row, ColumnType colType ); //must correspond to renderCell()! + virtual wxString getToolTip (size_t row, ColumnType colType) const { return wxString(); } //label area virtual wxString getColumnLabel(ColumnType colType) const = 0; @@ -191,8 +191,8 @@ public: void scrollTo(size_t row); - virtual void Refresh(bool eraseBackground = true, const wxRect* rect = nullptr); - virtual bool Enable( bool enable = true) { Refresh(); return wxScrolledWindow::Enable(enable); } + void Refresh(bool eraseBackground = true, const wxRect* rect = nullptr) override; + bool Enable( bool enable = true) override { Refresh(); return wxScrolledWindow::Enable(enable); } //############################################################################################################ private: @@ -208,10 +208,10 @@ private: void redirectRowLabelEvent(wxMouseEvent& event); - virtual wxSize GetSizeAvailableForScrollTarget(const wxSize& size); //required since wxWidgets 2.9 if SetTargetWindow() is used + wxSize GetSizeAvailableForScrollTarget(const wxSize& size) override; //required since wxWidgets 2.9 if SetTargetWindow() is used #if defined ZEN_WIN || defined ZEN_MAC - virtual void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh); //get rid of scrollbars, but preserve scrolling behavior! + void SetScrollbar(int orientation, int position, int thumbSize, int range, bool refresh) override; //get rid of scrollbars, but preserve scrolling behavior! #endif int getBestColumnSize(size_t col) const; //return -1 on error diff --git a/wx+/image_resources.cpp b/wx+/image_resources.cpp index 3c471e2c..062ad88c 100644 --- a/wx+/image_resources.cpp +++ b/wx+/image_resources.cpp @@ -84,7 +84,7 @@ void GlobalResources::init(const Zstring& filepath) //generic image loading if (endsWith(name, L".png")) - bitmaps.insert(std::make_pair(name, wxImage(streamIn, wxBITMAP_TYPE_PNG))); + bitmaps.emplace(name, wxImage(streamIn, wxBITMAP_TYPE_PNG)); else if (endsWith(name, L".gif")) loadAnimFromZip(streamIn, anims[name]); } diff --git a/wx+/image_tools.cpp b/wx+/image_tools.cpp index d36fb2fa..b1732032 100644 --- a/wx+/image_tools.cpp +++ b/wx+/image_tools.cpp @@ -156,7 +156,7 @@ wxImage zen::createImageFromText(const wxString& text, const wxFont& font, const dc.SetTextBackground(*wxWHITE); // dc.SetFont(font); - assert(!contains(text, L"&")); //accelerator keys not supported here; see also getTextExtent() + assert(!contains(text, L"&")); //accelerator keys not supported here; see also getTextExtent() wxString textFmt = replaceCpy(text, L"&", L"", false); //for some reason wxDC::DrawText messes up "weak" bidi characters even when wxLayout_RightToLeft is set! (--> arrows in hebrew/arabic) diff --git a/wx+/popup_dlg.cpp b/wx+/popup_dlg.cpp index dff6bdb0..ff0bf125 100644 --- a/wx+/popup_dlg.cpp +++ b/wx+/popup_dlg.cpp @@ -149,8 +149,8 @@ public: } private: - virtual void OnClose (wxCloseEvent& event) override { EndModal(static_cast(ConfirmationButton3::CANCEL)); } - virtual void OnCancel(wxCommandEvent& event) override { EndModal(static_cast(ConfirmationButton3::CANCEL)); } + void OnClose (wxCloseEvent& event) override { EndModal(static_cast(ConfirmationButton3::CANCEL)); } + void OnCancel(wxCommandEvent& event) override { EndModal(static_cast(ConfirmationButton3::CANCEL)); } void OnKeyPressed(wxKeyEvent& event) { @@ -163,14 +163,14 @@ private: event.Skip(); } - virtual void OnButtonAffirmative(wxCommandEvent& event) override + void OnButtonAffirmative(wxCommandEvent& event) override { if (checkBoxValue_) * checkBoxValue_ = m_checkBoxCustom->GetValue(); EndModal(static_cast(ConfirmationButton3::DO_IT)); } - virtual void OnButtonNegative(wxCommandEvent& event) override + void OnButtonNegative(wxCommandEvent& event) override { if (checkBoxValue_) * checkBoxValue_ = m_checkBoxCustom->GetValue(); @@ -243,7 +243,7 @@ public: } private: - virtual void OnCheckBoxClick(wxCommandEvent& event) override { updateGui(); event.Skip(); } + void OnCheckBoxClick(wxCommandEvent& event) override { updateGui(); event.Skip(); } void updateGui() { diff --git a/wx+/popup_dlg_generated.cpp b/wx+/popup_dlg_generated.cpp index a0a4cd5d..c3d50a70 100644 --- a/wx+/popup_dlg_generated.cpp +++ b/wx+/popup_dlg_generated.cpp @@ -11,79 +11,79 @@ PopupDialogGenerated::PopupDialogGenerated( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style ) { - this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); - this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); - - wxBoxSizer* bSizer24; - bSizer24 = new wxBoxSizer( wxVERTICAL ); - - m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); - m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); - - wxBoxSizer* bSizer165; - bSizer165 = new wxBoxSizer( wxHORIZONTAL ); - - m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 ); - - wxBoxSizer* bSizer16; - bSizer16 = new wxBoxSizer( wxVERTICAL ); - - m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextMain->Wrap( -1 ); - bSizer16->Add( m_staticTextMain, 0, wxTOP|wxBOTTOM|wxRIGHT, 15 ); - - m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); - bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 ); - - - bSizer165->Add( bSizer16, 1, wxEXPAND, 5 ); - - - m_panel33->SetSizer( bSizer165 ); - m_panel33->Layout(); - bSizer165->Fit( m_panel33 ); - bSizer24->Add( m_panel33, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizer24->Add( m_staticline6, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); - - wxBoxSizer* bSizer25; - bSizer25 = new wxBoxSizer( wxVERTICAL ); - - m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); - - bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); - - m_buttonAffirmative = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonAffirmative, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_buttonNegative = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonNegative, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); - - m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); - bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); - - - bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); - - - bSizer24->Add( bSizer25, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); - - - this->SetSizer( bSizer24 ); - this->Layout(); - bSizer24->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) ); - m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this ); - m_buttonAffirmative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAffirmative ), NULL, this ); - m_buttonNegative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonNegative ), NULL, this ); - m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this ); + this->SetSizeHints( wxSize( -1,-1 ), wxDefaultSize ); + this->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) ); + + wxBoxSizer* bSizer24; + bSizer24 = new wxBoxSizer( wxVERTICAL ); + + m_panel33 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + m_panel33->SetBackgroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) ); + + wxBoxSizer* bSizer165; + bSizer165 = new wxBoxSizer( wxHORIZONTAL ); + + m_bitmapMsgType = new wxStaticBitmap( m_panel33, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizer165->Add( m_bitmapMsgType, 0, wxALL, 10 ); + + wxBoxSizer* bSizer16; + bSizer16 = new wxBoxSizer( wxVERTICAL ); + + m_staticTextMain = new wxStaticText( m_panel33, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticTextMain->Wrap( -1 ); + bSizer16->Add( m_staticTextMain, 0, wxTOP|wxBOTTOM|wxRIGHT, 15 ); + + m_textCtrlTextDetail = new wxTextCtrl( m_panel33, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY|wxNO_BORDER ); + bSizer16->Add( m_textCtrlTextDetail, 1, wxEXPAND, 5 ); + + + bSizer165->Add( bSizer16, 1, wxEXPAND, 5 ); + + + m_panel33->SetSizer( bSizer165 ); + m_panel33->Layout(); + bSizer165->Fit( m_panel33 ); + bSizer24->Add( m_panel33, 1, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + m_staticline6 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); + bSizer24->Add( m_staticline6, 0, wxEXPAND|wxALIGN_CENTER_HORIZONTAL, 5 ); + + wxBoxSizer* bSizer25; + bSizer25 = new wxBoxSizer( wxVERTICAL ); + + m_checkBoxCustom = new wxCheckBox( this, wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer25->Add( m_checkBoxCustom, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 ); + + bSizerStdButtons = new wxBoxSizer( wxHORIZONTAL ); + + m_buttonAffirmative = new wxButton( this, wxID_YES, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonAffirmative, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); + + m_buttonNegative = new wxButton( this, wxID_NO, _("dummy"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonNegative, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); + + m_buttonCancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxSize( -1,-1 ), 0 ); + bSizerStdButtons->Add( m_buttonCancel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 ); + + + bSizer25->Add( bSizerStdButtons, 0, wxALIGN_RIGHT, 5 ); + + + bSizer24->Add( bSizer25, 0, wxALIGN_CENTER_HORIZONTAL|wxEXPAND, 5 ); + + + this->SetSizer( bSizer24 ); + this->Layout(); + bSizer24->Fit( this ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( PopupDialogGenerated::OnClose ) ); + m_checkBoxCustom->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCheckBoxClick ), NULL, this ); + m_buttonAffirmative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonAffirmative ), NULL, this ); + m_buttonNegative->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnButtonNegative ), NULL, this ); + m_buttonCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PopupDialogGenerated::OnCancel ), NULL, this ); } PopupDialogGenerated::~PopupDialogGenerated() diff --git a/wx+/popup_dlg_generated.h b/wx+/popup_dlg_generated.h index ad1bc51c..b0397f1e 100644 --- a/wx+/popup_dlg_generated.h +++ b/wx+/popup_dlg_generated.h @@ -37,35 +37,35 @@ /////////////////////////////////////////////////////////////////////////////// /// Class PopupDialogGenerated /////////////////////////////////////////////////////////////////////////////// -class PopupDialogGenerated : public wxDialog +class PopupDialogGenerated : public wxDialog { - private: - - protected: - wxPanel* m_panel33; - wxStaticBitmap* m_bitmapMsgType; - wxStaticText* m_staticTextMain; - wxTextCtrl* m_textCtrlTextDetail; - wxStaticLine* m_staticline6; - wxCheckBox* m_checkBoxCustom; - wxBoxSizer* bSizerStdButtons; - wxButton* m_buttonAffirmative; - wxButton* m_buttonNegative; - wxButton* m_buttonCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } - virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); } - virtual void OnButtonAffirmative( wxCommandEvent& event ) { event.Skip(); } - virtual void OnButtonNegative( wxCommandEvent& event ) { event.Skip(); } - virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } - - - public: - - PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~PopupDialogGenerated(); - +private: + +protected: + wxPanel* m_panel33; + wxStaticBitmap* m_bitmapMsgType; + wxStaticText* m_staticTextMain; + wxTextCtrl* m_textCtrlTextDetail; + wxStaticLine* m_staticline6; + wxCheckBox* m_checkBoxCustom; + wxBoxSizer* bSizerStdButtons; + wxButton* m_buttonAffirmative; + wxButton* m_buttonNegative; + wxButton* m_buttonCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnCheckBoxClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonAffirmative( wxCommandEvent& event ) { event.Skip(); } + virtual void OnButtonNegative( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); } + + +public: + + PopupDialogGenerated( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("dummy"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~PopupDialogGenerated(); + }; #endif //__POPUP_DLG_GENERATED_H__ diff --git a/wx+/rtl.h b/wx+/rtl.h index 4caf19ea..0ec84524 100644 --- a/wx+/rtl.h +++ b/wx+/rtl.h @@ -55,7 +55,7 @@ void drawRtlImpl(wxDC& dc, const wxRect& rect, std::unique_ptr& buffer if (dc.GetLayoutDirection() == wxLayout_RightToLeft) { if (!buffer || buffer->GetWidth() != rect.width || buffer->GetHeight() < rect.height) //[!] since we do a mirror, width needs to match exactly! - buffer.reset(new wxBitmap(rect.width, rect.height)); + buffer = zen::make_unique(rect.width, rect.height); wxMemoryDC memDc(*buffer); memDc.Blit(wxPoint(0, 0), rect.GetSize(), &dc, rect.GetTopLeft()); //blit in: background is mirrored due to memDc, dc having different layout direction! diff --git a/wx+/std_button_layout.h b/wx+/std_button_layout.h index b7b1af3a..b5e30472 100644 --- a/wx+/std_button_layout.h +++ b/wx+/std_button_layout.h @@ -91,7 +91,7 @@ void setStandardButtonLayout(wxBoxSizer& sizer, const StdButtons& buttons) { assert(btn->GetMinSize().GetHeight() == -1); //let OS or this routine do the sizing! note: OS X does not allow changing the (visible!) button height! #if defined ZEN_WIN || defined ZEN_LINUX - const int defaultHeight = wxButton::GetDefaultSize().GetHeight(); //buffered by wxWidgets + const int defaultHeight = wxButton::GetDefaultSize().GetHeight(); //buffered by wxWidgets btn->SetMinSize(wxSize(-1, std::max(defaultHeight, 30))); //default button height is much too small => increase! #endif diff --git a/wx+/zlib_wrap.h b/wx+/zlib_wrap.h index 4cdc96b3..c271276f 100644 --- a/wx+/zlib_wrap.h +++ b/wx+/zlib_wrap.h @@ -85,12 +85,12 @@ BinContainer decompress(const BinContainer& stream) //throw ZlibInternalError std::copy(&*stream.begin(), &*stream.begin() + sizeof(uncompressedSize), reinterpret_cast(&uncompressedSize)); - //attention: contOut MUST NOT be empty! Else it will pass a nullptr to zlib_decompress() => Z_STREAM_ERROR although "uncompressedSize == 0"!!! - //secondary bug: don't dereference iterator into empty container! - if (uncompressedSize == 0) //cannot be 0: compress() directly maps empty -> empty container skipping zlib! - throw ZlibInternalError(); + //attention: contOut MUST NOT be empty! Else it will pass a nullptr to zlib_decompress() => Z_STREAM_ERROR although "uncompressedSize == 0"!!! + //secondary bug: don't dereference iterator into empty container! + if (uncompressedSize == 0) //cannot be 0: compress() directly maps empty -> empty container skipping zlib! + throw ZlibInternalError(); - try + try { contOut.resize(static_cast(uncompressedSize)); //throw std::bad_alloc } diff --git a/zen/assert_static.h b/zen/assert_static.h deleted file mode 100644 index 17d5c370..00000000 --- a/zen/assert_static.h +++ /dev/null @@ -1,43 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef ASSERTSTATIC_H_INCLUDED -#define ASSERTSTATIC_H_INCLUDED - -/* -//compile time assert based on Loki (http://loki-lib.sourceforge.net) - -#ifdef NDEBUG - -#define assert_static(x) //((void)0) -> leads to error when seen in namespace scope! - -#else // debugging enabled -namespace static_check_impl -{ -template -struct CompileTimeError; - -template<> -struct CompileTimeError {}; -} - -#define LOKI_CONCAT(X, Y) LOKI_CONCAT_SUB(X, Y) -#define LOKI_CONCAT_SUB(X, Y) X ## Y - -#define assert_static(expr) \ - enum { LOKI_CONCAT(loki_enum_dummy_value, __LINE__) = sizeof(StaticCheckImpl::CompileTimeError(expr) >) } - -// #define assert_static(expr) \ -// { Loki::CompileTimeError<((expr) != 0)> Static_Assert_Has_Failed; (void)Static_Assert_Has_Failed; } - -#endif -*/ - -//C++11: please get rid of this pointless string literal requirement! -#define assert_static(X) \ - static_assert(X, "Static assert has failed!"); - -#endif //ASSERTSTATIC_H_INCLUDED diff --git a/zen/basic_math.h b/zen/basic_math.h index 69e861be..41f42dbe 100644 --- a/zen/basic_math.h +++ b/zen/basic_math.h @@ -145,7 +145,7 @@ T clampCpy(const T& val, const T& minVal, const T& maxVal) } template inline -void clamp(T& val, const T& minVal, const T& maxVal) +void clamp(T& val, const T& minVal, const T& maxVal) { assert(minVal <= maxVal); if (val < minVal) diff --git a/zen/dir_watcher.cpp b/zen/dir_watcher.cpp index 3751e5dd..9a685fc5 100644 --- a/zen/dir_watcher.cpp +++ b/zen/dir_watcher.cpp @@ -40,7 +40,7 @@ public: boost::lock_guard dummy(lockAccess); if (bytesWritten == 0) //according to docu this may happen in case of internal buffer overflow: report some "dummy" change - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_CREATE, L"Overflow.")); + changedFiles.emplace_back(DirWatcher::ACTION_CREATE, L"Overflow."); else { const char* bufPos = &buffer[0]; @@ -67,14 +67,14 @@ public: { case FILE_ACTION_ADDED: case FILE_ACTION_RENAMED_NEW_NAME: //harmonize with "move" which is notified as "create + delete" - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_CREATE, fullpath)); + changedFiles.emplace_back(DirWatcher::ACTION_CREATE, fullpath); break; case FILE_ACTION_REMOVED: case FILE_ACTION_RENAMED_OLD_NAME: - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_DELETE, fullpath)); + changedFiles.emplace_back(DirWatcher::ACTION_DELETE, fullpath); break; case FILE_ACTION_MODIFIED: - changedFiles.push_back(DirWatcher::Entry(DirWatcher::ACTION_UPDATE, fullpath)); + changedFiles.emplace_back(DirWatcher::ACTION_UPDATE, fullpath); break; } }(); @@ -193,8 +193,8 @@ public: nullptr); //__in_opt LPCTSTR lpName if (overlapped.hEvent == nullptr) { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"CreateEvent", lastError), lastError); + const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"CreateEvent", ec), ec); } ZEN_ON_SCOPE_EXIT(::CloseHandle(overlapped.hEvent)); @@ -213,8 +213,8 @@ public: &overlapped, // __inout_opt LPOVERLAPPED lpOverlapped, nullptr)) // __in_opt LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"ReadDirectoryChangesW", lastError), lastError); + const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"ReadDirectoryChangesW", ec), ec); } //async I/O is a resource that needs to be guarded since it will write to local variable "buffer"! @@ -236,9 +236,9 @@ public: &bytesWritten, //__out LPDWORD lpNumberOfBytesTransferred, false)) //__in BOOL bWait { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - if (lastError != ERROR_IO_INCOMPLETE) - return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"GetOverlappedResult", lastError), lastError); + const DWORD ec = ::GetLastError(); //copy before directly or indirectly making other system calls! + if (ec != ERROR_IO_INCOMPLETE) + return shared_->reportError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(dirpathPf)), formatSystemError(L"GetOverlappedResult", ec), ec); //execute asynchronous procedure calls (APC) queued on this thread ::SleepEx(50, // __in DWORD dwMilliseconds, @@ -287,7 +287,7 @@ public: bool finished() const { return operationComplete; } private: - virtual void onRequestRemoval(HANDLE hnd) + void onRequestRemoval(HANDLE hnd) override { //must release hDir immediately => stop monitoring! if (worker_.joinable()) //= join() precondition: play safe; can't trust Windows to only call-back once @@ -300,7 +300,7 @@ private: removalRequested = true; } //don't throw! - virtual void onRemovalFinished(HANDLE hnd, bool successful) { operationComplete = true; } //throw()! + void onRemovalFinished(HANDLE hnd, bool successful) override { operationComplete = true; } //throw()! boost::thread& worker_; bool removalRequested; @@ -320,13 +320,13 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError - pimpl_(new Pimpl) + pimpl_(zen::make_unique()) { pimpl_->shared = std::make_shared(); pimpl_->dirpath = directory; ReadChangesAsync reader(directory, pimpl_->shared); //throw FileError - pimpl_->volRemoval.reset(new HandleVolumeRemoval(reader.getDirHandle(), pimpl_->worker)); //throw FileError + pimpl_->volRemoval = zen::make_unique(reader.getDirHandle(), pimpl_->worker); //throw FileError pimpl_->worker = boost::thread(std::move(reader)); } @@ -360,7 +360,7 @@ std::vector DirWatcher::getChanges(const std::functiondirpath)); //report removal as change to main directory + output.emplace_back(ACTION_DELETE, pimpl_->dirpath); //report removal as change to main directory } else //the normal case... pimpl_->shared->fetchChanges(output); //throw FileError @@ -376,15 +376,15 @@ class DirsOnlyTraverser : public zen::TraverseCallback public: DirsOnlyTraverser(std::vector& dirs) : dirs_(dirs) {} - virtual void onFile (const Zchar* shortName, const Zstring& filepath, const FileInfo& details) {} - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) { return LINK_SKIP; } - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) + void onFile (const Zchar* shortName, const Zstring& filepath, const FileInfo& details ) override {} + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override { return LINK_SKIP; } + TraverseCallback* onDir (const Zchar* shortName, const Zstring& dirpath ) override { dirs_.push_back(dirpath); return this; } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); } - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); } + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { throw FileError(msg); } private: std::vector& dirs_; @@ -403,7 +403,7 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError - pimpl_(new Pimpl) + pimpl_(zen::make_unique()) { //get all subdirectories Zstring dirpathFmt = directory; @@ -449,9 +449,15 @@ DirWatcher::DirWatcher(const Zstring& directory) : //throw FileError IN_MOVED_TO | IN_MOVE_SELF); if (wd == -1) - throwFileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), L"inotify_add_watch", getLastError()); + { + const auto ec = getLastError(); + if (ec == ENOSPC) //fix misleading system message "No space left on device" + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), formatSystemError(L"inotify_add_watch", ec, L"The user limit on the total number of inotify watches was reached or the kernel failed to allocate a needed resource.")); + + throw FileError(replaceCpy(_("Cannot monitor directory %x."), L"%x", fmtFileName(subdir)), formatSystemError(L"inotify_add_watch", ec)); + } - pimpl_->watchDescrs.insert(std::make_pair(wd, appendSeparator(subdir))); + pimpl_->watchDescrs.emplace(wd, appendSeparator(subdir)); } guardDescr.dismiss(); @@ -502,15 +508,15 @@ std::vector DirWatcher::getChanges(const std::function irrelevant! //kFSEventStreamEventFlagHistoryDone -> not expected due to kFSEventStreamEventIdSinceNow below @@ -571,7 +577,7 @@ struct DirWatcher::Pimpl DirWatcher::DirWatcher(const Zstring& directory) : - pimpl_(new Pimpl) + pimpl_(zen::make_unique()) { CFStringRef dirpathCf = osx::createCFString(directory.c_str()); //returns nullptr on error if (!dirpathCf) diff --git a/zen/dst_hack.cpp b/zen/dst_hack.cpp deleted file mode 100644 index b38f73b4..00000000 --- a/zen/dst_hack.cpp +++ /dev/null @@ -1,368 +0,0 @@ -#include "dst_hack.h" -#include -#include "basic_math.h" -#include "long_path_prefix.h" -#include "utf.h" -#include "assert_static.h" -#include "int64.h" -#include "file_error.h" -#include "dll.h" - -using namespace zen; - - -namespace -{ -//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points) -Zstring getVolumeName(const Zstring& filepath) -{ - //this call is expensive: ~1.5 ms! - // if (!::GetVolumePathName(filepath.c_str(), //__in LPCTSTR lpszFileName, - // fsName, //__out LPTSTR lpszVolumePathName, - // BUFFER_SIZE)) //__in DWORD cchBufferLength - // ... - // Zstring volumePath = appendSeparator(fsName); - - const Zstring nameFmt = appendSeparator(removeLongPathPrefix(filepath)); //throw() - - if (startsWith(nameFmt, Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\" - { - size_t nameSize = nameFmt.size(); - const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2); - if (posFirstSlash != Zstring::npos) - { - nameSize = posFirstSlash + 1; - const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1); - if (posSecondSlash != Zstring::npos) - nameSize = posSecondSlash + 1; - } - return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash! - } - else //local path: "C:\Folder\" - { - const size_t pos = nameFmt.find(Zstr(":\\")); - if (pos == 1) //expect single letter volume - return Zstring(nameFmt.c_str(), 3); - } - - return Zstring(); -} -} - - -bool dst::isFatDrive(const Zstring& filepath) //throw() -{ - const Zstring volumePath = getVolumeName(filepath); - if (volumePath.empty()) - return false; - - const DWORD bufferSize = MAX_PATH + 1; - wchar_t fsName[bufferSize] = {}; - - //suprisingly fast: ca. 0.03 ms per call! - if (!::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName, - nullptr, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - fsName, //__out LPTSTR lpFileSystemNameBuffer, - bufferSize)) //__in DWORD nFileSystemNameSize - { - assert(false); //shouldn't happen - return false; - } - //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) - return fsName == Zstring(L"FAT") || - fsName == Zstring(L"FAT32"); -} - - -/* -bool dst::isFatDrive(HANDLE hFile) //throw() -{ -Requires Windows Vista! - //dynamically load windows API function - typedef BOOL (WINAPI* GetVolumeInformationByHandleWFunc)(HANDLE hFile, - LPWSTR lpVolumeNameBuffer, - DWORD nVolumeNameSize, - LPDWORD lpVolumeSerialNumber, - LPDWORD lpMaximumComponentLength, - LPDWORD lpFileSystemFlags, - LPWSTR lpFileSystemNameBuffer, - DWORD nFileSystemNameSize); - - const SysDllFun getVolumeInformationByHandle(L"kernel32.dll", "GetVolumeInformationByHandleW"); - if (!getVolumeInformationByHandle) - { - assert(false); - return false; - } - - const size_t BUFFER_SIZE = MAX_PATH + 1; - wchar_t fsName[BUFFER_SIZE]; - - if (!getVolumeInformationByHandle(hFile, //__in HANDLE hFile, - nullptr, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - nullptr, //__out_opt LPDWORD lpFileSystemFlags, - fsName, //__out LPTSTR lpFileSystemNameBuffer, - BUFFER_SIZE)) //__in DWORD nFileSystemNameSize - { - assert(false); //shouldn't happen - return false; - } - //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) - return fsName == Zstring(L"FAT") || - fsName == Zstring(L"FAT32"); -} -*/ - - -namespace -{ -//convert std::uint64_t and std::int64_t to FILETIME -inline -FILETIME toFiletime(std::uint64_t number) -{ - ULARGE_INTEGER cvt = {}; - cvt.QuadPart = number; - - const FILETIME output = { cvt.LowPart, cvt.HighPart }; - return output; -} - -inline -FILETIME toFiletime(std::int64_t number) -{ - return toFiletime(static_cast(number)); -} - -inline -std::uint64_t toUInt64(const FILETIME& fileTime) -{ - return get64BitUInt(fileTime.dwLowDateTime, fileTime.dwHighDateTime); -} - - -inline -std::int64_t toInt64(const FILETIME& fileTime) -{ - return get64BitUInt(fileTime.dwLowDateTime, fileTime.dwHighDateTime); //convert unsigned to signed in return -} - - -inline -FILETIME utcToLocal(const FILETIME& utcTime) //throw std::runtime_error -{ - //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC: - FILETIME localTime = {}; - if (!::FileTimeToLocalFileTime( - &utcTime, //__in const FILETIME *lpFileTime, - &localTime)) //__out LPFILETIME lpLocalFileTime - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - throw std::runtime_error(utfCvrtTo(_("Conversion error:") + L" FILETIME -> local FILETIME: " + L"(" + - L"High: " + numberTo(utcTime.dwHighDateTime) + L" " + - L"Low: " + numberTo(utcTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"FileTimeToLocalFileTime", lastError))); - } - return localTime; -} - - -inline -FILETIME localToUtc(const FILETIME& localTime) //throw std::runtime_error -{ - //treat binary local time representation (which is invariant under DST time zone shift) as logical UTC: - FILETIME utcTime = {}; - if (!::LocalFileTimeToFileTime( - &localTime, //__in const FILETIME *lpLocalFileTime, - &utcTime)) //__out LPFILETIME lpFileTime - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - throw std::runtime_error(utfCvrtTo(_("Conversion error:") + L" local FILETIME -> FILETIME: " + L"(" + - L"High: " + numberTo(localTime.dwHighDateTime) + L" " + - L"Low: " + numberTo(localTime.dwLowDateTime) + L") " + L"\n\n" + formatSystemError(L"LocalFileTimeToFileTime", lastError))); - } - return utcTime; -} - - -//struct FILETIME {DWORD dwLowDateTime; DWORD dwHighDateTime;}; -const FILETIME FAT_MIN_TIME = { 13374976, 27846544 }; //1980 \ both are valid max/min FAT dates for 2 second precision -const FILETIME FAT_MAX_TIME = { 14487552, 37251238 }; //2107 / - -//http://en.wikipedia.org/wiki/File_Allocation_Table -const size_t PRECISION_WRITE_TIME = 20000000; //number of 100 ns per step -> 2 s -const size_t PRECISION_CREATE_TIME = 100000; // -> 1/100 s - -/* -Number of bits of information in create time: ln_2((FAT_MAX_TIME - FAT_MIN_TIME) / PRECISION_CREATE_TIME) = 38.55534023 -Number of bits of information in write time: 30.91148404 -*/ -//total size available to store data: -const size_t CREATE_TIME_INFO_BITS = 38; -// I. indicator that offset in II) is present -const size_t INDICATOR_EXISTING_BITS = 1; -// II. local<->UTC time offset -const size_t UTC_LOCAL_OFFSET_BITS = 7; -// III. indicator that offset in II) corresponds to current local write time (this could be a hash of the write time) -const size_t WRITE_TIME_HASH_BITS = CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS - UTC_LOCAL_OFFSET_BITS; - - -template inline -FILETIME encodeRawInformation(std::uint64_t rawInfo) -{ - rawInfo *= precision; - rawInfo += toUInt64(FAT_MIN_TIME); - - assert(rawInfo <= toUInt64(FAT_MAX_TIME)); - return toFiletime(rawInfo); -} - - -template inline -std::uint64_t extractRawInformation(const FILETIME& createTime) -{ - assert(toUInt64(FAT_MIN_TIME) <= toUInt64(createTime)); - assert(toUInt64(createTime) <= toUInt64(FAT_MAX_TIME)); - - //FAT create time ranges from 1980 - 2107 (2^7 years) with 1/100 seconds precision - std::uint64_t rawInfo = toUInt64(createTime); - - rawInfo -= toUInt64(FAT_MIN_TIME); - rawInfo /= precision; //reduce precision (FILETIME has unit 10^-7 s) - - assert(toUInt64(encodeRawInformation(rawInfo)) == toUInt64(createTime)); //must be reversible - return rawInfo; -} - - -//convert write time to it's minimal representation (no restriction to FAT range "1980 - 2107") -inline -std::uint64_t extractRawWriteTime(const FILETIME& writeTime) -{ - std::uint64_t rawInfo = toUInt64(writeTime); - assert(rawInfo % PRECISION_WRITE_TIME == 0U); - rawInfo /= PRECISION_WRITE_TIME; //reduce precision (FILETIME has unit 10^-7 s) - return rawInfo; -} - - -//files with different resolution than 2 seconds are rounded up when written to FAT -inline -FILETIME roundToFatWriteTime(const FILETIME& writeTime) -{ - std::uint64_t rawData = toUInt64(writeTime); - - if (rawData % PRECISION_WRITE_TIME != 0U) - rawData += PRECISION_WRITE_TIME; - - rawData /= PRECISION_WRITE_TIME; - rawData *= PRECISION_WRITE_TIME; - return toFiletime(rawData); -} - - -//get 7-bit value representing time shift in number of quarter-hours -std::bitset getUtcLocalShift() -{ - FILETIME utcTime = FAT_MIN_TIME; - utcTime.dwHighDateTime += 5000000; //some arbitrary valid time - - const FILETIME localTime = utcToLocal(utcTime); - - const int timeShiftSec = static_cast((toInt64(localTime) - toInt64(utcTime)) / 10000000); //time shift in seconds - - const int timeShiftQuarter = timeShiftSec / (60 * 15); //time shift in quarter-hours - - const int absValue = numeric::abs(timeShiftQuarter); //MSVC C++0x bug: std::bitset<>(unsigned long) is ambiguous - - if (std::bitset < UTC_LOCAL_OFFSET_BITS - 1 > (absValue).to_ulong() != static_cast(absValue) || //time shifts that big shouldn't be possible! - timeShiftSec % (60 * 15) != 0) //all known time shift have at least 15 minute granularity! - { - const std::wstring errorMsg = _("Conversion error:") + L" Unexpected UTC <-> local time shift: " + L"(" + numberTo(timeShiftSec) + L")"; - throw std::runtime_error(utfCvrtTo(errorMsg)); - } - - std::bitset output(absValue); - output[UTC_LOCAL_OFFSET_BITS - 1] = timeShiftQuarter < 0; //sign bit - return output; -} - - -//get time-zone shift in seconds -inline -int convertUtcLocalShift(std::bitset rawShift) -{ - const bool hasSign = rawShift[UTC_LOCAL_OFFSET_BITS - 1]; - rawShift[UTC_LOCAL_OFFSET_BITS - 1] = false; - - assert_static(UTC_LOCAL_OFFSET_BITS <= sizeof(unsigned long) * 8); - return hasSign ? - static_cast(rawShift.to_ulong()) * 15 * 60 * -1 : - static_cast(rawShift.to_ulong()) * 15 * 60; -} -} - - -bool dst::fatHasUtcEncoded(const RawTime& rawTime) //"createTimeRaw" as retrieved by ::FindFirstFile() and ::GetFileAttributesEx(); throw std::runtime_error -{ - if (toUInt64(rawTime.createTimeRaw) < toUInt64(FAT_MIN_TIME) || - toUInt64(FAT_MAX_TIME) < toUInt64(rawTime.createTimeRaw)) - { - assert(false); //shouldn't be possible according to FAT specification - return false; - } - - const std::uint64_t rawInfo = extractRawInformation(utcToLocal(rawTime.createTimeRaw)); - - assert_static(WRITE_TIME_HASH_BITS == 30); - return (extractRawWriteTime(utcToLocal(rawTime.writeTimeRaw)) & 0x3FFFFFFFU) == (rawInfo & 0x3FFFFFFFU) && //ensure write time wasn't changed externally - rawInfo >> (CREATE_TIME_INFO_BITS - INDICATOR_EXISTING_BITS) == 1U; //extended data available -} - - -dst::RawTime dst::fatEncodeUtcTime(const FILETIME& writeTimeRealUtc) //throw std::runtime_error -{ - const FILETIME fatWriteTimeUtc = roundToFatWriteTime(writeTimeRealUtc); //writeTimeRealUtc may have incompatible precision (NTFS) - - //create time lets us store 40 bit of information - - //indicator that utc time is encoded -> hopefully results in a date long way in the future; but even if this bit is accidentally set, we still have the hash! - std::uint64_t data = 1U; - - const std::bitset utcShift = getUtcLocalShift(); - data <<= UTC_LOCAL_OFFSET_BITS; - data |= utcShift.to_ulong(); - - data <<= WRITE_TIME_HASH_BITS; - data |= extractRawWriteTime(utcToLocal(fatWriteTimeUtc)) & 0x3FFFFFFFU; //trim to last 30 bit of information - assert_static(WRITE_TIME_HASH_BITS == 30); - - const FILETIME encodedData = localToUtc(encodeRawInformation(data)); //localToUtc: make sure data is physically saved as FAT local time - assert(toUInt64(FAT_MIN_TIME) <= toUInt64(encodedData)); - assert(toUInt64(encodedData) <= toUInt64(FAT_MAX_TIME)); - - return RawTime(encodedData, fatWriteTimeUtc); //keep compatible with other applications, at least until DST shift actually occurs -} - - -FILETIME dst::fatDecodeUtcTime(const RawTime& rawTime) //return real UTC time; throw (std::runtime_error) -{ - if (!fatHasUtcEncoded(rawTime)) - return rawTime.writeTimeRaw; - - const std::uint64_t rawInfo = extractRawInformation(utcToLocal(rawTime.createTimeRaw)); - - const std::bitset rawShift(static_cast((rawInfo >> WRITE_TIME_HASH_BITS) & 0x7FU)); //static_cast: a shame MSC... "unsigned long long" should be supported instead! - assert_static(UTC_LOCAL_OFFSET_BITS == 7); - - const std::int64_t timeShiftSec = convertUtcLocalShift(rawShift); - const FILETIME writeTimeLocal = utcToLocal(rawTime.writeTimeRaw); - - const std::int64_t realUTC = toInt64(writeTimeLocal) - timeShiftSec * 10000000; - return toFiletime(realUTC); -} diff --git a/zen/dst_hack.h b/zen/dst_hack.h deleted file mode 100644 index 600107bb..00000000 --- a/zen/dst_hack.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef DST_HACK_H_INCLUDED -#define DST_HACK_H_INCLUDED - -#include "win.h" //includes "windows.h" -#include "zstring.h" -#include - - -namespace dst -{ -/* -Solve DST +-1h and time zone shift issues on FAT drives -------------------------------------------------------- -- (local) last write time is not touched! -- all additional metadata is encoded in local create time: - I. indicator that offset in II) is present - II. local<->UTC time offset - III. indicator that offset in II) corresponds to current local write time (a hash of local last write time) -*/ - -bool isFatDrive(const Zstring& fileName); //throw () - -//all subsequent functions may throw the std::runtime_error exception! - -struct RawTime //time as retrieved by ::FindFirstFile() and ::GetFileAttributesEx() -{ - RawTime(const FILETIME& create, const FILETIME& lastWrite) : createTimeRaw(create), writeTimeRaw(lastWrite) {} - FILETIME createTimeRaw; - FILETIME writeTimeRaw; -}; -//save UTC time resistant against DST/time zone shifts -bool fatHasUtcEncoded(const RawTime& rawTime); //throw std::runtime_error; as retrieved by ::FindFirstFile() and ::GetFileAttributesEx() - -RawTime fatEncodeUtcTime(const FILETIME& writeTimeRealUtc); //throw std::runtime_error -FILETIME fatDecodeUtcTime(const RawTime& rawTime); //throw std::runtime_error; return last write time in real UTC -} - -#endif // DST_HACK_H_INCLUDED diff --git a/zen/file_access.cpp b/zen/file_access.cpp new file mode 100644 index 00000000..c4768dd2 --- /dev/null +++ b/zen/file_access.cpp @@ -0,0 +1,2313 @@ +// ************************************************************************** +// * 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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#include "file_access.h" +#include +#include +#include +#include "int64.h" +#include "file_traverser.h" +#include "scope_guard.h" +#include "symlink_target.h" +#include "file_io.h" +#include "file_id_def.h" + +#ifdef ZEN_WIN +#include +#include "privilege.h" +#include "dll.h" +#include "long_path_prefix.h" +#include "win_ver.h" +#include "IFileOperation/file_op.h" + +#elif defined ZEN_LINUX +#include //statfs +#include //AT_SYMLINK_NOFOLLOW, UTIME_OMIT +#ifdef HAVE_SELINUX +#include +#endif + +#elif defined ZEN_MAC +#include //statfs +#endif + +#if defined ZEN_LINUX || defined ZEN_MAC +#include +#include //lutimes +#endif + +using namespace zen; + + +bool zen::fileExists(const Zstring& filepath) +{ + //symbolic links (broken or not) are also treated as existing files! +#ifdef ZEN_WIN + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str()); + if (attr != INVALID_FILE_ATTRIBUTES) + return (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat fileInfo = {}; + if (::stat(filepath.c_str(), &fileInfo) == 0) //follow symlinks! + return S_ISREG(fileInfo.st_mode); +#endif + return false; +} + + +bool zen::dirExists(const Zstring& dirpath) +{ + //symbolic links (broken or not) are also treated as existing directories! +#ifdef ZEN_WIN + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirpath).c_str()); + if (attr != INVALID_FILE_ATTRIBUTES) + return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat dirInfo = {}; + if (::stat(dirpath.c_str(), &dirInfo) == 0) //follow symlinks! + return S_ISDIR(dirInfo.st_mode); +#endif + return false; +} + + +bool zen::symlinkExists(const Zstring& linkname) +{ +#ifdef ZEN_WIN + WIN32_FIND_DATA linkInfo = {}; + const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &linkInfo); + if (searchHandle != INVALID_HANDLE_VALUE) + { + ::FindClose(searchHandle); + return isSymlink(linkInfo); + } + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat linkInfo = {}; + if (::lstat(linkname.c_str(), &linkInfo) == 0) + return S_ISLNK(linkInfo.st_mode); +#endif + return false; +} + + +bool zen::somethingExists(const Zstring& objname) +{ +#ifdef ZEN_WIN + const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str()); + if (attr != INVALID_FILE_ATTRIBUTES) + return true; + const DWORD lastError = ::GetLastError(); + + //handle obscure file permission problem where ::GetFileAttributes() fails with ERROR_ACCESS_DENIED or ERROR_SHARING_VIOLATION + //while parent directory traversal is successful: e.g. "C:\pagefile.sys" + if (lastError != ERROR_PATH_NOT_FOUND && //perf: short circuit for common "not existing" error codes + lastError != ERROR_FILE_NOT_FOUND && // + lastError != ERROR_BAD_NETPATH && // + lastError != ERROR_BAD_NET_NAME) // + { + WIN32_FIND_DATA fileInfo = {}; + const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(objname).c_str(), &fileInfo); + if (searchHandle != INVALID_HANDLE_VALUE) + { + ::FindClose(searchHandle); + return true; + } + } + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat fileInfo = {}; + if (::lstat(objname.c_str(), &fileInfo) == 0) + return true; +#endif + return false; +} + + +namespace +{ +#ifdef ZEN_WIN +//fast ::GetVolumePathName() clone: let's hope it's not too simple (doesn't honor mount points) +Zstring getVolumeNameFast(const Zstring& filepath) +{ + //this call is expensive: ~1.5 ms! + // if (!::GetVolumePathName(filepath.c_str(), //__in LPCTSTR lpszFileName, + // fsName, //__out LPTSTR lpszVolumePathName, + // BUFFER_SIZE)) //__in DWORD cchBufferLength + // ... + // Zstring volumePath = appendSeparator(fsName); + + const Zstring nameFmt = appendSeparator(removeLongPathPrefix(filepath)); //throw() + + if (startsWith(nameFmt, Zstr("\\\\"))) //UNC path: "\\ComputerName\SharedFolder\" + { + size_t nameSize = nameFmt.size(); + const size_t posFirstSlash = nameFmt.find(Zstr("\\"), 2); + if (posFirstSlash != Zstring::npos) + { + nameSize = posFirstSlash + 1; + const size_t posSecondSlash = nameFmt.find(Zstr("\\"), posFirstSlash + 1); + if (posSecondSlash != Zstring::npos) + nameSize = posSecondSlash + 1; + } + return Zstring(nameFmt.c_str(), nameSize); //include trailing backslash! + } + else //local path: "C:\Folder\" + { + const size_t pos = nameFmt.find(Zstr(":\\")); + if (pos == 1) //expect single letter volume + return Zstring(nameFmt.c_str(), 3); + } + + return Zstring(); +} + + +bool isFatDrive(const Zstring& filepath) //throw() +{ + const Zstring volumePath = getVolumeNameFast(filepath); + if (volumePath.empty()) + return false; + + const DWORD bufferSize = MAX_PATH + 1; + wchar_t fsName[bufferSize] = {}; + + //suprisingly fast: ca. 0.03 ms per call! + if (!::GetVolumeInformation(appendSeparator(volumePath).c_str(), //__in_opt LPCTSTR lpRootPathName, + nullptr, //__out LPTSTR lpVolumeNameBuffer, + 0, //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + nullptr, //__out_opt LPDWORD lpFileSystemFlags, + fsName, //__out LPTSTR lpFileSystemNameBuffer, + bufferSize)) //__in DWORD nFileSystemNameSize + { + assert(false); //shouldn't happen + return false; + } + //DST hack seems to be working equally well for FAT and FAT32 (in particular creation time has 10^-2 s precision as advertised) + return fsName == Zstring(L"FAT") || + fsName == Zstring(L"FAT32"); +} + + +//(try to) enhance error messages by showing which processes lock the file +Zstring getLockingProcessNames(const Zstring& filepath) //throw(), empty string if none found or error occurred +{ + if (vistaOrLater()) + { + using namespace fileop; + const DllFun getLockingProcesses(getDllName(), funName_getLockingProcesses); + const DllFun freeString (getDllName(), funName_freeString); + + if (getLockingProcesses && freeString) + if (const wchar_t* procList = getLockingProcesses(filepath.c_str())) + { + ZEN_ON_SCOPE_EXIT(freeString(procList)); + return procList; + } + } + return Zstring(); +} +#endif +} + + +std::uint64_t zen::getFilesize(const Zstring& filepath) //throw FileError +{ +#ifdef ZEN_WIN + { + WIN32_FIND_DATA fileInfo = {}; + const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filepath).c_str(), &fileInfo); + if (searchHandle == INVALID_HANDLE_VALUE) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"FindFirstFile", getLastError()); + ::FindClose(searchHandle); + + if (!isSymlink(fileInfo)) + return get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); + } + // WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; + // if (!::GetFileAttributesEx(applyLongPathPrefix(filepath).c_str(), //__in LPCTSTR lpFileName, + // GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, + // &sourceAttr)) //__out LPVOID lpFileInformation + + //open handle to target of symbolic link + const HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, + 0, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hFile == INVALID_HANDLE_VALUE) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"CreateFile", getLastError()); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); + + BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; + if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileInformationByHandle", getLastError()); + + return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat fileInfo = {}; + if (::stat(filepath.c_str(), &fileInfo) != 0) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"stat", getLastError()); + + return fileInfo.st_size; +#endif +} + + +std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError +{ +#ifdef ZEN_WIN + ULARGE_INTEGER bytesFree = {}; + if (!::GetDiskFreeSpaceEx(appendSeparator(path).c_str(), //__in_opt LPCTSTR lpDirectoryName, -> "UNC name [...] must include a trailing backslash, for example, "\\MyServer\MyShare\" + &bytesFree, //__out_opt PULARGE_INTEGER lpFreeBytesAvailable, + nullptr, //__out_opt PULARGE_INTEGER lpTotalNumberOfBytes, + nullptr)) //__out_opt PULARGE_INTEGER lpTotalNumberOfFreeBytes + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), L"GetDiskFreeSpaceEx", getLastError()); + + return get64BitUInt(bytesFree.LowPart, bytesFree.HighPart); + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct statfs info = {}; + if (::statfs(path.c_str(), &info) != 0) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), L"statfs", getLastError()); + + return static_cast(info.f_bsize) * info.f_bavail; +#endif +} + + +bool zen::removeFile(const Zstring& filepath) //throw FileError +{ +#ifdef ZEN_WIN + const wchar_t functionName[] = L"DeleteFile"; + const Zstring& filepathFmt = applyLongPathPrefix(filepath); + if (!::DeleteFile(filepathFmt.c_str())) +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"unlink"; + if (::unlink(filepath.c_str()) != 0) +#endif + { + ErrorCode lastError = getLastError(); +#ifdef ZEN_WIN + if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only + { + ::SetFileAttributes(filepathFmt.c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes + + if (::DeleteFile(filepathFmt.c_str())) //now try again... + return true; + lastError = ::GetLastError(); + } +#endif + if (!somethingExists(filepath)) //warning: changes global error code!! + return false; //neither file nor any other object (e.g. broken symlink) with that name existing - caveat: what if "access is denied"!?!??!?!? + + //begin of "regular" error reporting + const std::wstring errorMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filepath)); + std::wstring errorDescr = formatSystemError(functionName, lastError); + +#ifdef ZEN_WIN + if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! + lastError == ERROR_LOCK_VIOLATION) + { + const Zstring procList = getLockingProcessNames(filepath); //throw() + if (!procList.empty()) + errorDescr = _("The file is locked by another process:") + L"\n" + procList; + } +#endif + throw FileError(errorMsg, errorDescr); + } + return true; +} + + +namespace +{ +/* Usage overview: (avoid circular pattern!) + + renameFile() --> renameFile_sub() + | /|\ + \|/ | + Fix8Dot3NameClash() +*/ +//wrapper for file system rename function: +void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting +{ +#ifdef ZEN_WIN + const Zstring oldNameFmt = applyLongPathPrefix(oldName); + const Zstring newNameFmt = applyLongPathPrefix(newName); + + if (!::MoveFileEx(oldNameFmt.c_str(), //__in LPCTSTR lpExistingFileName, + newNameFmt.c_str(), //__in_opt LPCTSTR lpNewFileName, + 0)) //__in DWORD dwFlags + { + DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + + if (lastError == ERROR_ACCESS_DENIED) //MoveFileEx may fail to rename a read-only file on a SAMBA-share -> (try to) handle this + { + const DWORD oldAttr = ::GetFileAttributes(oldNameFmt.c_str()); + if (oldAttr != INVALID_FILE_ATTRIBUTES && (oldAttr & FILE_ATTRIBUTE_READONLY)) + { + if (::SetFileAttributes(oldNameFmt.c_str(), FILE_ATTRIBUTE_NORMAL)) //remove readonly-attribute + { + //try again... + if (::MoveFileEx(oldNameFmt.c_str(), //__in LPCTSTR lpExistingFileName, + newNameFmt.c_str(), //__in_opt LPCTSTR lpNewFileName, + 0)) //__in DWORD dwFlags + { + //(try to) restore file attributes + ::SetFileAttributes(newNameFmt.c_str(), oldAttr); //don't handle error + return; + } + else + { + lastError = ::GetLastError(); //use error code from second call to ::MoveFileEx() + //cleanup: (try to) restore file attributes: assume oldName is still existing + ::SetFileAttributes(oldNameFmt.c_str(), oldAttr); + } + } + } + } + + const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)); + std::wstring errorDescr = formatSystemError(L"MoveFileEx", lastError); + + //try to enhance error message: + if (lastError == ERROR_SHARING_VIOLATION || + lastError == ERROR_LOCK_VIOLATION) + { + const Zstring procList = getLockingProcessNames(oldName); //throw() + if (!procList.empty()) + errorDescr = _("The file is locked by another process:") + L"\n" + procList; + } + + if (lastError == ERROR_NOT_SAME_DEVICE) + throw ErrorDifferentVolume(errorMsg, errorDescr); + else if (lastError == ERROR_ALREADY_EXISTS || //-> used on Win7 x64 + lastError == ERROR_FILE_EXISTS) //-> used by XP??? + throw ErrorTargetExisting(errorMsg, errorDescr); + else + throw FileError(errorMsg, errorDescr); + } + +#elif defined ZEN_LINUX || defined ZEN_MAC + if (::rename(oldName.c_str(), newName.c_str()) != 0) + { + const int lastError = errno; //copy before directly or indirectly making other system calls! + const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)); + const std::wstring errorDescr = formatSystemError(L"rename", lastError); + + if (lastError == EXDEV) + throw ErrorDifferentVolume(errorMsg, errorDescr); + else if (lastError == EEXIST) + throw ErrorTargetExisting(errorMsg, errorDescr); + else + throw FileError(errorMsg, errorDescr); + } +#endif +} + + +#ifdef ZEN_WIN +/*small wrapper around +::GetShortPathName() +::GetLongPathName() */ +template +Zstring getFilenameFmt(const Zstring& filepath, Function fun) //throw(); returns empty string on error +{ + const Zstring filepathFmt = applyLongPathPrefix(filepath); + + const DWORD bufferSize = fun(filepathFmt.c_str(), nullptr, 0); + if (bufferSize == 0) + return Zstring(); + + std::vector buffer(bufferSize); + + const DWORD charsWritten = fun(filepathFmt.c_str(), //__in LPCTSTR lpszShortPath, + &buffer[0], //__out LPTSTR lpszLongPath, + bufferSize); //__in DWORD cchBuffer + if (charsWritten == 0 || charsWritten >= bufferSize) + return Zstring(); + + return &buffer[0]; +} + + +Zstring findUnused8Dot3Name(const Zstring& filepath) //find a unique 8.3 short name +{ + const Zstring pathPrefix = contains(filepath, FILE_NAME_SEPARATOR) ? + (beforeLast(filepath, FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR) : Zstring(); + + Zstring extension = afterLast(afterLast(filepath, FILE_NAME_SEPARATOR), Zchar('.')); //extension needn't contain reasonable data + if (extension.empty()) + extension = Zstr("FFS"); + else if (extension.length() > 3) + extension.resize(3); + + for (int index = 0; index < 100000000; ++index) //filepath must be representable by <= 8 characters + { + const Zstring output = pathPrefix + numberTo(index) + Zchar('.') + extension; + 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...") + utfCvrtTo(pathPrefix)); +} + + +bool have8dot3NameClash(const Zstring& filepath) +{ + if (!contains(filepath, FILE_NAME_SEPARATOR)) + return false; + + if (somethingExists(filepath)) //name OR directory! + { + const Zstring origName = afterLast(filepath, FILE_NAME_SEPARATOR); //returns the whole string if ch not found + const Zstring shortName = afterLast(getFilenameFmt(filepath, ::GetShortPathName), FILE_NAME_SEPARATOR); //throw() returns empty string on error + const Zstring longName = afterLast(getFilenameFmt(filepath, ::GetLongPathName ), FILE_NAME_SEPARATOR); // + + if (!shortName.empty() && + !longName .empty() && + EqualFilename()(origName, shortName) && + !EqualFilename()(shortName, longName)) + { + //for filepath short and long file name are equal and another unrelated file happens to have the same short name + //e.g. filepath == "TESTWE~1", but another file is existing named "TestWeb" with short name ""TESTWE~1" + return true; + } + } + return false; +} + +class Fix8Dot3NameClash //throw FileError +{ +public: + Fix8Dot3NameClash(const Zstring& filepath) + { + const Zstring longName = afterLast(getFilenameFmt(filepath, ::GetLongPathName), FILE_NAME_SEPARATOR); //throw() returns empty string on error + + unrelatedFile = beforeLast(filepath, FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + longName; + + //find another name in short format: this ensures the actual short name WILL be renamed as well! + unrelatedFileParked = findUnused8Dot3Name(filepath); + + //move already existing short name out of the way for now + renameFile_sub(unrelatedFile, unrelatedFileParked); //throw FileError, ErrorDifferentVolume + //DON'T call renameFile() to avoid reentrance! + } + + ~Fix8Dot3NameClash() + { + //the file system should assign this unrelated file a new (unique) short name + try + { + renameFile_sub(unrelatedFileParked, unrelatedFile); //throw FileError, ErrorDifferentVolume + } + catch (FileError&) {} + } +private: + Zstring unrelatedFile; + Zstring unrelatedFileParked; +}; +#endif +} + + +//rename file: no copying!!! +void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting +{ + try + { + renameFile_sub(oldName, newName); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + } + catch (const ErrorTargetExisting&) + { +#ifdef ZEN_WIN + //try to handle issues with already existing short 8.3 file names on Windows + if (have8dot3NameClash(newName)) + { + Fix8Dot3NameClash dummy(newName); //throw FileError; move clashing filepath to the side + //now try again... + renameFile_sub(oldName, newName); //throw FileError + return; + } +#endif + throw; + } +} + + +namespace +{ +class CollectFilesFlat : public zen::TraverseCallback +{ +public: + CollectFilesFlat(std::vector& files, std::vector& dirs) : + files_(files), + dirs_(dirs) {} + + void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) override + { + files_.push_back(filepath); + } + HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) override + { + if (dirExists(linkpath)) //dir symlink + dirs_.push_back(shortName); + else //file symlink, broken symlink + files_.push_back(shortName); + return LINK_SKIP; + } + TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) override + { + dirs_.push_back(dirpath); + return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively! + } + HandleError reportDirError (const std::wstring& msg, size_t retryNumber) override { throw FileError(msg); } + HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) override { throw FileError(msg); } + +private: + CollectFilesFlat (const CollectFilesFlat&) = delete; + CollectFilesFlat& operator=(const CollectFilesFlat&) = delete; + + std::vector& files_; + std::vector& dirs_; +}; + + +void removeDirectoryImpl(const Zstring& directory, //throw FileError + const std::function& onBeforeFileDeletion, + const std::function& onBeforeDirDeletion) +{ + assert(somethingExists(directory)); //[!] + +#ifdef ZEN_WIN + const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix + + //(try to) normalize file attributes: actually NEEDED for symbolic links also! + ::SetFileAttributes(directoryFmt.c_str(), FILE_ATTRIBUTE_NORMAL); +#endif + + //attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!! + if (symlinkExists(directory)) //remove symlink directly + { + if (onBeforeDirDeletion) + onBeforeDirDeletion(directory); //once per symlink +#ifdef ZEN_WIN + const wchar_t functionName[] = L"RemoveDirectory"; + if (!::RemoveDirectory(directoryFmt.c_str())) +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"unlink"; + if (::unlink(directory.c_str()) != 0) +#endif + throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), functionName, getLastError()); + } + else + { + std::vector fileList; + std::vector dirList; + { + //get all files and directories from current directory (WITHOUT subdirectories!) + CollectFilesFlat cff(fileList, dirList); + traverseFolder(directory, cff); //don't follow symlinks + } + + //delete directories recursively + for (const Zstring& dirpath : dirList) + removeDirectoryImpl(dirpath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError; call recursively to correctly handle symbolic links + + //delete files + for (const Zstring& filepath : fileList) + { + if (onBeforeFileDeletion) + onBeforeFileDeletion(filepath); //call once per file + removeFile(filepath); //throw FileError + } + + //parent directory is deleted last + if (onBeforeDirDeletion) + onBeforeDirDeletion(directory); //and once per folder +#ifdef ZEN_WIN + const wchar_t functionName[] = L"RemoveDirectory"; + if (!::RemoveDirectory(directoryFmt.c_str())) +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"rmdir"; + if (::rmdir(directory.c_str()) != 0) +#endif + throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), functionName, getLastError()); + //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have + //successfully been *marked* for deletion, but some application still has a handle open! + //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 + //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html + } +} + + +#ifdef ZEN_WIN +void setFileTimeRaw(const Zstring& filepath, + const FILETIME* creationTime, //optional + const FILETIME& lastWriteTime, + ProcSymlink procSl) //throw FileError +{ + { + //extra scope for debug check below + + //privilege SE_BACKUP_NAME doesn't seem to be required here for symbolic links + //note: setting privileges requires admin rights! + + //opening newly created target file may fail due to some AV-software scanning it: no error, we will wait! + //http://support.microsoft.com/?scid=kb%3Ben-us%3B316609&x=17&y=20 + //-> enable as soon it turns out it is required! + + /*const int retryInterval = 50; + const int maxRetries = 2000 / retryInterval; + for (int i = 0; i < maxRetries; ++i) + { + */ + + /* + if (hTarget == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_SHARING_VIOLATION) + ::Sleep(retryInterval); //wait then retry + else //success or unknown failure + break; + } + */ + //temporarily reset read-only flag if required + DWORD attribs = INVALID_FILE_ATTRIBUTES; + ZEN_ON_SCOPE_EXIT( + if (attribs != INVALID_FILE_ATTRIBUTES) + ::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), attribs); + ); + + auto removeReadonly = [&]() -> bool //throw FileError; may need to remove the readonly-attribute (e.g. on FAT usb drives) + { + if (attribs == INVALID_FILE_ATTRIBUTES) + { + const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str()); + if (tmpAttr == INVALID_FILE_ATTRIBUTES) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileAttributes", getLastError()); + + if (tmpAttr & FILE_ATTRIBUTE_READONLY) + { + if (!::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), FILE_ATTRIBUTE_NORMAL)) + throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filepath)), L"SetFileAttributes", getLastError()); + + attribs = tmpAttr; //reapplied on scope exit + return true; + } + } + return false; + }; + + auto openFile = [&](bool conservativeApproach) + { + return ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, + (conservativeApproach ? + //some NAS seem to have issues with FILE_WRITE_ATTRIBUTES, even worse, they may fail silently! + //http://sourceforge.net/tracker/?func=detail&atid=1093081&aid=3536680&group_id=234430 + //Citrix shares seem to have this issue, too, but at least fail with "access denied" => try generic access first: + GENERIC_READ | GENERIC_WRITE : + //avoids mysterious "access denied" when using "GENERIC_READ | GENERIC_WRITE" on a read-only file, even *after* read-only was removed directly before the call! + //http://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 + //since former gives an error notification we may very well try FILE_WRITE_ATTRIBUTES second. + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES), //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + (procSl == ProcSymlink::DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0) | + FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + }; + + HANDLE hFile = INVALID_HANDLE_VALUE; + for (int i = 0; i < 2; ++i) //we will get this handle, no matter what! :) + { + //1. be conservative + hFile = openFile(true); + if (hFile == INVALID_HANDLE_VALUE) + { + if (::GetLastError() == ERROR_ACCESS_DENIED) //fails if file is read-only (or for "other" reasons) + if (removeReadonly()) //throw FileError + continue; + + //2. be a *little* fancy + hFile = openFile(false); + if (hFile == INVALID_HANDLE_VALUE) + { + const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + if (lastError == ERROR_ACCESS_DENIED) + if (removeReadonly()) //throw FileError + continue; + + //3. after these herculean stunts we give up... + throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)), L"CreateFile", lastError); + } + } + break; + } + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); + + + auto isNullTime = [](const FILETIME& ft) { return ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0; }; + + if (!::SetFileTime(hFile, //__in HANDLE hFile, + creationTime, //__in_opt const FILETIME *lpCreationTime, + nullptr, //__in_opt const FILETIME *lpLastAccessTime, + &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime + { + ErrorCode lastError = getLastError(); //copy before directly or indirectly making other system calls! + + //function may fail if file is read-only: https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 + if (lastError == ERROR_ACCESS_DENIED) + { + //dynamically load windows API function: available with Windows Vista and later + typedef BOOL (WINAPI* SetFileInformationByHandleFunc)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize); + const SysDllFun setFileInformationByHandle(L"kernel32.dll", "SetFileInformationByHandle"); + + if (setFileInformationByHandle) //if not: let the original error propagate! + { + auto setFileInfo = [&](FILE_BASIC_INFO basicInfo) //throw FileError; no const& since SetFileInformationByHandle() requires non-const parameter! + { + if (!setFileInformationByHandle(hFile, //__in HANDLE hFile, + FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, + &basicInfo, //__in LPVOID lpFileInformation, + sizeof(basicInfo))) //__in DWORD dwBufferSize + throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filepath)), L"SetFileInformationByHandle", getLastError()); + }; + + auto toLargeInteger = [](const FILETIME& ft) -> LARGE_INTEGER + { + LARGE_INTEGER tmp = {}; + tmp.LowPart = ft.dwLowDateTime; + tmp.HighPart = ft.dwHighDateTime; + return tmp; + }; + //--------------------------------------------------------------------------- + + BY_HANDLE_FILE_INFORMATION fileInfo = {}; + if (::GetFileInformationByHandle(hFile, &fileInfo)) + if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + { + FILE_BASIC_INFO basicInfo = {}; //undocumented: file times of "0" stand for "don't change" + basicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; //[!] the bug in the ticket above requires we set this together with file times!!! + basicInfo.LastWriteTime = toLargeInteger(lastWriteTime); // + if (creationTime) + basicInfo.CreationTime = toLargeInteger(*creationTime); + + //set file time + attributes + setFileInfo(basicInfo); //throw FileError + + try //... to restore original file attributes + { + FILE_BASIC_INFO basicInfo2 = {}; + basicInfo2.FileAttributes = fileInfo.dwFileAttributes; + setFileInfo(basicInfo2); //throw FileError + } + catch (FileError&) {} + + lastError = ERROR_SUCCESS; + } + } + } + + std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)); + + //add more meaningful message: FAT accepts only a subset of the NTFS date range + if (lastError == ERROR_INVALID_PARAMETER && + isFatDrive(filepath)) + { + //we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!! + auto fmtDate = [](const FILETIME& ft) -> Zstring + { + SYSTEMTIME st = {}; + if (!::FileTimeToSystemTime(&ft, //__in const FILETIME *lpFileTime, + &st)) //__out LPSYSTEMTIME lpSystemTime + return Zstring(); + + Zstring dateTime; + { + const int bufferSize = ::GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0); + if (bufferSize > 0) + { + std::vector buffer(bufferSize); + if (::GetDateFormat(LOCALE_USER_DEFAULT, //_In_ LCID Locale, + 0, //_In_ DWORD dwFlags, + &st, //_In_opt_ const SYSTEMTIME *lpDate, + nullptr, //_In_opt_ LPCTSTR lpFormat, + &buffer[0], //_Out_opt_ LPTSTR lpDateStr, + bufferSize) > 0) //_In_ int cchDate + dateTime = &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! + } + } + + const int bufferSize = ::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0); + if (bufferSize > 0) + { + std::vector buffer(bufferSize); + if (::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, &buffer[0], bufferSize) > 0) + { + dateTime += L" "; + dateTime += &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! + } + } + return dateTime; + }; + + errorMsg += std::wstring(L"\nA FAT volume can only store dates between 1980 and 2107:\n") + + L"\twrite (UTC): \t" + fmtDate(lastWriteTime) + + (creationTime ? L"\n\tcreate (UTC): \t" + fmtDate(*creationTime) : L""); + } + + if (lastError != ERROR_SUCCESS) + throwFileError(errorMsg, L"SetFileTime", lastError); + } + } +#ifndef NDEBUG //verify written data: mainly required to check consistency of DST hack + FILETIME creationTimeDbg = {}; + FILETIME lastWriteTimeDbg = {}; + + HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + (procSl == ProcSymlink::DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0) | + FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + assert(hFile != INVALID_HANDLE_VALUE); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); + + assert(::GetFileTime(hFile, //probably more up to date than GetFileAttributesEx()!? + &creationTimeDbg, + nullptr, + &lastWriteTimeDbg)); + + assert(std::abs(filetimeToTimeT(lastWriteTimeDbg) - filetimeToTimeT(lastWriteTime)) <= 2); //respect 2 second FAT/FAT32 precision + //assert(std::abs(filetimeToTimeT(creationTimeDbg ) - filetimeToTimeT(creationTime )) <= 2); -> creation time not available for Linux-hosted Samba shares! +#endif +} +#endif +} + + +void zen::removeDirectory(const Zstring& directory, //throw FileError + const std::function& onBeforeFileDeletion, + const std::function& onBeforeDirDeletion) +{ + //no error situation if directory is not existing! manual deletion relies on it! + if (!somethingExists(directory)) + return; //neither directory nor any other object (e.g. broken symlink) with that name existing + removeDirectoryImpl(directory, onBeforeFileDeletion, onBeforeDirDeletion); +} + + +void zen::setFileTime(const Zstring& filepath, std::int64_t modTime, ProcSymlink procSl) //throw FileError +{ +#ifdef ZEN_WIN + setFileTimeRaw(filepath, nullptr, timetToFileTime(modTime), procSl); //throw FileError + +#elif defined ZEN_LINUX || defined ZEN_MAC + //sigh, we can't use utimensat on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit??? + + // struct ::timespec newTimes[2] = {}; + // newTimes[0].tv_nsec = UTIME_OMIT; //omit access time + // newTimes[1].tv_sec = to(modTime); //modification time (seconds) + // + // if (::utimensat(AT_FDCWD, filepath.c_str(), newTimes, procSl == SYMLINK_DIRECT ? AT_SYMLINK_NOFOLLOW : 0) != 0) + // throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)), L"utimensat", getLastError()); + + //=> fallback to "retarded-idiot version"! -- DarkByte + + //OS X: utime() is obsoleted by utimes()! utimensat() not yet implemented + + struct ::timeval newTimes[2] = {}; + newTimes[0].tv_sec = ::time(nullptr); //access time (seconds) + newTimes[1].tv_sec = modTime; //modification time (seconds) + + const int rv = procSl == ProcSymlink::FOLLOW ? + :: utimes(filepath.c_str(), newTimes) : + ::lutimes(filepath.c_str(), newTimes); + if (rv != 0) + throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)), L"utimes", getLastError()); +#endif +} + + +bool zen::supportsPermissions(const Zstring& dirpath) //throw FileError +{ +#ifdef ZEN_WIN + const DWORD bufferSize = MAX_PATH + 1; + std::vector buffer(bufferSize); + if (!::GetVolumePathName(dirpath.c_str(), //__in LPCTSTR lpszFileName, + &buffer[0], //__out LPTSTR lpszVolumePathName, + bufferSize)) //__in DWORD cchBufferLength + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirpath)), L"GetVolumePathName", getLastError()); + + DWORD fsFlags = 0; + if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName, + nullptr, //__out LPTSTR lpVolumeNameBuffer, + 0, //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirpath)), L"GetVolumeInformation", getLastError()); + + return (fsFlags & FILE_PERSISTENT_ACLS) != 0; + +#elif defined ZEN_LINUX || defined ZEN_MAC + return true; +#endif +} + + +namespace +{ +#ifdef HAVE_SELINUX +//copy SELinux security context +void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError +{ + security_context_t contextSource = nullptr; + const int rv = procSl == ProcSymlink::FOLLOW ? + ::getfilecon(source.c_str(), &contextSource) : + ::lgetfilecon(source.c_str(), &contextSource); + if (rv < 0) + { + if (errno == ENODATA || //no security context (allegedly) is not an error condition on SELinux + errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem + return; + + throwFileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtFileName(source)), L"getfilecon", getLastError()); + } + ZEN_ON_SCOPE_EXIT(::freecon(contextSource)); + + { + security_context_t contextTarget = nullptr; + const int rv2 = procSl == ProcSymlink::FOLLOW ? + ::getfilecon(target.c_str(), &contextTarget) : + ::lgetfilecon(target.c_str(), &contextTarget); + if (rv2 < 0) + { + if (errno == EOPNOTSUPP) + return; + //else: still try to set security context + } + else + { + ZEN_ON_SCOPE_EXIT(::freecon(contextTarget)); + + if (::strcmp(contextSource, contextTarget) == 0) //nothing to do + return; + } + } + + const int rv3 = procSl == ProcSymlink::FOLLOW ? + ::setfilecon(target.c_str(), contextSource) : + ::lsetfilecon(target.c_str(), contextSource); + if (rv3 < 0) + throwFileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtFileName(target)), L"setfilecon", getLastError()); +} +#endif //HAVE_SELINUX + + +//copy permissions for files, directories or symbolic links: requires admin rights +void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError +{ +#ifdef ZEN_WIN + //in contrast to ::SetSecurityInfo(), ::SetFileSecurity() seems to honor the "inherit DACL/SACL" flags + //CAVEAT: if a file system does not support ACLs, GetFileSecurity() will return successfully with a *valid* security descriptor containing *no* ACL entries! + + //NOTE: ::GetFileSecurity()/::SetFileSecurity() do NOT follow Symlinks! getResolvedFilePath() requires Vista or later! + const Zstring sourceResolved = procSl == ProcSymlink::FOLLOW && symlinkExists(source) ? getResolvedFilePath(source) : source; //throw FileError + const Zstring targetResolved = procSl == ProcSymlink::FOLLOW && symlinkExists(target) ? getResolvedFilePath(target) : target; // + + //setting privileges requires admin rights! + try + { + //enable privilege: required to read/write SACL information (only) + activatePrivilege(SE_SECURITY_NAME); //throw FileError + //Note: trying to copy SACL (SACL_SECURITY_INFORMATION) may return ERROR_PRIVILEGE_NOT_HELD (1314) on Samba shares. This is not due to missing privileges! + //However, this is okay, since copying NTFS permissions doesn't make sense in this case anyway + + //enable privilege: required to copy owner information + activatePrivilege(SE_RESTORE_NAME); //throw FileError + + //the following privilege may be required according to http://msdn.microsoft.com/en-us/library/aa364399(VS.85).aspx (although not needed nor active in my tests) + activatePrivilege(SE_BACKUP_NAME); //throw FileError + } + catch (const FileError& e)//add some more context description (e.g. user is not an admin) + { + throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), e.toString()); + } + + + std::vector buffer(10000); //example of actually required buffer size: 192 bytes + for (;;) + { + DWORD bytesNeeded = 0; + if (::GetFileSecurity(applyLongPathPrefix(sourceResolved).c_str(), //__in LPCTSTR lpFileName, -> long path prefix IS needed, although it is NOT mentioned on MSDN!!! + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION RequestedInformation, + reinterpret_cast(&buffer[0]), //__out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor, + static_cast(buffer.size()), //__in DWORD nLength, + &bytesNeeded)) //__out LPDWORD lpnLengthNeeded + break; + //failure: ... + if (bytesNeeded > buffer.size()) + buffer.resize(bytesNeeded); + else + throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), L"GetFileSecurity", getLastError()); + } + SECURITY_DESCRIPTOR& secDescr = reinterpret_cast(buffer[0]); + + /* + SECURITY_DESCRIPTOR_CONTROL secCtrl = 0; + { + DWORD ctrlRev = 0; + if (!::GetSecurityDescriptorControl(&secDescr, // __in PSECURITY_DESCRIPTOR pSecurityDescriptor, + &secCtrl, // __out PSECURITY_DESCRIPTOR_CONTROL pControl, + &ctrlRev)) //__out LPDWORD lpdwRevision + throw FileErro + } + //interesting flags: + //#define SE_DACL_PRESENT (0x0004) + //#define SE_SACL_PRESENT (0x0010) + //#define SE_DACL_PROTECTED (0x1000) + //#define SE_SACL_PROTECTED (0x2000) + */ + + if (!::SetFileSecurity(applyLongPathPrefix(targetResolved).c_str(), //__in LPCTSTR lpFileName, -> long path prefix IS needed, although it is NOT mentioned on MSDN!!! + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInformation, + &secDescr)) //__in PSECURITY_DESCRIPTOR pSecurityDescriptor + throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetResolved)), L"SetFileSecurity", getLastError()); + + /* + PSECURITY_DESCRIPTOR buffer = nullptr; + PSID owner = nullptr; + PSID group = nullptr; + PACL dacl = nullptr; + PACL sacl = nullptr; + + //File Security and Access Rights: http://msdn.microsoft.com/en-us/library/aa364399(v=VS.85).aspx + //SECURITY_INFORMATION Access Rights: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379573(v=vs.85).aspx + 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, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //FILE_FLAG_BACKUP_SEMANTICS needed to open a directory + nullptr); + if (hSource == INVALID_HANDLE_VALUE) + throw FileError + ZEN_ON_SCOPE_EXIT(::CloseHandle(hSource)); + + // DWORD rc = ::GetNamedSecurityInfo(const_cast(applyLongPathPrefix(source).c_str()), -> does NOT dereference symlinks! + DWORD rc = ::GetSecurityInfo(hSource, //__in LPTSTR pObjectName, + SE_FILE_OBJECT, //__in SE_OBJECT_TYPE ObjectType, + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInfo, + &owner, //__out_opt PSID *ppsidOwner, + &group, //__out_opt PSID *ppsidGroup, + &dacl, //__out_opt PACL *ppDacl, + &sacl, //__out_opt PACL *ppSacl, + &buffer); //__out_opt PSECURITY_DESCRIPTOR *ppSecurityDescriptor + if (rc != ERROR_SUCCESS) + throw FileError + ZEN_ON_SCOPE_EXIT(::LocalFree(buffer)); + + SECURITY_DESCRIPTOR_CONTROL secCtrl = 0; + { + DWORD ctrlRev = 0; + if (!::GetSecurityDescriptorControl(buffer, // __in PSECURITY_DESCRIPTOR pSecurityDescriptor, + &secCtrl, // __out PSECURITY_DESCRIPTOR_CONTROL pControl, + &ctrlRev))//__out LPDWORD lpdwRevision + throw FileError + } + + //may need to remove the readonly-attribute + FileUpdateHandle targetHandle(target, [=] + { + return ::CreateFile(applyLongPathPrefix(target).c_str(), // lpFileName + GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY, // dwDesiredAccess: all four seem to be required!!! + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // dwShareMode + nullptr, // lpSecurityAttributes + OPEN_EXISTING, // dwCreationDisposition + FILE_FLAG_BACKUP_SEMANTICS | (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), // dwFlagsAndAttributes + nullptr); // hTemplateFile + }); + + if (targetHandle.get() == INVALID_HANDLE_VALUE) + throw FileError + + SECURITY_INFORMATION secFlags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; + + //SACL/DACL inheritence flag is NOT copied by default: we have to tell ::SetSecurityInfo(() to enable/disable it manually! + //if (secCtrl & SE_DACL_PRESENT) + secFlags |= (secCtrl & SE_DACL_PROTECTED) ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION; + //if (secCtrl & SE_SACL_PRESENT) + secFlags |= (secCtrl & SE_SACL_PROTECTED) ? PROTECTED_SACL_SECURITY_INFORMATION : UNPROTECTED_SACL_SECURITY_INFORMATION; + + + // rc = ::SetNamedSecurityInfo(const_cast(applyLongPathPrefix(target).c_str()), //__in LPTSTR pObjectName, -> does NOT dereference symlinks! + rc = ::SetSecurityInfo(targetHandle.get(), //__in LPTSTR pObjectName, + SE_FILE_OBJECT, //__in SE_OBJECT_TYPE ObjectType, + secFlags, //__in SECURITY_INFORMATION SecurityInfo, + owner, //__in_opt PSID psidOwner, + group, //__in_opt PSID psidGroup, + dacl, //__in_opt PACL pDacl, + sacl); //__in_opt PACL pSacl + + if (rc != ERROR_SUCCESS) + throw FileError + */ + +#elif defined ZEN_LINUX || defined ZEN_MAC + +#ifdef HAVE_SELINUX //copy SELinux security context + copySecurityContext(source, target, procSl); //throw FileError +#endif + + struct stat fileInfo = {}; + if (procSl == ProcSymlink::FOLLOW) + { + if (::stat(source.c_str(), &fileInfo) != 0) + throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)), L"stat", getLastError()); + + if (::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! + throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), L"chown", getLastError()); + + if (::chmod(target.c_str(), fileInfo.st_mode) != 0) + throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), L"chmod", getLastError()); + } + else + { + if (::lstat(source.c_str(), &fileInfo) != 0) + throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)), L"lstat", getLastError()); + + if (::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! + throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), L"lchown", getLastError()); + + if (!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() + throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), L"chmod", getLastError()); + } +#endif +} + + +void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTargetExisting +{ + assert(!endsWith(directory, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input! + + try + { + makeDirectoryPlain(directory, Zstring(), false); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + } + catch (const ErrorTargetPathMissing&) + { + //we need to create parent directories first + const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR); + if (!dirParent.empty()) + { + //recurse... + try + { + makeDirectoryRecursively(dirParent); //throw FileError, (ErrorTargetExisting) + } + catch (const ErrorTargetExisting&) { /*parent directory created externally in the meantime? => NOT AN ERROR*/ } + + //now try again... + makeDirectoryPlain(directory, Zstring(), false); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing) + return; + } + throw; + } +} +} + + +void zen::makeDirectory(const Zstring& directory, bool failIfExists) //throw FileError, ErrorTargetExisting +{ + //remove trailing separator (even for C:\ root directories) + const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ? + beforeLast(directory, FILE_NAME_SEPARATOR) : + directory; + + try + { + makeDirectoryRecursively(dirFormatted); //FileError, ErrorTargetExisting + } + catch (const ErrorTargetExisting&) + { + //avoid any file system race-condition by *not* checking directory existence again here!!! + if (failIfExists) + throw; + } + catch (const FileError&) + { + /* + could there be situations where a directory/network path exists, + but creation fails with error different than "ErrorTargetExisting"?? + - creation of C:\ fails with ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS + */ + if (somethingExists(directory)) //a file system race-condition! don't use dirExists() => harmonize with ErrorTargetExisting! + { + assert(false); + if (failIfExists) + throw; //do NOT convert to ErrorTargetExisting: if "failIfExists", not getting a ErrorTargetExisting *atomically* is unexpected! + } + else + throw; + } +} + + +void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + const Zstring& templateDir, + bool copyFilePermissions) +{ +#ifdef ZEN_WIN + //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS! + Zstring dirTmp = removeLongPathPrefix(endsWith(directory, FILE_NAME_SEPARATOR) ? + beforeLast(directory, FILE_NAME_SEPARATOR) : + directory); + if (dirTmp.size() == 2 && + std::iswalpha(dirTmp[0]) && dirTmp[1] == L':') + { + dirTmp += FILE_NAME_SEPARATOR; //we do not support "C:" to represent a relative path! + + const ErrorCode lastError = somethingExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting! + + const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp)); + const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); + + if (lastError == ERROR_ALREADY_EXISTS) + throw ErrorTargetExisting(errorMsg, errorDescr); + throw FileError(errorMsg, errorDescr); //[!] this is NOT a ErrorTargetPathMissing case! + } + + //don't use ::CreateDirectoryEx: + //- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage + //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)! + //- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!) + if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), //__in LPCTSTR lpPathName, + nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes + { + DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + + //handle issues with already existing short 8.3 file names on Windows + if (lastError == ERROR_ALREADY_EXISTS) + if (have8dot3NameClash(directory)) + { + Fix8Dot3NameClash dummy(directory); //throw FileError; move clashing object to the side + + //now try again... + if (::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), nullptr)) + lastError = ERROR_SUCCESS; + else + lastError = ::GetLastError(); + } + + if (lastError != ERROR_SUCCESS) + { + const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)); + const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); + + if (lastError == ERROR_ALREADY_EXISTS) + throw ErrorTargetExisting(errorMsg, errorDescr); + else if (lastError == ERROR_PATH_NOT_FOUND) + throw ErrorTargetPathMissing(errorMsg, errorDescr); + throw FileError(errorMsg, errorDescr); + } + } + +#elif defined ZEN_LINUX || defined ZEN_MAC + if (::mkdir(directory.c_str(), 0755) != 0) //mode: drwxr-xr-x + { + const int lastError = errno; //copy before directly or indirectly making other system calls! + const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)); + const std::wstring errorDescr = formatSystemError(L"mkdir", lastError); + + if (lastError == EEXIST) + throw ErrorTargetExisting(errorMsg, errorDescr); + else if (lastError == ENOENT) + throw ErrorTargetPathMissing(errorMsg, errorDescr); + throw FileError(errorMsg, errorDescr); + } +#endif + + if (!templateDir.empty()) + { +#ifdef ZEN_WIN + //optional: try to copy file attributes (dereference symlinks and junctions) + const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(), //_In_ LPCTSTR lpFileName, + 0, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + // FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks! + FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hDirSrc != INVALID_HANDLE_VALUE) //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error... + { + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirSrc)); + + BY_HANDLE_FILE_INFORMATION dirInfo = {}; + if (::GetFileInformationByHandle(hDirSrc, &dirInfo)) + { + ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), dirInfo.dwFileAttributes); + //copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx + + const bool isEncrypted = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; + const bool isCompressed = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; + + if (isEncrypted) + ::EncryptFile(directory.c_str()); //seems no long path is required (check passed!) + + HANDLE hDirTrg = ::CreateFile(applyLongPathPrefix(directory).c_str(), //_In_ LPCTSTR lpFileName, + GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess, + /*read access required for FSCTL_SET_COMPRESSION*/ + FILE_SHARE_READ | + FILE_SHARE_WRITE | + FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hDirTrg != INVALID_HANDLE_VALUE) + { + ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirTrg)); + + if (isCompressed) + { + USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; + DWORD bytesReturned = 0; + /*bool rv = */::DeviceIoControl(hDirTrg, //_In_ HANDLE hDevice, + FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode, + &cmpState, //_In_opt_ LPVOID lpInBuffer, + sizeof(cmpState), //_In_ DWORD nInBufferSize, + nullptr, //_Out_opt_ LPVOID lpOutBuffer, + 0, //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned, + nullptr); //_Inout_opt_ LPOVERLAPPED lpOverlapped + } + + //(try to) set creation and modification time + /*bool rv = */::SetFileTime(hDirTrg, //_In_ HANDLE hFile, + &dirInfo.ftCreationTime, //_Out_opt_ LPFILETIME lpCreationTime, + nullptr, //_Out_opt_ LPFILETIME lpLastAccessTime, + &dirInfo.ftLastWriteTime); //_Out_opt_ LPFILETIME lpLastWriteTime + } + } + } +#endif + + zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(directory); } catch (FileError&) {} }); //ensure cleanup: + + //enforce copying file permissions: it's advertized on GUI... + if (copyFilePermissions) + copyObjectPermissions(templateDir, directory, ProcSymlink::FOLLOW); //throw FileError + + guardNewDir.dismiss(); //target has been created successfully! + } +} + + +void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions) //throw FileError +{ + const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks + +#ifdef ZEN_WIN + const bool isDirLink = [&]() -> bool + { + const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(sourceLink).c_str()); + return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY); + }(); + + typedef BOOLEAN (WINAPI* CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags); + const SysDllFun createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW"); + + if (!createSymbolicLink) + throw FileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)), + replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\"")); + + const wchar_t functionName[] = L"CreateSymbolicLinkW"; + if (!createSymbolicLink(targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, - seems no long path prefix is required... + linkPath.c_str(), //__in LPTSTR lpTargetFileName, + (isDirLink ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0))) //__in DWORD dwFlags +#elif defined ZEN_LINUX || defined ZEN_MAC + const wchar_t functionName[] = L"symlink"; + if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0) +#endif + throwFileError(replaceCpy(replaceCpy(_("Cannot copy symbolic link %x to %y."), L"%x", L"\n" + fmtFileName(sourceLink)), L"%y", L"\n" + fmtFileName(targetLink)), functionName, getLastError()); + + //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist! + zen::ScopeGuard guardNewLink = zen::makeGuard([&] + { + try + { +#ifdef ZEN_WIN + if (isDirLink) + removeDirectory(targetLink); //throw FileError + else +#endif + removeFile(targetLink); //throw FileError + } + catch (FileError&) {} + }); + + //file times: essential for sync'ing a symlink: enforce this! (don't just try!) +#ifdef ZEN_WIN + WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; + if (!::GetFileAttributesEx(applyLongPathPrefix(sourceLink).c_str(), //__in LPCTSTR lpFileName, + GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, + &sourceAttr)) //__out LPVOID lpFileInformation + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"GetFileAttributesEx", getLastError()); + + setFileTimeRaw(targetLink, &sourceAttr.ftCreationTime, sourceAttr.ftLastWriteTime, ProcSymlink::DIRECT); //throw FileError + +#elif defined ZEN_LINUX || defined ZEN_MAC + struct ::stat srcInfo = {}; + if (::lstat(sourceLink.c_str(), &srcInfo) != 0) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"lstat", getLastError()); + + setFileTime(targetLink, srcInfo.st_mtime, ProcSymlink::DIRECT); //throw FileError +#endif + + if (copyFilePermissions) + copyObjectPermissions(sourceLink, targetLink, ProcSymlink::DIRECT); //throw FileError + + guardNewLink.dismiss(); //target has been created successfully! +} + + +namespace +{ +#ifdef ZEN_WIN +/* + CopyFileEx() BackupRead() FileRead() + -------------------------------------------- +Attributes YES NO NO +create time NO NO NO +ADS YES YES NO +Encrypted YES NO(silent fail!) NO +Compressed NO NO NO +Sparse NO YES NO +Nonstandard FS YES UNKNOWN -> issues writing ADS to Samba, issues reading from NAS, error copying files having "blocked" state... ect. +PERF - 6% faster + +Mark stream as compressed: FSCTL_SET_COMPRESSION - compatible with both BackupRead() and FileRead() + + +Current support for combinations of NTFS extended attributes: + +source attr | tf normal | tf compressed | tf encrypted | handled by +============|================================================================== + --- | --- -C- E-- copyFileWindowsDefault + --S | --S -CS E-S copyFileWindowsSparse + -C- | -C- -C- E-- copyFileWindowsDefault + -CS | -CS -CS E-S copyFileWindowsSparse + E-- | E-- E-- E-- copyFileWindowsDefault + E-S | E-- (NOK) E-- (NOK) E-- (NOK) copyFileWindowsDefault -> may fail with ERROR_DISK_FULL!! + +tf := target folder +E := encrypted +C := compressed +S := sparse +NOK := current behavior is not optimal/OK yet. + +Note: - if target parent folder is compressed or encrypted, both attributes are added automatically during file creation! + - "compressed" and "encrypted" are mutually exclusive: http://support.microsoft.com/kb/223093/en-us +*/ + + +//due to issues on non-NTFS volumes, we should use the copy-as-sparse routine only if required and supported! +bool canCopyAsSparse(DWORD fileAttrSource, const Zstring& targetFile) //throw () +{ + const bool sourceIsEncrypted = (fileAttrSource & FILE_ATTRIBUTE_ENCRYPTED) != 0; + const bool sourceIsSparse = (fileAttrSource & FILE_ATTRIBUTE_SPARSE_FILE) != 0; + + if (sourceIsEncrypted || !sourceIsSparse) //BackupRead() silently fails reading encrypted files! + return false; //small perf optimization: don't check "targetFile" if not needed + + //------------------------------------------------------------------------------------ + const DWORD bufferSize = 10000; + std::vector buffer(bufferSize); + + //full pathName need not yet exist! + if (!::GetVolumePathName(targetFile.c_str(), //__in LPCTSTR lpszFileName, + &buffer[0], //__out LPTSTR lpszVolumePathName, + bufferSize)) //__in DWORD cchBufferLength + return false; + + DWORD fsFlagsTarget = 0; + if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName + nullptr, //__out_opt LPTSTR lpVolumeNameBuffer, + 0, //__in DWORD nVolumeNameSize, + nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, + nullptr, //__out_opt LPDWORD lpMaximumComponentLength, + &fsFlagsTarget, //__out_opt LPDWORD lpFileSystemFlags, + nullptr, //__out LPTSTR lpFileSystemNameBuffer, + 0)) //__in DWORD nFileSystemNameSize + return false; + + const bool targetSupportSparse = (fsFlagsTarget & FILE_SUPPORTS_SPARSE_FILES) != 0; + + return targetSupportSparse; + //both source and target must not be FAT since copyFileWindowsSparse() does no DST hack! implicitly guaranteed at this point! +} + + +bool canCopyAsSparse(const Zstring& sourceFile, const Zstring& targetFile) //throw () +{ + //follow symlinks! + HANDLE hSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), //_In_ LPCTSTR lpFileName, + 0, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | //all shared modes are required to read files that are open in other applications + FILE_SHARE_WRITE | + FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + 0, //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hSource == INVALID_HANDLE_VALUE) + return false; + ZEN_ON_SCOPE_EXIT(::CloseHandle(hSource)); + + BY_HANDLE_FILE_INFORMATION fileInfoSource = {}; + if (!::GetFileInformationByHandle(hSource, &fileInfoSource)) + return false; + + return canCopyAsSparse(fileInfoSource.dwFileAttributes, targetFile); //throw () +} + + +//precondition: canCopyAsSparse() must return "true"! +void copyFileWindowsSparse(const Zstring& sourceFile, + const Zstring& targetFile, + const std::function& onUpdateCopyStatus, + InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting, ErrorFileLocked +{ + assert(canCopyAsSparse(sourceFile, targetFile)); + + //try to get backup read and write privileges: who knows, maybe this helps solve some obscure "access denied" errors + try { activatePrivilege(SE_BACKUP_NAME); } + catch (const FileError&) {} + try { activatePrivilege(SE_RESTORE_NAME); } + catch (const FileError&) {} + + //open sourceFile for reading + HANDLE hFileSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), //_In_ LPCTSTR lpFileName, + GENERIC_READ, //_In_ DWORD dwDesiredAccess, + FILE_SHARE_READ | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, + //FILE_FLAG_OVERLAPPED must not be used! + //FILE_FLAG_NO_BUFFERING should not be used! + FILE_FLAG_SEQUENTIAL_SCAN | + FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hFileSource == INVALID_HANDLE_VALUE) + { + const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + + const std::wstring errorMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)); + std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); + + //if file is locked throw "ErrorFileLocked" instead! + if (lastError == ERROR_SHARING_VIOLATION || + lastError == ERROR_LOCK_VIOLATION) + { + const Zstring procList = getLockingProcessNames(sourceFile); //throw() + if (!procList.empty()) + errorDescr = _("The file is locked by another process:") + L"\n" + procList; + throw ErrorFileLocked(errorMsg, errorDescr); + } + + throw FileError(errorMsg, errorDescr); + } + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileSource)); + + //---------------------------------------------------------------------- + BY_HANDLE_FILE_INFORMATION fileInfoSource = {}; + if (!::GetFileInformationByHandle(hFileSource, &fileInfoSource)) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), L"GetFileInformationByHandle", getLastError()); + + //---------------------------------------------------------------------- + const DWORD validAttribs = FILE_ATTRIBUTE_NORMAL | //"This attribute is valid only if used alone." + FILE_ATTRIBUTE_READONLY | + FILE_ATTRIBUTE_HIDDEN | + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_ARCHIVE | //those two are not set properly (not worse than ::CopyFileEx()) + FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; // + //FILE_ATTRIBUTE_ENCRYPTED -> no! + + //create targetFile and open it for writing + HANDLE hFileTarget = ::CreateFile(applyLongPathPrefix(targetFile).c_str(), //_In_ LPCTSTR lpFileName, + GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess, + //read access required for FSCTL_SET_COMPRESSION + FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, + //FILE_SHARE_DELETE is required to rename file while handle is open! + nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, + CREATE_NEW, //_In_ DWORD dwCreationDisposition, + //FILE_FLAG_OVERLAPPED must not be used! FILE_FLAG_NO_BUFFERING should not be used! + (fileInfoSource.dwFileAttributes & validAttribs) | + FILE_FLAG_SEQUENTIAL_SCAN | + FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, + nullptr); //_In_opt_ HANDLE hTemplateFile + if (hFileTarget == INVALID_HANDLE_VALUE) + { + const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)); + const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); + + if (lastError == ERROR_FILE_EXISTS || //confirmed to be used + lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 + throw ErrorTargetExisting(errorMsg, errorDescr); + + //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); + + throw FileError(errorMsg, errorDescr); + } + ScopeGuard guardTarget = makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: guard just after opening target and before managing hFileTarget + ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget)); + + //---------------------------------------------------------------------- + BY_HANDLE_FILE_INFORMATION fileInfoTarget = {}; + if (!::GetFileInformationByHandle(hFileTarget, &fileInfoTarget)) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), L"GetFileInformationByHandle", getLastError()); + + //return up-to-date file attributes + if (newAttrib) + { + newAttrib->fileSize = get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh); + newAttrib->modificationTime = filetimeToTimeT(fileInfoSource.ftLastWriteTime); //no DST hack (yet) + newAttrib->sourceFileId = extractFileId(fileInfoSource); + newAttrib->targetFileId = extractFileId(fileInfoTarget); + } + + //#################### copy NTFS compressed attribute ######################### + const bool sourceIsCompressed = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; + const bool targetIsCompressed = (fileInfoTarget.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; //already set by CreateFile if target parent folder is compressed! + if (sourceIsCompressed && !targetIsCompressed) + { + USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; + DWORD bytesReturned = 0; + if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice, + FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode, + &cmpState, //_In_opt_ LPVOID lpInBuffer, + sizeof(cmpState), //_In_ DWORD nInBufferSize, + nullptr, //_Out_opt_ LPVOID lpOutBuffer, + 0, //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned, + nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped + {} //may legitimately fail with ERROR_INVALID_FUNCTION if: + // - target folder is encrypted + // - target volume does not support compressed attribute -> unlikely in this context + } + //############################################################################# + + //although it seems the sparse attribute is set automatically by BackupWrite, we are required to do this manually: http://support.microsoft.com/kb/271398/en-us + //Quote: It is the responsibility of the backup utility to apply file attributes to a file after it is restored by using BackupWrite. + //The application should retrieve the attributes by using GetFileAttributes prior to creating a backup with BackupRead. + //If a file originally had the sparse attribute (FILE_ATTRIBUTE_SPARSE_FILE), the backup utility must explicitly set the + //attribute on the restored file. + + //if (sourceIsSparse && targetSupportsSparse) -> no need to check, this is our precondition! + { + DWORD bytesReturned = 0; + if (!::DeviceIoControl(hFileTarget, //_In_ HANDLE hDevice, + FSCTL_SET_SPARSE, //_In_ DWORD dwIoControlCode, + nullptr, //_In_opt_ LPVOID lpInBuffer, + 0, //_In_ DWORD nInBufferSize, + nullptr, //_Out_opt_ LPVOID lpOutBuffer, + 0, //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned, + nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped + throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError()); + } + + //---------------------------------------------------------------------- + const DWORD BUFFER_SIZE = 128 * 1024; //must be greater than sizeof(WIN32_STREAM_ID) + std::vector buffer(BUFFER_SIZE); + + LPVOID contextRead = nullptr; //manage context for BackupRead()/BackupWrite() + LPVOID contextWrite = nullptr; // + + ZEN_ON_SCOPE_EXIT( + if (contextRead ) ::BackupRead (0, nullptr, 0, nullptr, true, false, &contextRead); //lpContext must be passed [...] all other parameters are ignored. + if (contextWrite) ::BackupWrite(0, nullptr, 0, nullptr, true, false, &contextWrite); ); + + //stream-copy sourceFile to targetFile + bool eof = false; + bool someBytesWritten = false; //try to detect failure reading encrypted files + do + { + DWORD bytesRead = 0; + if (!::BackupRead(hFileSource, //__in HANDLE hFile, + &buffer[0], //__out LPBYTE lpBuffer, + BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, + &bytesRead, //__out LPDWORD lpNumberOfBytesRead, + false, //__in BOOL bAbort, + false, //__in BOOL bProcessSecurity, + &contextRead)) //__out LPVOID *lpContext + throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead", getLastError()); //better use fine-granular error messages "reading/writing"! + + if (bytesRead > BUFFER_SIZE) + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"buffer overflow"); //user should never see this + + if (bytesRead < BUFFER_SIZE) + eof = true; + + DWORD bytesWritten = 0; + if (!::BackupWrite(hFileTarget, //__in HANDLE hFile, + &buffer[0], //__in LPBYTE lpBuffer, + bytesRead, //__in DWORD nNumberOfBytesToWrite, + &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten, + false, //__in BOOL bAbort, + false, //__in BOOL bProcessSecurity, + &contextWrite)) //__out LPVOID *lpContext + throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"BackupWrite", getLastError()); + + if (bytesWritten != bytesRead) + throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"incomplete write"); //user should never see this + + //total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)! + + //invoke callback method to update progress indicators + if (onUpdateCopyStatus) + onUpdateCopyStatus(bytesRead); //throw X! + + if (bytesRead > 0) + someBytesWritten = true; + } + while (!eof); + + //DST hack not required, since both source and target volumes cannot be FAT! + + //::BackupRead() silently fails reading encrypted files -> double check! + if (!someBytesWritten && get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh) != 0U) + //note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)! + throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()" + + //time needs to be set at the end: BackupWrite() changes modification time + if (!::SetFileTime(hFileTarget, + &fileInfoSource.ftCreationTime, + nullptr, + &fileInfoSource.ftLastWriteTime)) + throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)), L"SetFileTime", getLastError()); + + guardTarget.dismiss(); + + /* + //create sparse file for testing: + HANDLE hSparse = ::CreateFile(L"C:\\sparse.file", + GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, + CREATE_NEW, + FILE_FLAG_SEQUENTIAL_SCAN, + nullptr); + if (hFileTarget == INVALID_HANDLE_VALUE) + throw FileError(L"fail"); + ZEN_ON_SCOPE_EXIT(::CloseHandle(hSparse)); + + DWORD br = 0; + if (!::DeviceIoControl(hSparse, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &br,nullptr)) + throw FileError(L"fail"); + + LARGE_INTEGER liDistanceToMove = {}; + liDistanceToMove.QuadPart = 1024 * 1024 * 1024; //create 5 TB sparse file + liDistanceToMove.QuadPart *= 5 * 1024; //maximum file size on NTFS: 16 TB - 64 kB + if (!::SetFilePointerEx(hSparse, liDistanceToMove, nullptr, FILE_BEGIN)) + throw FileError(L"fail"); + + if (!SetEndOfFile(hSparse)) + throw FileError(L"fail"); + + FILE_ZERO_DATA_INFORMATION zeroInfo = {}; + zeroInfo.BeyondFinalZero.QuadPart = liDistanceToMove.QuadPart; + if (!::DeviceIoControl(hSparse, FSCTL_SET_ZERO_DATA, &zeroInfo, sizeof(zeroInfo), nullptr, 0, &br, nullptr)) + throw FileError(L"fail"); + */ +} + + +DEFINE_NEW_FILE_ERROR(ErrorShouldCopyAsSparse); + +class ErrorHandling +{ +public: + ErrorHandling() : shouldCopyAsSparse(false) {} + + //call context: copyCallbackInternal() + void reportErrorShouldCopyAsSparse() { shouldCopyAsSparse = true; } + + void reportUserException(const std::exception_ptr& e) { exception = e; } + + void reportError(const std::wstring& msg, const std::wstring& description) { errorMsg = std::make_pair(msg, description); } + + //call context: copyFileWindowsDefault() + void evaluateErrors() //throw X + { + if (shouldCopyAsSparse) + throw ErrorShouldCopyAsSparse(L"sparse dummy value"); + + if (exception) + std::rethrow_exception(exception); + + if (!errorMsg.first.empty()) + throw FileError(errorMsg.first, errorMsg.second); + } + +private: + bool shouldCopyAsSparse; // + std::pair errorMsg; //these are exclusive! + std::exception_ptr exception; +}; + + +struct CallbackData +{ + CallbackData(const std::function& onUpdateCopyStatus, + const Zstring& sourceFile, + const Zstring& targetFile) : + sourceFile_(sourceFile), + targetFile_(targetFile), + onUpdateCopyStatus_(onUpdateCopyStatus), + fileInfoSrc(), + fileInfoTrg(), + bytesReported() {} + + const Zstring& sourceFile_; + const Zstring& targetFile_; + const std::function& onUpdateCopyStatus_; + + ErrorHandling errorHandler; + BY_HANDLE_FILE_INFORMATION fileInfoSrc; //modified by CopyFileEx() at beginning + BY_HANDLE_FILE_INFORMATION fileInfoTrg; // + + std::int64_t bytesReported; //used internally to calculate bytes transferred delta +}; + + +DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, + LARGE_INTEGER totalBytesTransferred, + LARGE_INTEGER streamSize, + LARGE_INTEGER streamBytesTransferred, + DWORD dwStreamNumber, + DWORD dwCallbackReason, + HANDLE hSourceFile, + HANDLE hDestinationFile, + LPVOID lpData) +{ + /* + this callback is invoked for block sizes managed by Windows, these may vary from e.g. 64 kB up to 1MB. It seems this depends on file size amongst others. + + symlink handling: + if source is a symlink and COPY_FILE_COPY_SYMLINK is specified, this callback is NOT invoked! + if source is a symlink and COPY_FILE_COPY_SYMLINK is NOT specified, this callback is called and hSourceFile is a handle to the *target* of the link! + + file time handling: + ::CopyFileEx() will (only) copy file modification time over from source file AFTER the last invokation of this callback + => it is possible to adapt file creation time of target in here, but NOT file modification time! + CAVEAT: if ::CopyFileEx() fails to set modification time, it silently ignores this error and returns success!!! + see procmon log in: https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 + + alternate data stream handling: + CopyFileEx() processes multiple streams one after another, stream 1 is the file data stream and always available! + Each stream is initialized with CALLBACK_STREAM_SWITCH and provides *new* hSourceFile, hDestinationFile. + Calling GetFileInformationByHandle() on hDestinationFile for stream > 1 results in ERROR_ACCESS_DENIED! + totalBytesTransferred contains size of *all* streams and so can be larger than the "file size" file attribute + */ + + CallbackData& cbd = *static_cast(lpData); + + if (dwCallbackReason == CALLBACK_STREAM_SWITCH && //called up-front for every file (even if 0-sized) + dwStreamNumber == 1) //consider ADS! + { + //#################### return source file attributes ################################ + if (!::GetFileInformationByHandle(hSourceFile, &cbd.fileInfoSrc)) + { + const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.sourceFile_)), formatSystemError(L"GetFileInformationByHandle", lastError)); + return PROGRESS_CANCEL; + } + + if (!::GetFileInformationByHandle(hDestinationFile, &cbd.fileInfoTrg)) + { + const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.targetFile_)), formatSystemError(L"GetFileInformationByHandle", lastError)); + return PROGRESS_CANCEL; + } + + //#################### switch to sparse file copy if req. ####################### + if (canCopyAsSparse(cbd.fileInfoSrc.dwFileAttributes, cbd.targetFile_)) //throw () + { + cbd.errorHandler.reportErrorShouldCopyAsSparse(); //use a different copy routine! + return PROGRESS_CANCEL; + } + + //#################### copy file creation time ################################ + ::SetFileTime(hDestinationFile, &cbd.fileInfoSrc.ftCreationTime, nullptr, nullptr); //no error handling! + //=> not really needed here, creation time is set anyway at the end of copyFileWindowsDefault()! + + //#################### copy NTFS compressed attribute ######################### + const bool sourceIsCompressed = (cbd.fileInfoSrc.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; + const bool targetIsCompressed = (cbd.fileInfoTrg.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; //already set by CopyFileEx if target parent folder is compressed! + if (sourceIsCompressed && !targetIsCompressed) + { + USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; + DWORD bytesReturned = 0; + if (!::DeviceIoControl(hDestinationFile, //_In_ HANDLE hDevice, + FSCTL_SET_COMPRESSION, //_In_ DWORD dwIoControlCode, + &cmpState, //_In_opt_ LPVOID lpInBuffer, + sizeof(cmpState), //_In_ DWORD nInBufferSize, + nullptr, //_Out_opt_ LPVOID lpOutBuffer, + 0, //_In_ DWORD nOutBufferSize, + &bytesReturned, //_Out_opt_ LPDWORD lpBytesReturned + nullptr)) //_Inout_opt_ LPOVERLAPPED lpOverlapped + {} //may legitimately fail with ERROR_INVALID_FUNCTION if + + // - if target folder is encrypted + // - target volume does not support compressed attribute + //############################################################################# + } + } + + //called after copy operation is finished - note: for 0-sized files this callback is invoked just ONCE! + //if (totalFileSize.QuadPart == totalBytesTransferred.QuadPart && dwStreamNumber == 1) {} + if (cbd.onUpdateCopyStatus_ && totalBytesTransferred.QuadPart >= 0) //should be always true, but let's still check + try + { + cbd.onUpdateCopyStatus_(totalBytesTransferred.QuadPart - cbd.bytesReported); //throw X! + cbd.bytesReported = totalBytesTransferred.QuadPart; + } + catch (...) + { + cbd.errorHandler.reportUserException(std::current_exception()); + return PROGRESS_CANCEL; + } + return PROGRESS_CONTINUE; +} + + +const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destination is not supported with Windows 2000 +//caveat: function scope static initialization is not thread-safe in VS 2010! + + +void copyFileWindowsDefault(const Zstring& sourceFile, + const Zstring& targetFile, + const std::function& onUpdateCopyStatus, + InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse +{ + //try to get backup read and write privileges: who knows, maybe this helps solve some obscure "access denied" errors + try { activatePrivilege(SE_BACKUP_NAME); } + catch (const FileError&) {} + try { activatePrivilege(SE_RESTORE_NAME); } + catch (const FileError&) {} + + zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); + //transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we? ;) + + DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS; + + if (supportNonEncryptedDestination) + copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrypted location + + //if (vistaOrLater()) //see http://blogs.technet.com/b/askperf/archive/2007/05/08/slow-large-file-copy-issues.aspx + // copyFlags |= COPY_FILE_NO_BUFFERING; //no perf difference at worst, huge improvement for large files (20% in test NTFS -> NTFS) + //It's a shame this flag causes file corruption! https://sourceforge.net/projects/freefilesync/forums/forum/847542/topic/5177950 + //documentation on CopyFile2() even states: "It is not recommended to pause copies that are using this flag." How dangerous is this thing, why offer it at all??? + //perf advantage: ~15% faster + + CallbackData cbd(onUpdateCopyStatus, sourceFile, targetFile); + + const bool success = ::CopyFileEx(applyLongPathPrefix(sourceFile).c_str(), //__in LPCTSTR lpExistingFileName, + applyLongPathPrefix(targetFile).c_str(), //__in LPCTSTR lpNewFileName, + copyCallbackInternal, //__in_opt LPPROGRESS_ROUTINE lpProgressRoutine, + &cbd, //__in_opt LPVOID lpData, + nullptr, //__in_opt LPBOOL pbCancel, + copyFlags) != FALSE; //__in DWORD dwCopyFlags + + cbd.errorHandler.evaluateErrors(); //throw ?, process errors in callback first! + if (!success) + { + const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! + + //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition! + + //trying to copy huge sparse files may directly fail with ERROR_DISK_FULL before entering the callback function + if (canCopyAsSparse(sourceFile, targetFile)) //noexcept + throw ErrorShouldCopyAsSparse(L"sparse dummy value2"); + + //assemble error message... + const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile)); + std::wstring errorDescr = formatSystemError(L"CopyFileEx", lastError); + + //if file is locked throw "ErrorFileLocked" instead! + if (lastError == ERROR_SHARING_VIOLATION || + lastError == ERROR_LOCK_VIOLATION) + { + const Zstring procList = getLockingProcessNames(sourceFile); //throw() -> enhance error message! + if (!procList.empty()) + errorDescr = _("The file is locked by another process:") + L"\n" + procList; + throw ErrorFileLocked(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), errorDescr); + } + + //if target is existing this functions is expected to throw ErrorTargetExisting!!! + if (lastError == ERROR_FILE_EXISTS || //confirmed to be used + lastError == ERROR_ALREADY_EXISTS) //not sure if used -> better be safe than sorry!!! + { + guardTarget.dismiss(); //don't delete file that existed previously! + throw ErrorTargetExisting(errorMsg, errorDescr); + } + + //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); //could this also be source path missing!? + + try //add more meaningful message + { + //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 && + isFatDrive(targetFile) && + getFilesize(sourceFile) >= 4U * std::uint64_t(1024U * 1024 * 1024)) //throw FileError + errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabyte."; + //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us + + //note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filepath is of a restricted type. + } + catch (FileError&) {} + + throw FileError(errorMsg, errorDescr); + } + + if (newAttrib) + { + newAttrib->fileSize = get64BitUInt(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh); + newAttrib->modificationTime = filetimeToTimeT(cbd.fileInfoSrc.ftLastWriteTime); + newAttrib->sourceFileId = extractFileId(cbd.fileInfoSrc); + newAttrib->targetFileId = extractFileId(cbd.fileInfoTrg); + } + warn_static("new perf check + investigate improvements now that DST hcak is gone! =>") + + //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking! + // - perf-loss on USB sticks with many small files of about 30%! + setFileTimeRaw(targetFile, &cbd.fileInfoSrc.ftCreationTime, cbd.fileInfoSrc.ftLastWriteTime, ProcSymlink::FOLLOW); //throw FileError + + guardTarget.dismiss(); //target has been created successfully! +} + + +//another layer to support copying sparse files +inline +void copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targetFile, const std::function& onUpdateCopyStatus, InSyncAttributes* sourceAttr) +{ + try + { + copyFileWindowsDefault(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse + } + catch (ErrorShouldCopyAsSparse&) //we quickly check for this condition within callback of ::CopyFileEx()! + { + copyFileWindowsSparse(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked + } +} + + +//another layer of indirection solving 8.3 name clashes +inline +void copyFileWindows(const Zstring& sourceFile, + const Zstring& targetFile, + const std::function& onUpdateCopyStatus, + InSyncAttributes* sourceAttr) +{ + try + { + copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked + } + catch (const ErrorTargetExisting&) + { + //try to handle issues with already existing short 8.3 file names on Windows + if (have8dot3NameClash(targetFile)) + { + Fix8Dot3NameClash dummy(targetFile); //throw FileError; move clashing filepath to the side + copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError; the short filepath name clash is solved, this should work now + return; + } + throw; + } +} + + +#elif defined ZEN_LINUX || defined ZEN_MAC +void copyFileLinuxMac(const Zstring& sourceFile, + const Zstring& targetFile, + const std::function& onUpdateCopyStatus, + InSyncAttributes* newAttrib) //throw FileError, ErrorTargetExisting +{ + FileInputUnbuffered fileIn(sourceFile); //throw FileError + + struct ::stat sourceInfo = {}; + if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), L"fstat", getLastError()); + + zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: place guard before lifetime of FileOutput + try + { + FileOutputUnbuffered fileOut(targetFile, sourceInfo.st_mode); //throw FileError, ErrorTargetExisting + + std::vector buffer(128 * 1024); //see comment in FileInputUnbuffered::read + do + { + const size_t bytesRead = fileIn.read(&buffer[0], buffer.size()); //throw FileError + + fileOut.write(&buffer[0], bytesRead); //throw FileError + + if (onUpdateCopyStatus) + onUpdateCopyStatus(bytesRead); //throw X! + } + while (!fileIn.eof()); + + //adapt target file modification time: + { + //read and return file statistics + struct ::stat targetInfo = {}; + if (::fstat(fileOut.getDescriptor(), &targetInfo) != 0) + throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), L"fstat", getLastError()); + + if (newAttrib) + { + newAttrib->fileSize = sourceInfo.st_size; + newAttrib->modificationTime = sourceInfo.st_mtime; + newAttrib->sourceFileId = extractFileId(sourceInfo); + newAttrib->targetFileId = extractFileId(targetInfo); + } + } + } + catch (const ErrorTargetExisting&) + { + guardTarget.dismiss(); //don't delete file that existed previously! + throw; + } + + //we cannot set the target file times while the file descriptor is open and being written: + //this triggers bugs on samba shares where the modification time is set to current time instead. + //http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236 + //http://comments.gmane.org/gmane.linux.file-systems.cifs/2854 + //on the other hand we thereby have to reopen https://sourceforge.net/p/freefilesync/bugs/230/ + setFileTime(targetFile, sourceInfo.st_mtime, ProcSymlink::FOLLOW); //throw FileError + + guardTarget.dismiss(); //target has been created successfully! +} +#endif + +/* + ------------------ + |File Copy Layers| + ------------------ + copyFile (setup transactional behavior) + | + copyFileSelectOs + / \ +copyFileLinuxMac copyFileWindows (solve 8.3 issue) + | + copyFileWindowsSelectRoutine + / \ +copyFileWindowsDefault(::CopyFileEx) copyFileWindowsSparse(::BackupRead/::BackupWrite) +*/ + +inline +void copyFileSelectOs(const Zstring& sourceFile, + const Zstring& targetFile, + bool copyFilePermissions, + const std::function& onUpdateCopyStatus, + InSyncAttributes* sourceAttr) +{ +#ifdef ZEN_WIN + copyFileWindows(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked + +#elif defined ZEN_LINUX || defined ZEN_MAC + copyFileLinuxMac(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting +#endif + + if (copyFilePermissions) + { + //at this point we know we created a new file, so it's fine to delete it for cleanup! + zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}}); + + copyObjectPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError + + guardTargetFile.dismiss(); //target has been created successfully! + } +} +} + + +void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorFileLocked + const Zstring& targetFile, + bool copyFilePermissions, + bool transactionalCopy, + const std::function& onDeleteTargetFile, + const std::function& onUpdateCopyStatus, + InSyncAttributes* sourceAttr) +{ + if (transactionalCopy) + { + Zstring tmpTarget = targetFile + TEMP_FILE_ENDING; + + for (int i = 0;; ++i) + try + { + copyFileSelectOs(sourceFile, tmpTarget, copyFilePermissions, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked + break; + } + catch (const ErrorTargetExisting&) //optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses + { + if (i == 10) throw; //avoid endless recursion in pathological cases, e.g. https://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/36adac33 + tmpTarget = targetFile + Zchar('_') + numberTo(i) + TEMP_FILE_ENDING; + } + + //transactional behavior: ensure cleanup; not needed before copyFileSelectOs() which is already transactional + zen::ScopeGuard guardTempFile = zen::makeGuard([&] { try { removeFile(tmpTarget); } catch (FileError&) {} }); + + //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite + if (onDeleteTargetFile) + onDeleteTargetFile(); //throw X + + //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! + renameFile(tmpTarget, targetFile); //throw FileError + + /* + CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" does + NOT PRESERVE the creation time of the .ffs_tmp file, but SILENTLY "reuses" whatever creation time the old "file.txt" had! + This "feature" is called "File System Tunneling": + http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx + http://support.microsoft.com/kb/172190/en-us + */ + + guardTempFile.dismiss(); + } + else + { + /* + Note: non-transactional file copy solves at least four problems: + -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER + -> network renaming issues + -> allow for true delete before copy to handle low disk space problems + -> higher performance on non-buffered drives (e.g. usb sticks) + */ + + if (onDeleteTargetFile) + onDeleteTargetFile(); + + copyFileSelectOs(sourceFile, targetFile, copyFilePermissions, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetExisting, ErrorFileLocked + } +} diff --git a/zen/file_access.h b/zen/file_access.h new file mode 100644 index 00000000..7cac889c --- /dev/null +++ b/zen/file_access.h @@ -0,0 +1,80 @@ +// ************************************************************************** +// * 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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * +// ************************************************************************** + +#ifndef FILE_ACCESS_H_8017341345614857 +#define FILE_ACCESS_H_8017341345614857 + +#include +#include "zstring.h" +#include "file_error.h" +#include "file_id_def.h" + +namespace zen +{ +bool fileExists (const Zstring& filepath); //noexcept; check whether file or file-symlink exists +bool dirExists (const Zstring& dirpath ); //noexcept; check whether directory or dir-symlink exists +bool symlinkExists (const Zstring& linkname); //noexcept; check whether a symbolic link exists +bool somethingExists(const Zstring& objname ); //noexcept; check whether any object with this name exists + +enum class ProcSymlink +{ + DIRECT, + FOLLOW +}; + +void setFileTime(const Zstring& filepath, std::int64_t modificationTime, ProcSymlink procSl); //throw FileError + +//symlink handling: always evaluate target +std::uint64_t getFilesize(const Zstring& filepath); //throw FileError +std::uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError + +bool removeFile(const Zstring& filepath); //throw FileError; return "false" if file is not existing +void removeDirectory(const Zstring& directory, //throw FileError + const std::function& onBeforeFileDeletion = nullptr, //optional; + const std::function& onBeforeDirDeletion = nullptr); //one call for each *existing* object! + +//rename file or directory: no copying!!! +void renameFile(const Zstring& oldName, const Zstring& newName); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting + +bool supportsPermissions(const Zstring& dirpath); //throw FileError, dereferences symlinks + +//if parent directory not existing: create recursively: +void makeDirectory(const Zstring& directory, bool failIfExists = false); //throw FileError, ErrorTargetExisting + +//fail if already existing or parent directory not existing: +//directory should not end with path separator +//templateDir may be empty +void makeDirectoryPlain(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing + +struct InSyncAttributes +{ + std::uint64_t fileSize; + std::int64_t modificationTime; //time_t UTC compatible + FileId sourceFileId; + FileId targetFileId; +}; + +void copyFile(const Zstring& sourceFile, //throw FileError, ErrorFileLocked (Windows-only) + const Zstring& targetFile, //symlink handling: dereference source + bool copyFilePermissions, + bool transactionalCopy, + //if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing! + //if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it. + const std::function& onDeleteTargetFile, //may be nullptr; may throw! + //accummulated delta != file size! consider ADS, sparse, compressed files + const std::function& onUpdateCopyStatus, //may be nullptr; may throw! + + InSyncAttributes* newAttrib = nullptr); //return current attributes at the time of copy + +//Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop. +// => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: + +const Zchar TEMP_FILE_ENDING[] = Zstr(".ffs_tmp"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace! + +void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError +} + +#endif //FILE_ACCESS_H_8017341345614857 diff --git a/zen/file_handling.cpp b/zen/file_handling.cpp deleted file mode 100644 index 75062462..00000000 --- a/zen/file_handling.cpp +++ /dev/null @@ -1,2328 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#include "file_handling.h" -#include -#include -#include -#include "int64.h" -#include "file_traverser.h" -#include "scope_guard.h" -#include "symlink_target.h" -#include "file_io.h" -#include "assert_static.h" -#include "file_id_def.h" - -#ifdef ZEN_WIN -#include -#include "privilege.h" -#include "dll.h" -#include "long_path_prefix.h" -#include "dst_hack.h" -#include "win_ver.h" -#include "IFileOperation/file_op.h" - -#elif defined ZEN_LINUX -#include //statfs -#include //AT_SYMLINK_NOFOLLOW, UTIME_OMIT -#ifdef HAVE_SELINUX -#include -#endif - -#elif defined ZEN_MAC -#include //statfs -#endif - -#if defined ZEN_LINUX || defined ZEN_MAC -#include -#include //lutimes -#endif - -using namespace zen; - - -bool zen::fileExists(const Zstring& filepath) -{ - //symbolic links (broken or not) are also treated as existing files! -#ifdef ZEN_WIN - const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str()); - if (attr != INVALID_FILE_ATTRIBUTES) - return (attr & FILE_ATTRIBUTE_DIRECTORY) == 0; //returns true for (file-)symlinks also - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat fileInfo = {}; - if (::stat(filepath.c_str(), &fileInfo) == 0) //follow symlinks! - return S_ISREG(fileInfo.st_mode); -#endif - return false; -} - - -bool zen::dirExists(const Zstring& dirpath) -{ - //symbolic links (broken or not) are also treated as existing directories! -#ifdef ZEN_WIN - const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(dirpath).c_str()); - if (attr != INVALID_FILE_ATTRIBUTES) - return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0; //returns true for (dir-)symlinks also - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat dirInfo = {}; - if (::stat(dirpath.c_str(), &dirInfo) == 0) //follow symlinks! - return S_ISDIR(dirInfo.st_mode); -#endif - return false; -} - - -bool zen::symlinkExists(const Zstring& linkname) -{ -#ifdef ZEN_WIN - WIN32_FIND_DATA linkInfo = {}; - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(linkname).c_str(), &linkInfo); - if (searchHandle != INVALID_HANDLE_VALUE) - { - ::FindClose(searchHandle); - return isSymlink(linkInfo); - } - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat linkInfo = {}; - if (::lstat(linkname.c_str(), &linkInfo) == 0) - return S_ISLNK(linkInfo.st_mode); -#endif - return false; -} - - -bool zen::somethingExists(const Zstring& objname) -{ -#ifdef ZEN_WIN - const DWORD attr = ::GetFileAttributes(applyLongPathPrefix(objname).c_str()); - if (attr != INVALID_FILE_ATTRIBUTES) - return true; - const DWORD lastError = ::GetLastError(); - - //handle obscure file permission problem where ::GetFileAttributes() fails with ERROR_ACCESS_DENIED or ERROR_SHARING_VIOLATION - //while parent directory traversal is successful: e.g. "C:\pagefile.sys" - if (lastError != ERROR_PATH_NOT_FOUND && //perf: short circuit for common "not existing" error codes - lastError != ERROR_FILE_NOT_FOUND && // - lastError != ERROR_BAD_NETPATH && // - lastError != ERROR_BAD_NET_NAME) // - { - WIN32_FIND_DATA fileInfo = {}; - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(objname).c_str(), &fileInfo); - if (searchHandle != INVALID_HANDLE_VALUE) - { - ::FindClose(searchHandle); - return true; - } - } - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat fileInfo = {}; - if (::lstat(objname.c_str(), &fileInfo) == 0) - return true; -#endif - return false; -} - - -namespace -{ -#ifdef ZEN_WIN -//(try to) enhance error messages by showing which processes lock the file -Zstring getLockingProcessNames(const Zstring& filepath) //throw(), empty string if none found or error occurred -{ - if (vistaOrLater()) - { - using namespace fileop; - const DllFun getLockingProcesses(getDllName(), funName_getLockingProcesses); - const DllFun freeString (getDllName(), funName_freeString); - - if (getLockingProcesses && freeString) - if (const wchar_t* procList = getLockingProcesses(filepath.c_str())) - { - ZEN_ON_SCOPE_EXIT(freeString(procList)); - return procList; - } - } - return Zstring(); -} -#endif -} - - -std::uint64_t zen::getFilesize(const Zstring& filepath) //throw FileError -{ -#ifdef ZEN_WIN - WIN32_FIND_DATA fileInfo = {}; - { - const HANDLE searchHandle = ::FindFirstFile(applyLongPathPrefix(filepath).c_str(), &fileInfo); - if (searchHandle == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"FindFirstFile", getLastError()); - ::FindClose(searchHandle); - } - // WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; - // if (!::GetFileAttributesEx(applyLongPathPrefix(sourceObj).c_str(), //__in LPCTSTR lpFileName, - // GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, - // &sourceAttr)) //__out LPVOID lpFileInformation - - if (!isSymlink(fileInfo)) - return get64BitUInt(fileInfo.nFileSizeLow, fileInfo.nFileSizeHigh); - else - { - //open handle to target of symbolic link - const HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, - 0, //_In_ DWORD dwDesiredAccess, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - if (hFile == INVALID_HANDLE_VALUE) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"CreateFile", getLastError()); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - - BY_HANDLE_FILE_INFORMATION fileInfoHnd = {}; - if (!::GetFileInformationByHandle(hFile, &fileInfoHnd)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileInformationByHandle", getLastError()); - - return get64BitUInt(fileInfoHnd.nFileSizeLow, fileInfoHnd.nFileSizeHigh); - } - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat fileInfo = {}; - if (::stat(filepath.c_str(), &fileInfo) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"stat", getLastError()); - - return fileInfo.st_size; -#endif -} - - -std::uint64_t zen::getFreeDiskSpace(const Zstring& path) //throw FileError -{ -#ifdef ZEN_WIN - ULARGE_INTEGER bytesFree = {}; - if (!::GetDiskFreeSpaceEx(appendSeparator(path).c_str(), //__in_opt LPCTSTR lpDirectoryName, -> "UNC name [...] must include a trailing backslash, for example, "\\MyServer\MyShare\" - &bytesFree, //__out_opt PULARGE_INTEGER lpFreeBytesAvailable, - nullptr, //__out_opt PULARGE_INTEGER lpTotalNumberOfBytes, - nullptr)) //__out_opt PULARGE_INTEGER lpTotalNumberOfFreeBytes - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), L"GetDiskFreeSpaceEx", getLastError()); - - return get64BitUInt(bytesFree.LowPart, bytesFree.HighPart); - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct statfs info = {}; - if (::statfs(path.c_str(), &info) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(path)), L"statfs", getLastError()); - - return static_cast(info.f_bsize) * info.f_bavail; -#endif -} - - -bool zen::removeFile(const Zstring& filepath) //throw FileError -{ -#ifdef ZEN_WIN - const wchar_t functionName[] = L"DeleteFile"; - const Zstring& filepathFmt = applyLongPathPrefix(filepath); - if (!::DeleteFile(filepathFmt.c_str())) -#elif defined ZEN_LINUX || defined ZEN_MAC - const wchar_t functionName[] = L"unlink"; - if (::unlink(filepath.c_str()) != 0) -#endif - { - ErrorCode lastError = getLastError(); -#ifdef ZEN_WIN - if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only - { - ::SetFileAttributes(filepathFmt.c_str(), FILE_ATTRIBUTE_NORMAL); //(try to) normalize file attributes - - if (::DeleteFile(filepathFmt.c_str())) //now try again... - return true; - lastError = ::GetLastError(); - } -#endif - if (!somethingExists(filepath)) //warning: changes global error code!! - return false; //neither file nor any other object (e.g. broken symlink) with that name existing - caveat: what if "access is denied"!?!??!?!? - - //begin of "regular" error reporting - const std::wstring errorMsg = replaceCpy(_("Cannot delete file %x."), L"%x", fmtFileName(filepath)); - std::wstring errorDescr = formatSystemError(functionName, lastError); - -#ifdef ZEN_WIN - if (lastError == ERROR_SHARING_VIOLATION || //-> enhance error message! - lastError == ERROR_LOCK_VIOLATION) - { - const Zstring procList = getLockingProcessNames(filepath); //throw() - if (!procList.empty()) - errorDescr = _("The file is locked by another process:") + L"\n" + procList; - } -#endif - throw FileError(errorMsg, errorDescr); - } - return true; -} - - -namespace -{ -/* Usage overview: (avoid circular pattern!) - - renameFile() --> renameFile_sub() - | /|\ - \|/ | - Fix8Dot3NameClash() -*/ -//wrapper for file system rename function: -void renameFile_sub(const Zstring& oldName, const Zstring& newName) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting -{ -#ifdef ZEN_WIN - const Zstring oldNameFmt = applyLongPathPrefix(oldName); - const Zstring newNameFmt = applyLongPathPrefix(newName); - - if (!::MoveFileEx(oldNameFmt.c_str(), //__in LPCTSTR lpExistingFileName, - newNameFmt.c_str(), //__in_opt LPCTSTR lpNewFileName, - 0)) //__in DWORD dwFlags - { - DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - - if (lastError == ERROR_ACCESS_DENIED) //MoveFileEx may fail to rename a read-only file on a SAMBA-share -> (try to) handle this - { - const DWORD oldAttr = ::GetFileAttributes(oldNameFmt.c_str()); - if (oldAttr != INVALID_FILE_ATTRIBUTES && (oldAttr & FILE_ATTRIBUTE_READONLY)) - { - if (::SetFileAttributes(oldNameFmt.c_str(), FILE_ATTRIBUTE_NORMAL)) //remove readonly-attribute - { - //try again... - if (::MoveFileEx(oldNameFmt.c_str(), //__in LPCTSTR lpExistingFileName, - newNameFmt.c_str(), //__in_opt LPCTSTR lpNewFileName, - 0)) //__in DWORD dwFlags - { - //(try to) restore file attributes - ::SetFileAttributes(newNameFmt.c_str(), oldAttr); //don't handle error - return; - } - else - { - lastError = ::GetLastError(); //use error code from second call to ::MoveFileEx() - //cleanup: (try to) restore file attributes: assume oldName is still existing - ::SetFileAttributes(oldNameFmt.c_str(), oldAttr); - } - } - } - } - - const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)); - std::wstring errorDescr = formatSystemError(L"MoveFileEx", lastError); - - //try to enhance error message: - if (lastError == ERROR_SHARING_VIOLATION || - lastError == ERROR_LOCK_VIOLATION) - { - const Zstring procList = getLockingProcessNames(oldName); //throw() - if (!procList.empty()) - errorDescr = _("The file is locked by another process:") + L"\n" + procList; - } - - if (lastError == ERROR_NOT_SAME_DEVICE) - throw ErrorDifferentVolume(errorMsg, errorDescr); - else if (lastError == ERROR_ALREADY_EXISTS || //-> used on Win7 x64 - lastError == ERROR_FILE_EXISTS) //-> used by XP??? - throw ErrorTargetExisting(errorMsg, errorDescr); - else - throw FileError(errorMsg, errorDescr); - } - -#elif defined ZEN_LINUX || defined ZEN_MAC - if (::rename(oldName.c_str(), newName.c_str()) != 0) - { - const int lastError = errno; //copy before directly or indirectly making other system calls! - const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot move file %x to %y."), L"%x", L"\n" + fmtFileName(oldName)), L"%y", L"\n" + fmtFileName(newName)); - const std::wstring errorDescr = formatSystemError(L"rename", lastError); - - if (lastError == EXDEV) - throw ErrorDifferentVolume(errorMsg, errorDescr); - else if (lastError == EEXIST) - throw ErrorTargetExisting(errorMsg, errorDescr); - else - throw FileError(errorMsg, errorDescr); - } -#endif -} - - -#ifdef ZEN_WIN -/*small wrapper around -::GetShortPathName() -::GetLongPathName() */ -template -Zstring getFilenameFmt(const Zstring& filepath, Function fun) //throw(); returns empty string on error -{ - const Zstring filepathFmt = applyLongPathPrefix(filepath); - - const DWORD bufferSize = fun(filepathFmt.c_str(), nullptr, 0); - if (bufferSize == 0) - return Zstring(); - - std::vector buffer(bufferSize); - - const DWORD charsWritten = fun(filepathFmt.c_str(), //__in LPCTSTR lpszShortPath, - &buffer[0], //__out LPTSTR lpszLongPath, - bufferSize); //__in DWORD cchBuffer - if (charsWritten == 0 || charsWritten >= bufferSize) - return Zstring(); - - return &buffer[0]; -} - - -Zstring findUnused8Dot3Name(const Zstring& filepath) //find a unique 8.3 short name -{ - const Zstring pathPrefix = contains(filepath, FILE_NAME_SEPARATOR) ? - (beforeLast(filepath, FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR) : Zstring(); - - Zstring extension = afterLast(afterLast(filepath, FILE_NAME_SEPARATOR), Zchar('.')); //extension needn't contain reasonable data - if (extension.empty()) - extension = Zstr("FFS"); - else if (extension.length() > 3) - extension.resize(3); - - for (int index = 0; index < 100000000; ++index) //filepath must be representable by <= 8 characters - { - const Zstring output = pathPrefix + numberTo(index) + Zchar('.') + extension; - 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...") + utfCvrtTo(pathPrefix)); -} - - -bool have8dot3NameClash(const Zstring& filepath) -{ - if (!contains(filepath, FILE_NAME_SEPARATOR)) - return false; - - if (somethingExists(filepath)) //name OR directory! - { - const Zstring origName = afterLast(filepath, FILE_NAME_SEPARATOR); //returns the whole string if ch not found - const Zstring shortName = afterLast(getFilenameFmt(filepath, ::GetShortPathName), FILE_NAME_SEPARATOR); //throw() returns empty string on error - const Zstring longName = afterLast(getFilenameFmt(filepath, ::GetLongPathName ), FILE_NAME_SEPARATOR); // - - if (!shortName.empty() && - !longName .empty() && - EqualFilename()(origName, shortName) && - !EqualFilename()(shortName, longName)) - { - //for filepath short and long file name are equal and another unrelated file happens to have the same short name - //e.g. filepath == "TESTWE~1", but another file is existing named "TestWeb" with short name ""TESTWE~1" - return true; - } - } - return false; -} - -class Fix8Dot3NameClash //throw FileError -{ -public: - Fix8Dot3NameClash(const Zstring& filepath) - { - const Zstring longName = afterLast(getFilenameFmt(filepath, ::GetLongPathName), FILE_NAME_SEPARATOR); //throw() returns empty string on error - - unrelatedFile = beforeLast(filepath, FILE_NAME_SEPARATOR) + FILE_NAME_SEPARATOR + longName; - - //find another name in short format: this ensures the actual short name WILL be renamed as well! - unrelatedFileParked = findUnused8Dot3Name(filepath); - - //move already existing short name out of the way for now - renameFile_sub(unrelatedFile, unrelatedFileParked); //throw FileError, ErrorDifferentVolume - //DON'T call renameFile() to avoid reentrance! - } - - ~Fix8Dot3NameClash() - { - //the file system should assign this unrelated file a new (unique) short name - try - { - renameFile_sub(unrelatedFileParked, unrelatedFile); //throw FileError, ErrorDifferentVolume - } - catch (FileError&) {} - } -private: - Zstring unrelatedFile; - Zstring unrelatedFileParked; -}; -#endif -} - - -//rename file: no copying!!! -void zen::renameFile(const Zstring& oldName, const Zstring& newName) //throw FileError, ErrorDifferentVolume, ErrorTargetExisting -{ - try - { - renameFile_sub(oldName, newName); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - } - catch (const ErrorTargetExisting&) - { -#ifdef ZEN_WIN - //try to handle issues with already existing short 8.3 file names on Windows - if (have8dot3NameClash(newName)) - { - Fix8Dot3NameClash dummy(newName); //throw FileError; move clashing filepath to the side - //now try again... - renameFile_sub(oldName, newName); //throw FileError - return; - } -#endif - throw; - } -} - - -namespace -{ -class CollectFilesFlat : public zen::TraverseCallback -{ -public: - CollectFilesFlat(std::vector& files, std::vector& dirs) : - files_(files), - dirs_(dirs) {} - - virtual void onFile(const Zchar* shortName, const Zstring& filepath, const FileInfo& details) - { - files_.push_back(filepath); - } - virtual HandleLink onSymlink(const Zchar* shortName, const Zstring& linkpath, const SymlinkInfo& details) - { - if (dirExists(linkpath)) //dir symlink - dirs_.push_back(shortName); - else //file symlink, broken symlink - files_.push_back(shortName); - return LINK_SKIP; - } - virtual TraverseCallback* onDir(const Zchar* shortName, const Zstring& dirpath) - { - dirs_.push_back(dirpath); - return nullptr; //DON'T traverse into subdirs; removeDirectory works recursively! - } - virtual HandleError reportDirError (const std::wstring& msg, size_t retryNumber) { throw FileError(msg); } - virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) { throw FileError(msg); } - -private: - CollectFilesFlat (const CollectFilesFlat&) = delete; - CollectFilesFlat& operator=(const CollectFilesFlat&) = delete; - - std::vector& files_; - std::vector& dirs_; -}; - - -void removeDirectoryImpl(const Zstring& directory, //throw FileError - const std::function& onBeforeFileDeletion, - const std::function& onBeforeDirDeletion) -{ - assert(somethingExists(directory)); //[!] - -#ifdef ZEN_WIN - const Zstring directoryFmt = applyLongPathPrefix(directory); //support for \\?\-prefix - - //(try to) normalize file attributes: actually NEEDED for symbolic links also! - ::SetFileAttributes(directoryFmt.c_str(), FILE_ATTRIBUTE_NORMAL); -#endif - - //attention: check if directory is a symlink! Do NOT traverse into it deleting contained files!!! - if (symlinkExists(directory)) //remove symlink directly - { - if (onBeforeDirDeletion) - onBeforeDirDeletion(directory); //once per symlink -#ifdef ZEN_WIN - const wchar_t functionName[] = L"RemoveDirectory"; - if (!::RemoveDirectory(directoryFmt.c_str())) -#elif defined ZEN_LINUX || defined ZEN_MAC - const wchar_t functionName[] = L"unlink"; - if (::unlink(directory.c_str()) != 0) -#endif - throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), functionName, getLastError()); - } - else - { - std::vector fileList; - std::vector dirList; - { - //get all files and directories from current directory (WITHOUT subdirectories!) - CollectFilesFlat cff(fileList, dirList); - traverseFolder(directory, cff); //don't follow symlinks - } - - //delete directories recursively - for (const Zstring& dirpath : dirList) - removeDirectoryImpl(dirpath, onBeforeFileDeletion, onBeforeDirDeletion); //throw FileError; call recursively to correctly handle symbolic links - - //delete files - for (const Zstring& filepath : fileList) - { - if (onBeforeFileDeletion) - onBeforeFileDeletion(filepath); //call once per file - removeFile(filepath); //throw FileError - } - - //parent directory is deleted last - if (onBeforeDirDeletion) - onBeforeDirDeletion(directory); //and once per folder -#ifdef ZEN_WIN - const wchar_t functionName[] = L"RemoveDirectory"; - if (!::RemoveDirectory(directoryFmt.c_str())) -#elif defined ZEN_LINUX || defined ZEN_MAC - const wchar_t functionName[] = L"rmdir"; - if (::rmdir(directory.c_str()) != 0) -#endif - throwFileError(replaceCpy(_("Cannot delete directory %x."), L"%x", fmtFileName(directory)), functionName, getLastError()); - //may spuriously fail with ERROR_DIR_NOT_EMPTY(145) even though all child items have - //successfully been *marked* for deletion, but some application still has a handle open! - //e.g. Open "C:\Test\Dir1\Dir2" (filled with lots of files) in Explorer, then delete "C:\Test\Dir1" via ::RemoveDirectory() => Error 145 - //Sample code: http://us.generation-nt.com/answer/createfile-directory-handles-removing-parent-help-29126332.html - } -} - - -#ifdef ZEN_WIN -void setFileTimeRaw(const Zstring& filepath, const FILETIME& creationTime, const FILETIME& lastWriteTime, ProcSymlink procSl) //throw FileError -{ - { - //extra scope for debug check below - - //privilege SE_BACKUP_NAME doesn't seem to be required here for symbolic links - //note: setting privileges requires admin rights! - - //opening newly created target file may fail due to some AV-software scanning it: no error, we will wait! - //http://support.microsoft.com/?scid=kb%3Ben-us%3B316609&x=17&y=20 - //-> enable as soon it turns out it is required! - - /*const int retryInterval = 50; - const int maxRetries = 2000 / retryInterval; - for (int i = 0; i < maxRetries; ++i) - { - */ - - /* - if (hTarget == INVALID_HANDLE_VALUE && ::GetLastError() == ERROR_SHARING_VIOLATION) - ::Sleep(retryInterval); //wait then retry - else //success or unknown failure - break; - } - */ - //temporarily reset read-only flag if required - DWORD attribs = INVALID_FILE_ATTRIBUTES; - ZEN_ON_SCOPE_EXIT( - if (attribs != INVALID_FILE_ATTRIBUTES) - ::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), attribs); - ); - - auto removeReadonly = [&]() -> bool //throw FileError; may need to remove the readonly-attribute (e.g. on FAT usb drives) - { - if (attribs == INVALID_FILE_ATTRIBUTES) - { - const DWORD tmpAttr = ::GetFileAttributes(applyLongPathPrefix(filepath).c_str()); - if (tmpAttr == INVALID_FILE_ATTRIBUTES) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(filepath)), L"GetFileAttributes", getLastError()); - - if (tmpAttr & FILE_ATTRIBUTE_READONLY) - { - if (!::SetFileAttributes(applyLongPathPrefix(filepath).c_str(), FILE_ATTRIBUTE_NORMAL)) - throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filepath)), L"SetFileAttributes", getLastError()); - - attribs = tmpAttr; //reapplied on scope exit - return true; - } - } - return false; - }; - - auto openFile = [&](bool conservativeApproach) - { - return ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, - (conservativeApproach ? - //some NAS seem to have issues with FILE_WRITE_ATTRIBUTES, even worse, they may fail silently! - //http://sourceforge.net/tracker/?func=detail&atid=1093081&aid=3536680&group_id=234430 - //Citrix shares seem to have this issue, too, but at least fail with "access denied" => try generic access first: - GENERIC_READ | GENERIC_WRITE : - //avoids mysterious "access denied" when using "GENERIC_READ | GENERIC_WRITE" on a read-only file, even *after* read-only was removed directly before the call! - //http://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 - //since former gives an error notification we may very well try FILE_WRITE_ATTRIBUTES second. - FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES), //_In_ DWORD dwDesiredAccess, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - (procSl == ProcSymlink::DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0) | - FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - }; - - HANDLE hFile = INVALID_HANDLE_VALUE; - for (int i = 0; i < 2; ++i) //we will get this handle, no matter what! :) - { - //1. be conservative - hFile = openFile(true); - if (hFile == INVALID_HANDLE_VALUE) - { - if (::GetLastError() == ERROR_ACCESS_DENIED) //fails if file is read-only (or for "other" reasons) - if (removeReadonly()) //throw FileError - continue; - - //2. be a *little* fancy - hFile = openFile(false); - if (hFile == INVALID_HANDLE_VALUE) - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - if (lastError == ERROR_ACCESS_DENIED) - if (removeReadonly()) //throw FileError - continue; - - //3. after these herculean stunts we give up... - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)), L"CreateFile", lastError); - } - } - break; - } - ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - - - auto isNullTime = [](const FILETIME& ft) { return ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0; }; - - if (!::SetFileTime(hFile, //__in HANDLE hFile, - !isNullTime(creationTime) ? &creationTime : nullptr, //__in_opt const FILETIME *lpCreationTime, - nullptr, //__in_opt const FILETIME *lpLastAccessTime, - &lastWriteTime)) //__in_opt const FILETIME *lpLastWriteTime - { - ErrorCode lastError = getLastError(); //copy before directly or indirectly making other system calls! - - //function may fail if file is read-only: https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 - if (lastError == ERROR_ACCESS_DENIED) - { - //dynamically load windows API function: available with Windows Vista and later - typedef BOOL (WINAPI* SetFileInformationByHandleFunc)(HANDLE hFile, FILE_INFO_BY_HANDLE_CLASS FileInformationClass, LPVOID lpFileInformation, DWORD dwBufferSize); - const SysDllFun setFileInformationByHandle(L"kernel32.dll", "SetFileInformationByHandle"); - - if (setFileInformationByHandle) //if not: let the original error propagate! - { - auto setFileInfo = [&](FILE_BASIC_INFO basicInfo) //throw FileError; no const& since SetFileInformationByHandle() requires non-const parameter! - { - if (!setFileInformationByHandle(hFile, //__in HANDLE hFile, - FileBasicInfo, //__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass, - &basicInfo, //__in LPVOID lpFileInformation, - sizeof(basicInfo))) //__in DWORD dwBufferSize - throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(filepath)), L"SetFileInformationByHandle", getLastError()); - }; - - auto toLargeInteger = [](const FILETIME& ft) -> LARGE_INTEGER - { - LARGE_INTEGER tmp = {}; - tmp.LowPart = ft.dwLowDateTime; - tmp.HighPart = ft.dwHighDateTime; - return tmp; - }; - //--------------------------------------------------------------------------- - - BY_HANDLE_FILE_INFORMATION fileInfo = {}; - if (::GetFileInformationByHandle(hFile, &fileInfo)) - if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) - { - FILE_BASIC_INFO basicInfo = {}; //undocumented: file times of "0" stand for "don't change" - basicInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL; //[!] the bug in the ticket above requires we set this together with file times!!! - basicInfo.LastWriteTime = toLargeInteger(lastWriteTime); // - if (!isNullTime(creationTime)) - basicInfo.CreationTime = toLargeInteger(creationTime); - - //set file time + attributes - setFileInfo(basicInfo); //throw FileError - - try //... to restore original file attributes - { - FILE_BASIC_INFO basicInfo2 = {}; - basicInfo2.FileAttributes = fileInfo.dwFileAttributes; - setFileInfo(basicInfo2); //throw FileError - } - catch (FileError&) {} - - lastError = ERROR_SUCCESS; - } - } - } - - std::wstring errorMsg = replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)); - - //add more meaningful message: FAT accepts only a subset of the NTFS date range - if (lastError == ERROR_INVALID_PARAMETER && - dst::isFatDrive(filepath)) - { - //we need a low-level reliable routine to format a potentially invalid date => don't use strftime!!! - auto fmtDate = [](const FILETIME& ft) -> Zstring - { - SYSTEMTIME st = {}; - if (!::FileTimeToSystemTime(&ft, //__in const FILETIME *lpFileTime, - &st)) //__out LPSYSTEMTIME lpSystemTime - return Zstring(); - - Zstring dateTime; - { - const int bufferSize = ::GetDateFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0); - if (bufferSize > 0) - { - std::vector buffer(bufferSize); - if (::GetDateFormat(LOCALE_USER_DEFAULT, //_In_ LCID Locale, - 0, //_In_ DWORD dwFlags, - &st, //_In_opt_ const SYSTEMTIME *lpDate, - nullptr, //_In_opt_ LPCTSTR lpFormat, - &buffer[0], //_Out_opt_ LPTSTR lpDateStr, - bufferSize) > 0) //_In_ int cchDate - dateTime = &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! - } - } - - const int bufferSize = ::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, nullptr, 0); - if (bufferSize > 0) - { - std::vector buffer(bufferSize); - if (::GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, nullptr, &buffer[0], bufferSize) > 0) - { - dateTime += L" "; - dateTime += &buffer[0]; //GetDateFormat() returns char count *including* 0-termination! - } - } - return dateTime; - }; - - errorMsg += std::wstring(L"\nA FAT volume can only store dates between 1980 and 2107:\n") + - L"\twrite (UTC): \t" + fmtDate(lastWriteTime) + - (!isNullTime(creationTime) ? L"\n\tcreate (UTC): \t" + fmtDate(creationTime) : L""); - } - - if (lastError != ERROR_SUCCESS) - throwFileError(errorMsg, L"SetFileTime", lastError); - } - } -#ifndef NDEBUG //verify written data: mainly required to check consistency of DST hack - FILETIME creationTimeDbg = {}; - FILETIME lastWriteTimeDbg = {}; - - HANDLE hFile = ::CreateFile(applyLongPathPrefix(filepath).c_str(), //_In_ LPCTSTR lpFileName, - FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, //_In_ DWORD dwDesiredAccess, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - (procSl == ProcSymlink::DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0) | - FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - assert(hFile != INVALID_HANDLE_VALUE); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hFile)); - - assert(::GetFileTime(hFile, //probably more up to date than GetFileAttributesEx()!? - &creationTimeDbg, - nullptr, - &lastWriteTimeDbg)); - - assert(std::abs(filetimeToTimeT(lastWriteTimeDbg) - filetimeToTimeT(lastWriteTime)) <= 2); //respect 2 second FAT/FAT32 precision - //assert(std::abs(filetimeToTimeT(creationTimeDbg ) - filetimeToTimeT(creationTime )) <= 2); -> creation time not available for Linux-hosted Samba shares! -#endif -} -#endif -} - - -void zen::removeDirectory(const Zstring& directory, //throw FileError - const std::function& onBeforeFileDeletion, - const std::function& onBeforeDirDeletion) -{ - //no error situation if directory is not existing! manual deletion relies on it! - if (!somethingExists(directory)) - return; //neither directory nor any other object (e.g. broken symlink) with that name existing - removeDirectoryImpl(directory, onBeforeFileDeletion, onBeforeDirDeletion); -} - - -void zen::setFileTime(const Zstring& filepath, std::int64_t modTime, ProcSymlink procSl) //throw FileError -{ -#ifdef ZEN_WIN - FILETIME creationTime = {}; - FILETIME lastWriteTime = timetToFileTime(modTime); - - //####################################### DST hack ########################################### - warn_static("let's tentatively disable the DST hack:") -#if 0 - if (dst::isFatDrive(filepath)) //throw(); hacky: does not consider symlinks pointing to FAT! - { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(lastWriteTime); //throw std::runtime_error - creationTime = encodedTime.createTimeRaw; - lastWriteTime = encodedTime.writeTimeRaw; - } -#endif - //####################################### DST hack ########################################### - - setFileTimeRaw(filepath, creationTime, lastWriteTime, procSl); //throw FileError - -#elif defined ZEN_LINUX || defined ZEN_MAC - //sigh, we can't use utimensat on NTFS volumes on Ubuntu: silent failure!!! what morons are programming this shit??? - - // struct ::timespec newTimes[2] = {}; - // newTimes[0].tv_nsec = UTIME_OMIT; //omit access time - // newTimes[1].tv_sec = to(modTime); //modification time (seconds) - // - // if (::utimensat(AT_FDCWD, filepath.c_str(), newTimes, procSl == SYMLINK_DIRECT ? AT_SYMLINK_NOFOLLOW : 0) != 0) - // throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)), L"utimensat", getLastError()); - - //=> fallback to "retarded-idiot version"! -- DarkByte - - //OS X: utime() is obsoleted by utimes()! utimensat() not yet implemented - - struct ::timeval newTimes[2] = {}; - newTimes[0].tv_sec = ::time(nullptr); //access time (seconds) - newTimes[1].tv_sec = modTime; //modification time (seconds) - - const int rv = procSl == ProcSymlink::FOLLOW ? - :: utimes(filepath.c_str(), newTimes) : - ::lutimes(filepath.c_str(), newTimes); - if (rv != 0) - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(filepath)), L"utimes", getLastError()); -#endif -} - - -bool zen::supportsPermissions(const Zstring& dirpath) //throw FileError -{ -#ifdef ZEN_WIN - const DWORD bufferSize = MAX_PATH + 1; - std::vector buffer(bufferSize); - if (!::GetVolumePathName(dirpath.c_str(), //__in LPCTSTR lpszFileName, - &buffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirpath)), L"GetVolumePathName", getLastError()); - - DWORD fsFlags = 0; - if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName, - nullptr, //__out LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - &fsFlags, //__out_opt LPDWORD lpFileSystemFlags, - nullptr, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(dirpath)), L"GetVolumeInformation", getLastError()); - - return (fsFlags & FILE_PERSISTENT_ACLS) != 0; - -#elif defined ZEN_LINUX || defined ZEN_MAC - return true; -#endif -} - - -namespace -{ -#ifdef HAVE_SELINUX -//copy SELinux security context -void copySecurityContext(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError -{ - security_context_t contextSource = nullptr; - const int rv = procSl == ProcSymlink::FOLLOW ? - ::getfilecon(source.c_str(), &contextSource) : - ::lgetfilecon(source.c_str(), &contextSource); - if (rv < 0) - { - if (errno == ENODATA || //no security context (allegedly) is not an error condition on SELinux - errno == EOPNOTSUPP) //extended attributes are not supported by the filesystem - return; - - throwFileError(replaceCpy(_("Cannot read security context of %x."), L"%x", fmtFileName(source)), L"getfilecon", getLastError()); - } - ZEN_ON_SCOPE_EXIT(::freecon(contextSource)); - - { - security_context_t contextTarget = nullptr; - const int rv2 = procSl == ProcSymlink::FOLLOW ? - ::getfilecon(target.c_str(), &contextTarget) : - ::lgetfilecon(target.c_str(), &contextTarget); - if (rv2 < 0) - { - if (errno == EOPNOTSUPP) - return; - //else: still try to set security context - } - else - { - ZEN_ON_SCOPE_EXIT(::freecon(contextTarget)); - - if (::strcmp(contextSource, contextTarget) == 0) //nothing to do - return; - } - } - - const int rv3 = procSl == ProcSymlink::FOLLOW ? - ::setfilecon(target.c_str(), contextSource) : - ::lsetfilecon(target.c_str(), contextSource); - if (rv3 < 0) - throwFileError(replaceCpy(_("Cannot write security context of %x."), L"%x", fmtFileName(target)), L"setfilecon", getLastError()); -} -#endif //HAVE_SELINUX - - -//copy permissions for files, directories or symbolic links: requires admin rights -void copyObjectPermissions(const Zstring& source, const Zstring& target, ProcSymlink procSl) //throw FileError -{ -#ifdef ZEN_WIN - //in contrast to ::SetSecurityInfo(), ::SetFileSecurity() seems to honor the "inherit DACL/SACL" flags - //CAVEAT: if a file system does not support ACLs, GetFileSecurity() will return successfully with a *valid* security descriptor containing *no* ACL entries! - - //NOTE: ::GetFileSecurity()/::SetFileSecurity() do NOT follow Symlinks! getResolvedFilePath() requires Vista or later! - const Zstring sourceResolved = procSl == ProcSymlink::FOLLOW && symlinkExists(source) ? getResolvedFilePath(source) : source; //throw FileError - const Zstring targetResolved = procSl == ProcSymlink::FOLLOW && symlinkExists(target) ? getResolvedFilePath(target) : target; // - - //setting privileges requires admin rights! - try - { - //enable privilege: required to read/write SACL information (only) - activatePrivilege(SE_SECURITY_NAME); //throw FileError - //Note: trying to copy SACL (SACL_SECURITY_INFORMATION) may return ERROR_PRIVILEGE_NOT_HELD (1314) on Samba shares. This is not due to missing privileges! - //However, this is okay, since copying NTFS permissions doesn't make sense in this case anyway - - //enable privilege: required to copy owner information - activatePrivilege(SE_RESTORE_NAME); //throw FileError - - //the following privilege may be required according to http://msdn.microsoft.com/en-us/library/aa364399(VS.85).aspx (although not needed nor active in my tests) - activatePrivilege(SE_BACKUP_NAME); //throw FileError - } - catch (const FileError& e)//add some more context description (e.g. user is not an admin) - { - throw FileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), e.toString()); - } - - - std::vector buffer(10000); //example of actually required buffer size: 192 bytes - for (;;) - { - DWORD bytesNeeded = 0; - if (::GetFileSecurity(applyLongPathPrefix(sourceResolved).c_str(), //__in LPCTSTR lpFileName, -> long path prefix IS needed, although it is NOT mentioned on MSDN!!! - OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION RequestedInformation, - reinterpret_cast(&buffer[0]), //__out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor, - static_cast(buffer.size()), //__in DWORD nLength, - &bytesNeeded)) //__out LPDWORD lpnLengthNeeded - break; - //failure: ... - if (bytesNeeded > buffer.size()) - buffer.resize(bytesNeeded); - else - throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(sourceResolved)), L"GetFileSecurity", getLastError()); - } - SECURITY_DESCRIPTOR& secDescr = reinterpret_cast(buffer[0]); - - /* - SECURITY_DESCRIPTOR_CONTROL secCtrl = 0; - { - DWORD ctrlRev = 0; - if (!::GetSecurityDescriptorControl(&secDescr, // __in PSECURITY_DESCRIPTOR pSecurityDescriptor, - &secCtrl, // __out PSECURITY_DESCRIPTOR_CONTROL pControl, - &ctrlRev)) //__out LPDWORD lpdwRevision - throw FileErro - } - //interesting flags: - //#define SE_DACL_PRESENT (0x0004) - //#define SE_SACL_PRESENT (0x0010) - //#define SE_DACL_PROTECTED (0x1000) - //#define SE_SACL_PROTECTED (0x2000) - */ - - if (!::SetFileSecurity(applyLongPathPrefix(targetResolved).c_str(), //__in LPCTSTR lpFileName, -> long path prefix IS needed, although it is NOT mentioned on MSDN!!! - OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInformation, - &secDescr)) //__in PSECURITY_DESCRIPTOR pSecurityDescriptor - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(targetResolved)), L"SetFileSecurity", getLastError()); - - /* - PSECURITY_DESCRIPTOR buffer = nullptr; - PSID owner = nullptr; - PSID group = nullptr; - PACL dacl = nullptr; - PACL sacl = nullptr; - - //File Security and Access Rights: http://msdn.microsoft.com/en-us/library/aa364399(v=VS.85).aspx - //SECURITY_INFORMATION Access Rights: http://msdn.microsoft.com/en-us/library/windows/desktop/aa379573(v=vs.85).aspx - 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, - nullptr, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), //FILE_FLAG_BACKUP_SEMANTICS needed to open a directory - nullptr); - if (hSource == INVALID_HANDLE_VALUE) - throw FileError - ZEN_ON_SCOPE_EXIT(::CloseHandle(hSource)); - - // DWORD rc = ::GetNamedSecurityInfo(const_cast(applyLongPathPrefix(source).c_str()), -> does NOT dereference symlinks! - DWORD rc = ::GetSecurityInfo(hSource, //__in LPTSTR pObjectName, - SE_FILE_OBJECT, //__in SE_OBJECT_TYPE ObjectType, - OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | - DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, //__in SECURITY_INFORMATION SecurityInfo, - &owner, //__out_opt PSID *ppsidOwner, - &group, //__out_opt PSID *ppsidGroup, - &dacl, //__out_opt PACL *ppDacl, - &sacl, //__out_opt PACL *ppSacl, - &buffer); //__out_opt PSECURITY_DESCRIPTOR *ppSecurityDescriptor - if (rc != ERROR_SUCCESS) - throw FileError - ZEN_ON_SCOPE_EXIT(::LocalFree(buffer)); - - SECURITY_DESCRIPTOR_CONTROL secCtrl = 0; - { - DWORD ctrlRev = 0; - if (!::GetSecurityDescriptorControl(buffer, // __in PSECURITY_DESCRIPTOR pSecurityDescriptor, - &secCtrl, // __out PSECURITY_DESCRIPTOR_CONTROL pControl, - &ctrlRev))//__out LPDWORD lpdwRevision - throw FileError - } - - //may need to remove the readonly-attribute - FileUpdateHandle targetHandle(target, [=] - { - return ::CreateFile(applyLongPathPrefix(target).c_str(), // lpFileName - GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY, // dwDesiredAccess: all four seem to be required!!! - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // dwShareMode - nullptr, // lpSecurityAttributes - OPEN_EXISTING, // dwCreationDisposition - FILE_FLAG_BACKUP_SEMANTICS | (procSl == SYMLINK_DIRECT ? FILE_FLAG_OPEN_REPARSE_POINT : 0), // dwFlagsAndAttributes - nullptr); // hTemplateFile - }); - - if (targetHandle.get() == INVALID_HANDLE_VALUE) - throw FileError - - SECURITY_INFORMATION secFlags = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; - - //SACL/DACL inheritence flag is NOT copied by default: we have to tell ::SetSecurityInfo(() to enable/disable it manually! - //if (secCtrl & SE_DACL_PRESENT) - secFlags |= (secCtrl & SE_DACL_PROTECTED) ? PROTECTED_DACL_SECURITY_INFORMATION : UNPROTECTED_DACL_SECURITY_INFORMATION; - //if (secCtrl & SE_SACL_PRESENT) - secFlags |= (secCtrl & SE_SACL_PROTECTED) ? PROTECTED_SACL_SECURITY_INFORMATION : UNPROTECTED_SACL_SECURITY_INFORMATION; - - - // rc = ::SetNamedSecurityInfo(const_cast(applyLongPathPrefix(target).c_str()), //__in LPTSTR pObjectName, -> does NOT dereference symlinks! - rc = ::SetSecurityInfo(targetHandle.get(), //__in LPTSTR pObjectName, - SE_FILE_OBJECT, //__in SE_OBJECT_TYPE ObjectType, - secFlags, //__in SECURITY_INFORMATION SecurityInfo, - owner, //__in_opt PSID psidOwner, - group, //__in_opt PSID psidGroup, - dacl, //__in_opt PACL pDacl, - sacl); //__in_opt PACL pSacl - - if (rc != ERROR_SUCCESS) - throw FileError - */ - -#elif defined ZEN_LINUX || defined ZEN_MAC - -#ifdef HAVE_SELINUX //copy SELinux security context - copySecurityContext(source, target, procSl); //throw FileError -#endif - - struct stat fileInfo = {}; - if (procSl == ProcSymlink::FOLLOW) - { - if (::stat(source.c_str(), &fileInfo) != 0) - throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)), L"stat", getLastError()); - - if (::chown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), L"chown", getLastError()); - - if (::chmod(target.c_str(), fileInfo.st_mode) != 0) - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), L"chmod", getLastError()); - } - else - { - if (::lstat(source.c_str(), &fileInfo) != 0) - throwFileError(replaceCpy(_("Cannot read permissions of %x."), L"%x", fmtFileName(source)), L"lstat", getLastError()); - - if (::lchown(target.c_str(), fileInfo.st_uid, fileInfo.st_gid) != 0) // may require admin rights! - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), L"lchown", getLastError()); - - if (!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() - throwFileError(replaceCpy(_("Cannot write permissions of %x."), L"%x", fmtFileName(target)), L"chmod", getLastError()); - } -#endif -} - - -void makeDirectoryRecursively(const Zstring& directory) //FileError, ErrorTargetExisting -{ - assert(!endsWith(directory, FILE_NAME_SEPARATOR)); //even "C:\" should be "C:" as input! - - try - { - makeDirectoryPlain(directory, Zstring(), false); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing - } - catch (const ErrorTargetPathMissing&) - { - //we need to create parent directories first - const Zstring dirParent = beforeLast(directory, FILE_NAME_SEPARATOR); - if (!dirParent.empty()) - { - //recurse... - try - { - makeDirectoryRecursively(dirParent); //throw FileError, (ErrorTargetExisting) - } - catch (const ErrorTargetExisting&) { /*parent directory created externally in the meantime? => NOT AN ERROR*/ } - - //now try again... - makeDirectoryPlain(directory, Zstring(), false); //throw FileError, (ErrorTargetExisting), (ErrorTargetPathMissing) - return; - } - throw; - } -} -} - - -void zen::makeDirectory(const Zstring& directory, bool failIfExists) //throw FileError, ErrorTargetExisting -{ - //remove trailing separator (even for C:\ root directories) - const Zstring dirFormatted = endsWith(directory, FILE_NAME_SEPARATOR) ? - beforeLast(directory, FILE_NAME_SEPARATOR) : - directory; - - try - { - makeDirectoryRecursively(dirFormatted); //FileError, ErrorTargetExisting - } - catch (const ErrorTargetExisting&) - { - //avoid any file system race-condition by *not* checking directory existence again here!!! - if (failIfExists) - throw; - } - catch (const FileError&) - { - /* - could there be situations where a directory/network path exists, - but creation fails with error different than "ErrorTargetExisting"?? - - creation of C:\ fails with ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS - */ - if (somethingExists(directory)) //a file system race-condition! don't use dirExists() => harmonize with ErrorTargetExisting! - { - assert(false); - if (failIfExists) - throw; //do NOT convert to ErrorTargetExisting: if "failIfExists", not getting a ErrorTargetExisting *atomically* is unexpected! - } - else - throw; - } -} - - -void zen::makeDirectoryPlain(const Zstring& directory, //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing - const Zstring& templateDir, - bool copyFilePermissions) -{ -#ifdef ZEN_WIN - //special handling for volume root: trying to create existing root directory results in ERROR_ACCESS_DENIED rather than ERROR_ALREADY_EXISTS! - Zstring dirTmp = removeLongPathPrefix(endsWith(directory, FILE_NAME_SEPARATOR) ? - beforeLast(directory, FILE_NAME_SEPARATOR) : - directory); - if (dirTmp.size() == 2 && - std::iswalpha(dirTmp[0]) && dirTmp[1] == L':') - { - dirTmp += FILE_NAME_SEPARATOR; //we do not support "C:" to represent a relative path! - - const ErrorCode lastError = somethingExists(dirTmp) ? ERROR_ALREADY_EXISTS : ERROR_PATH_NOT_FOUND; //don't use dirExists() => harmonize with ErrorTargetExisting! - - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(dirTmp)); - const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); - - if (lastError == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); //[!] this is NOT a ErrorTargetPathMissing case! - } - - //don't use ::CreateDirectoryEx: - //- it may fail with "wrong parameter (error code 87)" when source is on mapped online storage - //- automatically copies symbolic links if encountered: unfortunately it doesn't copy symlinks over network shares but silently creates empty folders instead (on XP)! - //- it isn't able to copy most junctions because of missing permissions (although target path can be retrieved alternatively!) - if (!::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), //__in LPCTSTR lpPathName, - nullptr)) //__in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes - { - DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - - //handle issues with already existing short 8.3 file names on Windows - if (lastError == ERROR_ALREADY_EXISTS) - if (have8dot3NameClash(directory)) - { - Fix8Dot3NameClash dummy(directory); //throw FileError; move clashing object to the side - - //now try again... - if (::CreateDirectory(applyLongPathPrefixCreateDir(directory).c_str(), nullptr)) - lastError = ERROR_SUCCESS; - else - lastError = ::GetLastError(); - } - - if (lastError != ERROR_SUCCESS) - { - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)); - const std::wstring errorDescr = formatSystemError(L"CreateDirectory", lastError); - - if (lastError == ERROR_ALREADY_EXISTS) - throw ErrorTargetExisting(errorMsg, errorDescr); - else if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); - } - } - -#elif defined ZEN_LINUX || defined ZEN_MAC - if (::mkdir(directory.c_str(), 0755) != 0) //mode: drwxr-xr-x - { - const int lastError = errno; //copy before directly or indirectly making other system calls! - const std::wstring errorMsg = replaceCpy(_("Cannot create directory %x."), L"%x", fmtFileName(directory)); - const std::wstring errorDescr = formatSystemError(L"mkdir", lastError); - - if (lastError == EEXIST) - throw ErrorTargetExisting(errorMsg, errorDescr); - else if (lastError == ENOENT) - throw ErrorTargetPathMissing(errorMsg, errorDescr); - throw FileError(errorMsg, errorDescr); - } -#endif - - if (!templateDir.empty()) - { -#ifdef ZEN_WIN - //try to copy file attributes (dereference symlinks and junctions) - const HANDLE hDirSrc = ::CreateFile(zen::applyLongPathPrefix(templateDir).c_str(), //_In_ LPCTSTR lpFileName, - 0, //_In_ DWORD dwDesiredAccess, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - // FILE_FLAG_OPEN_REPARSE_POINT -> no, we follow symlinks! - FILE_FLAG_BACKUP_SEMANTICS, /*needed to open a directory*/ //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - if (hDirSrc != INVALID_HANDLE_VALUE) //dereferencing a symbolic link usually fails if it is located on network drive or client is XP: NOT really an error... - { - ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirSrc)); - - BY_HANDLE_FILE_INFORMATION dirInfo = {}; - if (::GetFileInformationByHandle(hDirSrc, &dirInfo)) - { - ::SetFileAttributes(applyLongPathPrefix(directory).c_str(), dirInfo.dwFileAttributes); - //copy "read-only and system attributes": http://blogs.msdn.com/b/oldnewthing/archive/2003/09/30/55100.aspx - - const bool isEncrypted = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) != 0; - const bool isCompressed = (dirInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; - - if (isEncrypted) - ::EncryptFile(directory.c_str()); //seems no long path is required (check passed!) - - HANDLE hDirTrg = ::CreateFile(applyLongPathPrefix(directory).c_str(), //_In_ LPCTSTR lpFileName, - GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess, - /*read access required for FSCTL_SET_COMPRESSION*/ - FILE_SHARE_READ | - FILE_SHARE_WRITE | - FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - if (hDirTrg != INVALID_HANDLE_VALUE) - { - ZEN_ON_SCOPE_EXIT(::CloseHandle(hDirTrg)); - - if (isCompressed) - { - USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; - DWORD bytesReturned = 0; - /*bool rv = */::DeviceIoControl(hDirTrg, //handle to file or directory - FSCTL_SET_COMPRESSION, //dwIoControlCode - &cmpState, //input buffer - sizeof(cmpState), //size of input buffer - nullptr, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - nullptr); //OVERLAPPED structure - } - - //(try to) set creation and modification time - /*bool rv = */::SetFileTime(hDirTrg, //_In_ HANDLE hFile, - &dirInfo.ftCreationTime, //_Out_opt_ LPFILETIME lpCreationTime, - nullptr, //_Out_opt_ LPFILETIME lpLastAccessTime, - &dirInfo.ftLastWriteTime); //_Out_opt_ LPFILETIME lpLastWriteTime - } - } - } -#endif - - zen::ScopeGuard guardNewDir = zen::makeGuard([&] { try { removeDirectory(directory); } catch (FileError&) {} }); //ensure cleanup: - - //enforce copying file permissions: it's advertized on GUI... - if (copyFilePermissions) - copyObjectPermissions(templateDir, directory, ProcSymlink::FOLLOW); //throw FileError - - guardNewDir.dismiss(); //target has been created successfully! - } -} - - -void zen::copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions) //throw FileError -{ - const Zstring linkPath = getSymlinkTargetRaw(sourceLink); //throw FileError; accept broken symlinks - -#ifdef ZEN_WIN - const bool isDirLink = [&]() -> bool - { - const DWORD ret = ::GetFileAttributes(applyLongPathPrefix(sourceLink).c_str()); - return ret != INVALID_FILE_ATTRIBUTES && (ret & FILE_ATTRIBUTE_DIRECTORY); - }(); - - //dynamically load windows API function - typedef BOOLEAN (WINAPI* CreateSymbolicLinkFunc)(LPCTSTR lpSymlinkFileName, LPCTSTR lpTargetFileName, DWORD dwFlags); - const SysDllFun createSymbolicLink(L"kernel32.dll", "CreateSymbolicLinkW"); - - if (!createSymbolicLink) - throw FileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), replaceCpy(_("Cannot find system function %x."), L"%x", L"\"CreateSymbolicLinkW\"")); - - const wchar_t functionName[] = L"CreateSymbolicLinkW"; - if (!createSymbolicLink(targetLink.c_str(), //__in LPTSTR lpSymlinkFileName, - seems no long path prefix is required... - linkPath.c_str(), //__in LPTSTR lpTargetFileName, - (isDirLink ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0))) //__in DWORD dwFlags -#elif defined ZEN_LINUX || defined ZEN_MAC - const wchar_t functionName[] = L"symlink"; - if (::symlink(linkPath.c_str(), targetLink.c_str()) != 0) -#endif - throwFileError(replaceCpy(_("Cannot create symbolic link %x."), L"%x", fmtFileName(targetLink)), functionName, getLastError()); - - //allow only consistent objects to be created -> don't place before ::symlink, targetLink may already exist - zen::ScopeGuard guardNewLink = zen::makeGuard([&] - { - try - { -#ifdef ZEN_WIN - if (isDirLink) - removeDirectory(targetLink); //throw FileError - else -#endif - removeFile(targetLink); //throw FileError - } - catch (FileError&) {} - }); - - //file times: essential for sync'ing a symlink: enforce this! (don't just try!) -#ifdef ZEN_WIN - WIN32_FILE_ATTRIBUTE_DATA sourceAttr = {}; - if (!::GetFileAttributesEx(applyLongPathPrefix(sourceLink).c_str(), //__in LPCTSTR lpFileName, - GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, - &sourceAttr)) //__out LPVOID lpFileInformation - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"GetFileAttributesEx", getLastError()); - - setFileTimeRaw(targetLink, sourceAttr.ftCreationTime, sourceAttr.ftLastWriteTime, ProcSymlink::DIRECT); //throw FileError - -#elif defined ZEN_LINUX || defined ZEN_MAC - struct ::stat srcInfo = {}; - if (::lstat(sourceLink.c_str(), &srcInfo) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceLink)), L"lstat", getLastError()); - - setFileTime(targetLink, srcInfo.st_mtime, ProcSymlink::DIRECT); //throw FileError -#endif - - if (copyFilePermissions) - copyObjectPermissions(sourceLink, targetLink, ProcSymlink::DIRECT); //throw FileError - - guardNewLink.dismiss(); //target has been created successfully! -} - - -namespace -{ -#ifdef ZEN_WIN -/* - CopyFileEx() BackupRead() FileRead() - -------------------------------------------- -Attributes YES NO NO -create time NO NO NO -ADS YES YES NO -Encrypted YES NO(silent fail!) NO -Compressed NO NO NO -Sparse NO YES NO -Nonstandard FS YES UNKNOWN -> issues writing ADS to Samba, issues reading from NAS, error copying files having "blocked" state... ect. -PERF - 6% faster - -Mark stream as compressed: FSCTL_SET_COMPRESSION - compatible with both BackupRead() and FileRead() - - -Current support for combinations of NTFS extended attributes: - -source attr | tf normal | tf compressed | tf encrypted | handled by -============|================================================================== - --- | --- -C- E-- copyFileWindowsDefault - --S | --S -CS E-S copyFileWindowsSparse - -C- | -C- -C- E-- copyFileWindowsDefault - -CS | -CS -CS E-S copyFileWindowsSparse - E-- | E-- E-- E-- copyFileWindowsDefault - E-S | E-- (NOK) E-- (NOK) E-- (NOK) copyFileWindowsDefault -> may fail with ERROR_DISK_FULL!! - -tf := target folder -E := encrypted -C := compressed -S := sparse -NOK := current behavior is not optimal/OK yet. - -Note: - if target parent folder is compressed or encrypted, both attributes are added automatically during file creation! - - "compressed" and "encrypted" are mutually exclusive: http://support.microsoft.com/kb/223093/en-us -*/ - - -//due to issues on non-NTFS volumes, we should use the copy-as-sparse routine only if required and supported! -bool canCopyAsSparse(DWORD fileAttrSource, const Zstring& targetFile) //throw () -{ - const bool sourceIsEncrypted = (fileAttrSource & FILE_ATTRIBUTE_ENCRYPTED) != 0; - const bool sourceIsSparse = (fileAttrSource & FILE_ATTRIBUTE_SPARSE_FILE) != 0; - - if (sourceIsEncrypted || !sourceIsSparse) //BackupRead() silently fails reading encrypted files! - return false; //small perf optimization: don't check "targetFile" if not needed - - //------------------------------------------------------------------------------------ - const DWORD bufferSize = 10000; - std::vector buffer(bufferSize); - - //full pathName need not yet exist! - if (!::GetVolumePathName(targetFile.c_str(), //__in LPCTSTR lpszFileName, - &buffer[0], //__out LPTSTR lpszVolumePathName, - bufferSize)) //__in DWORD cchBufferLength - return false; - - DWORD fsFlagsTarget = 0; - if (!::GetVolumeInformation(&buffer[0], //__in_opt LPCTSTR lpRootPathName - nullptr, //__out_opt LPTSTR lpVolumeNameBuffer, - 0, //__in DWORD nVolumeNameSize, - nullptr, //__out_opt LPDWORD lpVolumeSerialNumber, - nullptr, //__out_opt LPDWORD lpMaximumComponentLength, - &fsFlagsTarget, //__out_opt LPDWORD lpFileSystemFlags, - nullptr, //__out LPTSTR lpFileSystemNameBuffer, - 0)) //__in DWORD nFileSystemNameSize - return false; - - const bool targetSupportSparse = (fsFlagsTarget & FILE_SUPPORTS_SPARSE_FILES) != 0; - - return targetSupportSparse; - //both source and target must not be FAT since copyFileWindowsSparse() does no DST hack! implicitly guaranteed at this point! -} - - -bool canCopyAsSparse(const Zstring& sourceFile, const Zstring& targetFile) //throw () -{ - //follow symlinks! - HANDLE hSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), //_In_ LPCTSTR lpFileName, - 0, //_In_ DWORD dwDesiredAccess, - FILE_SHARE_READ | //all shared modes are required to read files that are open in other applications - FILE_SHARE_WRITE | - FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - 0, //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - if (hSource == INVALID_HANDLE_VALUE) - return false; - ZEN_ON_SCOPE_EXIT(::CloseHandle(hSource)); - - BY_HANDLE_FILE_INFORMATION fileInfoSource = {}; - if (!::GetFileInformationByHandle(hSource, &fileInfoSource)) - return false; - - return canCopyAsSparse(fileInfoSource.dwFileAttributes, targetFile); //throw () -} - - -//precondition: canCopyAsSparse() must return "true"! -void copyFileWindowsSparse(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked -{ - assert(canCopyAsSparse(sourceFile, targetFile)); - - //try to get backup read and write privileges: who knows, maybe this helps solve some obscure "access denied" errors - try { activatePrivilege(SE_BACKUP_NAME); } - catch (const FileError&) {} - try { activatePrivilege(SE_RESTORE_NAME); } - catch (const FileError&) {} - - //open sourceFile for reading - HANDLE hFileSource = ::CreateFile(applyLongPathPrefix(sourceFile).c_str(), //_In_ LPCTSTR lpFileName, - GENERIC_READ, //_In_ DWORD dwDesiredAccess, - FILE_SHARE_READ | FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - OPEN_EXISTING, //_In_ DWORD dwCreationDisposition, - //FILE_FLAG_OVERLAPPED must not be used! - //FILE_FLAG_NO_BUFFERING should not be used! - FILE_FLAG_SEQUENTIAL_SCAN | - FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - if (hFileSource == INVALID_HANDLE_VALUE) - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - - const std::wstring errorMsg = replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)); - std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); - - //if file is locked throw "ErrorFileLocked" instead! - if (lastError == ERROR_SHARING_VIOLATION || - lastError == ERROR_LOCK_VIOLATION) - { - const Zstring procList = getLockingProcessNames(sourceFile); //throw() - if (!procList.empty()) - errorDescr = _("The file is locked by another process:") + L"\n" + procList; - throw ErrorFileLocked(errorMsg, errorDescr); - } - - throw FileError(errorMsg, errorDescr); - } - ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileSource)); - - //---------------------------------------------------------------------- - BY_HANDLE_FILE_INFORMATION fileInfoSource = {}; - if (!::GetFileInformationByHandle(hFileSource, &fileInfoSource)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), L"GetFileInformationByHandle", getLastError()); - - //---------------------------------------------------------------------- - const DWORD validAttribs = FILE_ATTRIBUTE_NORMAL | //"This attribute is valid only if used alone." - FILE_ATTRIBUTE_READONLY | - FILE_ATTRIBUTE_HIDDEN | - FILE_ATTRIBUTE_SYSTEM | - FILE_ATTRIBUTE_ARCHIVE | //those two are not set properly (not worse than ::CopyFileEx()) - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; // - //FILE_ATTRIBUTE_ENCRYPTED -> no! - - //create targetFile and open it for writing - HANDLE hFileTarget = ::CreateFile(applyLongPathPrefix(targetFile).c_str(), //_In_ LPCTSTR lpFileName, - GENERIC_READ | GENERIC_WRITE, //_In_ DWORD dwDesiredAccess, - //read access required for FSCTL_SET_COMPRESSION - FILE_SHARE_DELETE, //_In_ DWORD dwShareMode, - //FILE_SHARE_DELETE is required to rename file while handle is open! - nullptr, //_In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, - CREATE_NEW, //_In_ DWORD dwCreationDisposition, - //FILE_FLAG_OVERLAPPED must not be used! FILE_FLAG_NO_BUFFERING should not be used! - (fileInfoSource.dwFileAttributes & validAttribs) | - FILE_FLAG_SEQUENTIAL_SCAN | - FILE_FLAG_BACKUP_SEMANTICS, //_In_ DWORD dwFlagsAndAttributes, - nullptr); //_In_opt_ HANDLE hTemplateFile - if (hFileTarget == INVALID_HANDLE_VALUE) - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - const std::wstring errorMsg = replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)); - const std::wstring errorDescr = formatSystemError(L"CreateFile", lastError); - - if (lastError == ERROR_FILE_EXISTS || //confirmed to be used - lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 - throw ErrorTargetExisting(errorMsg, errorDescr); - - if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMsg, errorDescr); - - throw FileError(errorMsg, errorDescr); - } - ScopeGuard guardTarget = makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: guard just after opening target and before managing hFileTarget - ZEN_ON_SCOPE_EXIT(::CloseHandle(hFileTarget)); - - //---------------------------------------------------------------------- - BY_HANDLE_FILE_INFORMATION fileInfoTarget = {}; - if (!::GetFileInformationByHandle(hFileTarget, &fileInfoTarget)) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), L"GetFileInformationByHandle", getLastError()); - - //return up-to-date file attributes - if (newAttrib) - { - newAttrib->fileSize = get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh); - newAttrib->modificationTime = filetimeToTimeT(fileInfoSource.ftLastWriteTime); //no DST hack (yet) - newAttrib->sourceFileId = extractFileId(fileInfoSource); - newAttrib->targetFileId = extractFileId(fileInfoTarget); - } - - //#################### copy NTFS compressed attribute ######################### - const bool sourceIsCompressed = (fileInfoSource.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; - const bool targetIsCompressed = (fileInfoTarget.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; //already set by CreateFile if target parent folder is compressed! - if (sourceIsCompressed && !targetIsCompressed) - { - USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; - DWORD bytesReturned = 0; - if (!::DeviceIoControl(hFileTarget, //handle to file or directory - FSCTL_SET_COMPRESSION, //dwIoControlCode - &cmpState, //input buffer - sizeof(cmpState), //size of input buffer - nullptr, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - nullptr)) //OVERLAPPED structure - {} //may legitimately fail with ERROR_INVALID_FUNCTION if: - // - target folder is encrypted - // - target volume does not support compressed attribute -> unlikely in this context - } - //############################################################################# - - //although it seems the sparse attribute is set automatically by BackupWrite, we are required to do this manually: http://support.microsoft.com/kb/271398/en-us - //Quote: It is the responsibility of the backup utility to apply file attributes to a file after it is restored by using BackupWrite. - //The application should retrieve the attributes by using GetFileAttributes prior to creating a backup with BackupRead. - //If a file originally had the sparse attribute (FILE_ATTRIBUTE_SPARSE_FILE), the backup utility must explicitly set the - //attribute on the restored file. - - //if (sourceIsSparse && targetSupportsSparse) -> no need to check, this is our precondition! - { - DWORD bytesReturned = 0; - if (!::DeviceIoControl(hFileTarget, //handle to file - FSCTL_SET_SPARSE, //dwIoControlCode - nullptr, //input buffer - 0, //size of input buffer - nullptr, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - nullptr)) //OVERLAPPED structure - throwFileError(replaceCpy(_("Cannot write file attributes of %x."), L"%x", fmtFileName(targetFile)), L"DeviceIoControl, FSCTL_SET_SPARSE", getLastError()); - } - - //---------------------------------------------------------------------- - const DWORD BUFFER_SIZE = 128 * 1024; //must be greater than sizeof(WIN32_STREAM_ID) - std::vector buffer(BUFFER_SIZE); - - LPVOID contextRead = nullptr; //manage context for BackupRead()/BackupWrite() - LPVOID contextWrite = nullptr; // - - ZEN_ON_SCOPE_EXIT( - if (contextRead ) ::BackupRead (0, nullptr, 0, nullptr, true, false, &contextRead); //lpContext must be passed [...] all other parameters are ignored. - if (contextWrite) ::BackupWrite(0, nullptr, 0, nullptr, true, false, &contextWrite); ); - - //stream-copy sourceFile to targetFile - bool eof = false; - bool someBytesWritten = false; //try to detect failure reading encrypted files - do - { - DWORD bytesRead = 0; - if (!::BackupRead(hFileSource, //__in HANDLE hFile, - &buffer[0], //__out LPBYTE lpBuffer, - BUFFER_SIZE, //__in DWORD nNumberOfBytesToRead, - &bytesRead, //__out LPDWORD lpNumberOfBytesRead, - false, //__in BOOL bAbort, - false, //__in BOOL bProcessSecurity, - &contextRead)) //__out LPVOID *lpContext - throwFileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"BackupRead", getLastError()); //better use fine-granular error messages "reading/writing"! - - if (bytesRead > BUFFER_SIZE) - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"buffer overflow"); //user should never see this - - if (bytesRead < BUFFER_SIZE) - eof = true; - - DWORD bytesWritten = 0; - if (!::BackupWrite(hFileTarget, //__in HANDLE hFile, - &buffer[0], //__in LPBYTE lpBuffer, - bytesRead, //__in DWORD nNumberOfBytesToWrite, - &bytesWritten, //__out LPDWORD lpNumberOfBytesWritten, - false, //__in BOOL bAbort, - false, //__in BOOL bProcessSecurity, - &contextWrite)) //__out LPVOID *lpContext - throwFileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"BackupWrite", getLastError()); - - if (bytesWritten != bytesRead) - throw FileError(replaceCpy(_("Cannot write file %x."), L"%x", fmtFileName(targetFile)), L"incomplete write"); //user should never see this - - //total bytes transferred may be larger than file size! context information + ADS or smaller (sparse, compressed)! - - //invoke callback method to update progress indicators - if (onUpdateCopyStatus) - onUpdateCopyStatus(bytesRead); //throw X! - - if (bytesRead > 0) - someBytesWritten = true; - } - while (!eof); - - //DST hack not required, since both source and target volumes cannot be FAT! - - //::BackupRead() silently fails reading encrypted files -> double check! - if (!someBytesWritten && get64BitUInt(fileInfoSource.nFileSizeLow, fileInfoSource.nFileSizeHigh) != 0U) - //note: there is no guaranteed ordering relation beween bytes transferred and file size! Consider ADS (>) and compressed/sparse files (<)! - throw FileError(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), L"unknown error"); //user should never see this -> this method is called only if "canCopyAsSparse()" - - //time needs to be set at the end: BackupWrite() changes modification time - if (!::SetFileTime(hFileTarget, - &fileInfoSource.ftCreationTime, - nullptr, - &fileInfoSource.ftLastWriteTime)) - throwFileError(replaceCpy(_("Cannot write modification time of %x."), L"%x", fmtFileName(targetFile)), L"SetFileTime", getLastError()); - - guardTarget.dismiss(); - - /* - //create sparse file for testing: - HANDLE hSparse = ::CreateFile(L"C:\\sparse.file", - GENERIC_READ | GENERIC_WRITE, //read access required for FSCTL_SET_COMPRESSION - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - nullptr, - CREATE_NEW, - FILE_FLAG_SEQUENTIAL_SCAN, - nullptr); - if (hFileTarget == INVALID_HANDLE_VALUE) - throw FileError(L"fail"); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hSparse)); - - DWORD br = 0; - if (!::DeviceIoControl(hSparse, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &br,nullptr)) - throw FileError(L"fail"); - - LARGE_INTEGER liDistanceToMove = {}; - liDistanceToMove.QuadPart = 1024 * 1024 * 1024; //create 5 TB sparse file - liDistanceToMove.QuadPart *= 5 * 1024; //maximum file size on NTFS: 16 TB - 64 kB - if (!::SetFilePointerEx(hSparse, liDistanceToMove, nullptr, FILE_BEGIN)) - throw FileError(L"fail"); - - if (!SetEndOfFile(hSparse)) - throw FileError(L"fail"); - - FILE_ZERO_DATA_INFORMATION zeroInfo = {}; - zeroInfo.BeyondFinalZero.QuadPart = liDistanceToMove.QuadPart; - if (!::DeviceIoControl(hSparse, FSCTL_SET_ZERO_DATA, &zeroInfo, sizeof(zeroInfo), nullptr, 0, &br, nullptr)) - throw FileError(L"fail"); - */ -} - - -DEFINE_NEW_FILE_ERROR(ErrorShouldCopyAsSparse); - -class ErrorHandling -{ -public: - ErrorHandling() : shouldCopyAsSparse(false) {} - - //call context: copyCallbackInternal() - void reportErrorShouldCopyAsSparse() { shouldCopyAsSparse = true; } - - void reportUserException(const std::function& onUpdateCopyStatus) { exceptionInUserCallback = onUpdateCopyStatus; } - - void reportError(const std::wstring& msg, const std::wstring& description) { errorMsg = std::make_pair(msg, description); } - - //call context: copyFileWindowsDefault() - void evaluateErrors() //throw X - { - if (shouldCopyAsSparse) - throw ErrorShouldCopyAsSparse(L"sparse dummy value"); - - if (exceptionInUserCallback) - { - exceptionInUserCallback(0); //should throw again!!! - assert(false); // - } - - if (!errorMsg.first.empty()) - throw FileError(errorMsg.first, errorMsg.second); - } - -private: - bool shouldCopyAsSparse; // - std::pair errorMsg; //these are exclusive! - std::function exceptionInUserCallback; // -> optional -}; - - -struct CallbackData -{ - CallbackData(const std::function& onUpdateCopyStatus, - const Zstring& sourceFile, - const Zstring& targetFile) : - sourceFile_(sourceFile), - targetFile_(targetFile), - onUpdateCopyStatus_(onUpdateCopyStatus), - fileInfoSrc(), - fileInfoTrg(), - bytesReported() {} - - const Zstring& sourceFile_; - const Zstring& targetFile_; - const std::function& onUpdateCopyStatus_; - - ErrorHandling errorHandler; - BY_HANDLE_FILE_INFORMATION fileInfoSrc; //modified by CopyFileEx() at beginning - BY_HANDLE_FILE_INFORMATION fileInfoTrg; // - - std::int64_t bytesReported; //used internally to calculate bytes transferred delta -}; - - -DWORD CALLBACK copyCallbackInternal(LARGE_INTEGER totalFileSize, - LARGE_INTEGER totalBytesTransferred, - LARGE_INTEGER streamSize, - LARGE_INTEGER streamBytesTransferred, - DWORD dwStreamNumber, - DWORD dwCallbackReason, - HANDLE hSourceFile, - HANDLE hDestinationFile, - LPVOID lpData) -{ - /* - this callback is invoked for block sizes managed by Windows, these may vary from e.g. 64 kB up to 1MB. It seems this depends on file size amongst others. - - symlink handling: - if source is a symlink and COPY_FILE_COPY_SYMLINK is specified, this callback is NOT invoked! - if source is a symlink and COPY_FILE_COPY_SYMLINK is NOT specified, this callback is called and hSourceFile is a handle to the *target* of the link! - - file time handling: - ::CopyFileEx() will (only) copy file modification time over from source file AFTER the last invokation of this callback - => it is possible to adapt file creation time of target in here, but NOT file modification time! - CAVEAT: if ::CopyFileEx() fails to set modification time, it silently ignores this error and returns success!!! - see procmon log in: https://sourceforge.net/tracker/?func=detail&atid=1093080&aid=3514569&group_id=234430 - - alternate data stream handling: - CopyFileEx() processes multiple streams one after another, stream 1 is the file data stream and always available! - Each stream is initialized with CALLBACK_STREAM_SWITCH and provides *new* hSourceFile, hDestinationFile. - Calling GetFileInformationByHandle() on hDestinationFile for stream > 1 results in ERROR_ACCESS_DENIED! - totalBytesTransferred contains size of *all* streams and so can be larger than the "file size" file attribute - */ - - CallbackData& cbd = *static_cast(lpData); - - if (dwCallbackReason == CALLBACK_STREAM_SWITCH && //called up-front for every file (even if 0-sized) - dwStreamNumber == 1) //consider ADS! - { - //#################### return source file attributes ################################ - if (!::GetFileInformationByHandle(hSourceFile, &cbd.fileInfoSrc)) - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.sourceFile_)), formatSystemError(L"GetFileInformationByHandle", lastError)); - return PROGRESS_CANCEL; - } - - if (!::GetFileInformationByHandle(hDestinationFile, &cbd.fileInfoTrg)) - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - cbd.errorHandler.reportError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(cbd.targetFile_)), formatSystemError(L"GetFileInformationByHandle", lastError)); - return PROGRESS_CANCEL; - } - - //#################### switch to sparse file copy if req. ####################### - if (canCopyAsSparse(cbd.fileInfoSrc.dwFileAttributes, cbd.targetFile_)) //throw () - { - cbd.errorHandler.reportErrorShouldCopyAsSparse(); //use a different copy routine! - return PROGRESS_CANCEL; - } - - //#################### copy file creation time ################################ - ::SetFileTime(hDestinationFile, &cbd.fileInfoSrc.ftCreationTime, nullptr, nullptr); //no error handling! - //=> not really needed here, creation time is set anyway at the end of copyFileWindowsDefault()! - - //#################### copy NTFS compressed attribute ######################### - const bool sourceIsCompressed = (cbd.fileInfoSrc.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; - const bool targetIsCompressed = (cbd.fileInfoTrg.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0; //already set by CopyFileEx if target parent folder is compressed! - if (sourceIsCompressed && !targetIsCompressed) - { - USHORT cmpState = COMPRESSION_FORMAT_DEFAULT; - DWORD bytesReturned = 0; - if (!::DeviceIoControl(hDestinationFile, //handle to file or directory - FSCTL_SET_COMPRESSION, //dwIoControlCode - &cmpState, //input buffer - sizeof(cmpState), //size of input buffer - nullptr, //lpOutBuffer - 0, //OutBufferSize - &bytesReturned, //number of bytes returned - nullptr)) //OVERLAPPED structure - {} //may legitimately fail with ERROR_INVALID_FUNCTION if - // - if target folder is encrypted - // - target volume does not support compressed attribute - //############################################################################# - } - } - - //called after copy operation is finished - note: for 0-sized files this callback is invoked just ONCE! - //if (totalFileSize.QuadPart == totalBytesTransferred.QuadPart && dwStreamNumber == 1) {} - if (cbd.onUpdateCopyStatus_ && totalBytesTransferred.QuadPart >= 0) //should be always true, but let's still check - try - { - cbd.onUpdateCopyStatus_(totalBytesTransferred.QuadPart - cbd.bytesReported); //throw X! - cbd.bytesReported = totalBytesTransferred.QuadPart; - } - catch (...) - { - //#warning migrate to std::exception_ptr when available - - cbd.errorHandler.reportUserException(cbd.onUpdateCopyStatus_); - return PROGRESS_CANCEL; - } - return PROGRESS_CONTINUE; -} - - -const bool supportNonEncryptedDestination = winXpOrLater(); //encrypted destination is not supported with Windows 2000 -//caveat: function scope static initialization is not thread-safe in VS 2010! - - -void copyFileWindowsDefault(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked, ErrorShouldCopyAsSparse -{ - //try to get backup read and write privileges: who knows, maybe this helps solve some obscure "access denied" errors - try { activatePrivilege(SE_BACKUP_NAME); } - catch (const FileError&) {} - try { activatePrivilege(SE_RESTORE_NAME); } - catch (const FileError&) {} - - zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); - //transactional behavior: guard just before starting copy, we don't trust ::CopyFileEx(), do we? ;) - - DWORD copyFlags = COPY_FILE_FAIL_IF_EXISTS; - - if (supportNonEncryptedDestination) - copyFlags |= COPY_FILE_ALLOW_DECRYPTED_DESTINATION; //allow copying from encrypted to non-encrytped location - - //if (vistaOrLater()) //see http://blogs.technet.com/b/askperf/archive/2007/05/08/slow-large-file-copy-issues.aspx - // copyFlags |= COPY_FILE_NO_BUFFERING; //no perf difference at worst, huge improvement for large files (20% in test NTFS -> NTFS) - //It's a shame this flag causes file corruption! https://sourceforge.net/projects/freefilesync/forums/forum/847542/topic/5177950 - //documentation on CopyFile2() even states: "It is not recommended to pause copies that are using this flag." How dangerous is this thing, why offer it at all??? - //perf advantage: ~15% faster - - CallbackData cbd(onUpdateCopyStatus, sourceFile, targetFile); - - const bool success = ::CopyFileEx( //same performance like CopyFile() - applyLongPathPrefix(sourceFile).c_str(), //__in LPCTSTR lpExistingFileName, - applyLongPathPrefix(targetFile).c_str(), //__in LPCTSTR lpNewFileName, - copyCallbackInternal, //__in_opt LPPROGRESS_ROUTINE lpProgressRoutine, - &cbd, //__in_opt LPVOID lpData, - nullptr, //__in_opt LPBOOL pbCancel, - copyFlags) != FALSE; //__in DWORD dwCopyFlags - - cbd.errorHandler.evaluateErrors(); //throw ?, process errors in callback first! - if (!success) - { - const DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - - //don't suppress "lastError == ERROR_REQUEST_ABORTED": a user aborted operation IS an error condition! - - //trying to copy huge sparse files may directly fail with ERROR_DISK_FULL before entering the callback function - if (canCopyAsSparse(sourceFile, targetFile)) //throw () - throw ErrorShouldCopyAsSparse(L"sparse dummy value2"); - - //assemble error message... - const std::wstring errorMsg = replaceCpy(replaceCpy(_("Cannot copy file %x to %y."), L"%x", L"\n" + fmtFileName(sourceFile)), L"%y", L"\n" + fmtFileName(targetFile)); - std::wstring errorDescr = formatSystemError(L"CopyFileEx", lastError); - - //if file is locked throw "ErrorFileLocked" instead! - if (lastError == ERROR_SHARING_VIOLATION || - lastError == ERROR_LOCK_VIOLATION) - { - const Zstring procList = getLockingProcessNames(sourceFile); //throw() -> enhance error message! - if (!procList.empty()) - errorDescr = _("The file is locked by another process:") + L"\n" + procList; - throw ErrorFileLocked(replaceCpy(_("Cannot read file %x."), L"%x", fmtFileName(sourceFile)), errorDescr); - } - - //if target is existing this functions is expected to throw ErrorTargetExisting!!! - if (lastError == ERROR_FILE_EXISTS || //confirmed to be used - lastError == ERROR_ALREADY_EXISTS) //not sure if used -> better be safe than sorry!!! - { - guardTarget.dismiss(); //don't delete file that existed previously! - throw ErrorTargetExisting(errorMsg, errorDescr); - } - - if (lastError == ERROR_PATH_NOT_FOUND) - { - guardTarget.dismiss(); //not relevant - throw ErrorTargetPathMissing(errorMsg, errorDescr); //could this also be source path missing!? - } - - try //add more meaningful message - { - //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 * std::uint64_t(1024U * 1024 * 1024)) //throw FileError - errorDescr += L"\nFAT volumes cannot store files larger than 4 gigabyte."; - //see "Limitations of the FAT32 File System": http://support.microsoft.com/kb/314463/en-us - - //note: ERROR_INVALID_PARAMETER can also occur when copying to a SharePoint server or MS SkyDrive and the target filepath is of a restricted type. - } - catch (FileError&) {} - - throw FileError(errorMsg, errorDescr); - } - - if (newAttrib) - { - newAttrib->fileSize = get64BitUInt(cbd.fileInfoSrc.nFileSizeLow, cbd.fileInfoSrc.nFileSizeHigh); - //newAttrib->modificationTime = -> set further below - newAttrib->sourceFileId = extractFileId(cbd.fileInfoSrc); - newAttrib->targetFileId = extractFileId(cbd.fileInfoTrg); - } - - { - FILETIME creationtime = cbd.fileInfoSrc.ftCreationTime; - FILETIME lastWriteTimeRaw = cbd.fileInfoSrc.ftLastWriteTime; - //####################################### DST hack ########################################### - warn_static("let's tentatively disable the DST hack:") -#if 0 - if (dst::isFatDrive(sourceFile)) //throw(); hacky: does not consider symlinks pointing to FAT! - { - const dst::RawTime rawTime(creationtime, lastWriteTimeRaw); - if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error - { - lastWriteTimeRaw = dst::fatDecodeUtcTime(rawTime); //return last write time in real UTC, throw (std::runtime_error) - ::GetSystemTimeAsFileTime(&creationtime); //real creation time information is not available... - } - } -#endif - //####################################### DST hack ########################################### - - if (newAttrib) - newAttrib->modificationTime = filetimeToTimeT(lastWriteTimeRaw); - - //caveat: - ::CopyFileEx() silently *ignores* failure to set modification time!!! => we always need to set it again but with proper error checking! - // - perf-loss on USB sticks with many small files of about 30%! - FILETIME creationTimeOut = creationtime; - FILETIME lastWriteTimeOut = lastWriteTimeRaw; - - //####################################### DST hack ########################################### - warn_static("let's tentatively disable the DST hack:") -#if 0 - if (dst::isFatDrive(targetFile)) //throw(); target cannot be a symlink in this context! - { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(lastWriteTimeRaw); //throw std::runtime_error - creationTimeOut = encodedTime.createTimeRaw; - lastWriteTimeOut = encodedTime.writeTimeRaw; - } -#endif - //####################################### DST hack ########################################### - - setFileTimeRaw(targetFile, creationTimeOut, lastWriteTimeOut, ProcSymlink::FOLLOW); //throw FileError - } - - guardTarget.dismiss(); //target has been created successfully! -} - - -//another layer to support copying sparse files -inline -void copyFileWindowsSelectRoutine(const Zstring& sourceFile, const Zstring& targetFile, const std::function& onUpdateCopyStatus, InSyncAttributes* sourceAttr) -{ - try - { - copyFileWindowsDefault(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw ErrorShouldCopyAsSparse et al. - } - catch (ErrorShouldCopyAsSparse&) //we cheaply check for this condition within callback of ::CopyFileEx()! - { - copyFileWindowsSparse(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); - } -} - - -//another layer of indirection solving 8.3 name clashes -inline -void copyFileWindows(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function& onUpdateCopyStatus, - InSyncAttributes* sourceAttr) -{ - try - { - copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked - } - catch (const ErrorTargetExisting&) - { - //try to handle issues with already existing short 8.3 file names on Windows - if (have8dot3NameClash(targetFile)) - { - Fix8Dot3NameClash dummy(targetFile); //throw FileError; move clashing filepath to the side - copyFileWindowsSelectRoutine(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError; the short filepath name clash is solved, this should work now - return; - } - throw; - } -} - - -#elif defined ZEN_LINUX || defined ZEN_MAC -void copyFileLinuxMac(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function& onUpdateCopyStatus, - InSyncAttributes* newAttrib) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting -{ - //open sourceFile for reading - FileInputUnbuffered fileIn(sourceFile); //throw FileError - - struct ::stat sourceInfo = {}; - if (::fstat(fileIn.getDescriptor(), &sourceInfo) != 0) //read file attributes from source - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(sourceFile)), L"fstat", getLastError()); - - zen::ScopeGuard guardTarget = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {} }); //transactional behavior: place guard before lifetime of FileOutput - try - { - //create targetFile and open it for writing - FileOutputUnbuffered fileOut(targetFile, sourceInfo.st_mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting - - //copy contents of sourceFile to targetFile - std::vector buffer(128 * 1024); //see comment in FileInputUnbuffered::read - do - { - const size_t bytesRead = fileIn.read(&buffer[0], buffer.size()); //throw FileError - - fileOut.write(&buffer[0], bytesRead); //throw FileError - - //invoke callback method to update progress indicators - if (onUpdateCopyStatus) - onUpdateCopyStatus(bytesRead); //throw X! - } - while (!fileIn.eof()); - - //adapt target file modification time: - { - //read and return file statistics - struct ::stat targetInfo = {}; - if (::fstat(fileOut.getDescriptor(), &targetInfo) != 0) - throwFileError(replaceCpy(_("Cannot read file attributes of %x."), L"%x", fmtFileName(targetFile)), L"fstat", getLastError()); - - if (newAttrib) - { - newAttrib->fileSize = sourceInfo.st_size; - newAttrib->modificationTime = sourceInfo.st_mtime; - newAttrib->sourceFileId = extractFileId(sourceInfo); - newAttrib->targetFileId = extractFileId(targetInfo); - } - } - } - catch (const ErrorTargetExisting&) - { - guardTarget.dismiss(); //don't delete file that existed previously! - throw; - } - - //we cannot set the target file times while the file descriptor is open and being written: - //this triggers bugs on samba shares where the modification time is set to current time instead. - //http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=340236 - //http://comments.gmane.org/gmane.linux.file-systems.cifs/2854 - //on the other hand we thereby have to reopen https://sourceforge.net/p/freefilesync/bugs/230/ - setFileTime(targetFile, sourceInfo.st_mtime, ProcSymlink::FOLLOW); //throw FileError - - guardTarget.dismiss(); //target has been created successfully! -} -#endif - -/* - ------------------ - |File Copy Layers| - ------------------ - copyFile (setup transactional behavior) - | - copyFileSelectOs - / \ -copyFileLinuxMac copyFileWindows (solve 8.3 issue) - | - copyFileWindowsSelectRoutine - / \ -copyFileWindowsDefault(::CopyFileEx) copyFileWindowsSparse(::BackupRead/::BackupWrite) -*/ - -inline -void copyFileSelectOs(const Zstring& sourceFile, - const Zstring& targetFile, - const std::function& onUpdateCopyStatus, - InSyncAttributes* sourceAttr) -{ -#ifdef ZEN_WIN - copyFileWindows(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked - -#elif defined ZEN_LINUX || defined ZEN_MAC - copyFileLinuxMac(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting -#endif -} -} - - -void zen::copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissing, ErrorFileLocked - const Zstring& targetFile, - bool copyFilePermissions, - bool transactionalCopy, - const std::function& onDeleteTargetFile, - const std::function& onUpdateCopyStatus, - InSyncAttributes* sourceAttr) -{ - if (transactionalCopy) - { - Zstring tmpTarget = targetFile + TEMP_FILE_ENDING; //use temporary file until a correct date has been set - - //raw file copy - for (int i = 0;; ++i) - try - { - copyFileSelectOs(sourceFile, tmpTarget, onUpdateCopyStatus, sourceAttr); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked - break; - } - catch (const ErrorTargetExisting&) //optimistic strategy: assume everything goes well, but recover on error -> minimize file accesses - { - if (i == 10) throw; //avoid endless recursion in pathological cases, e.g. https://sourceforge.net/p/freefilesync/discussion/open-discussion/thread/36adac33 - tmpTarget = targetFile + Zchar('_') + numberTo(i) + TEMP_FILE_ENDING; - } - - //transactional behavior: ensure cleanup; not needed before copyFileSelectOs() which is already transactional - zen::ScopeGuard guardTempFile = zen::makeGuard([&] { try { removeFile(tmpTarget); } catch (FileError&) {} }); - - //have target file deleted (after read access on source and target has been confirmed) => allow for almost transactional overwrite - if (onDeleteTargetFile) - onDeleteTargetFile(); //throw X - - //rename temporary file: - //perf: this call is REALLY expensive on unbuffered volumes! ~40% performance decrease on FAT USB stick! - renameFile(tmpTarget, targetFile); //throw FileError - - /* - CAVEAT on FAT/FAT32: the sequence of deleting the target file and renaming "file.txt.ffs_tmp" to "file.txt" does - NOT PRESERVE the creation time of the .ffs_tmp file, but SILENTLY "reuses" whatever creation time the old "file.txt" had! - This "feature" is called "File System Tunneling": - http://blogs.msdn.com/b/oldnewthing/archive/2005/07/15/439261.aspx - http://support.microsoft.com/kb/172190/en-us - - However during the next comparison the DST hack will be applied correctly since the DST-hash of the mod.time is invalid. - - EXCEPTION: the hash may match!!! reproduce: - 1. set system time back to date within previous DST - 2. save some file on FAT32 usb stick and FFS-compare to make sure the DST hack is applied correctly - 4. pull out usb stick, put back in - 3. restore system time - 4. copy file from USB to local drive via explorer - => - NTFS <-> FAT, file exists on both sides; mod times match, DST hack on USB stick causes 1-hour offset when comparing in FFS. - When syncing, modification time is copied correctly, but new DST hack fails to apply and old creation time is reused (see above). - Unfortunately, the old DST hash matches mod time! => On next comparison FFS will *still* see both sides as different!!!!!!!!! - */ - - guardTempFile.dismiss(); - } - else - { - if (onDeleteTargetFile) - onDeleteTargetFile(); - - copyFileSelectOs(sourceFile, targetFile, onUpdateCopyStatus, sourceAttr); //throw FileError: ErrorTargetPathMissing, ErrorTargetExisting, ErrorFileLocked - } - /* - Note: non-transactional file copy solves at least four problems: - -> skydrive - doesn't allow for .ffs_tmp extension and returns ERROR_INVALID_PARAMETER - -> network renaming issues - -> allow for true delete before copy to handle low disk space problems - -> higher performance on non-buffered drives (e.g. usb sticks) - */ - - //file permissions - if (copyFilePermissions) - { - zen::ScopeGuard guardTargetFile = zen::makeGuard([&] { try { removeFile(targetFile); } catch (FileError&) {}}); - - copyObjectPermissions(sourceFile, targetFile, ProcSymlink::FOLLOW); //throw FileError - - guardTargetFile.dismiss(); //target has been created successfully! - } -} diff --git a/zen/file_handling.h b/zen/file_handling.h deleted file mode 100644 index b58f6c07..00000000 --- a/zen/file_handling.h +++ /dev/null @@ -1,82 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef FILE_HANDLING_H_8017341345614857 -#define FILE_HANDLING_H_8017341345614857 - -#include -#include "zstring.h" -#include "file_error.h" -#include "file_id_def.h" - -namespace zen -{ -bool fileExists (const Zstring& filepath); //noexcept; check whether file or file-symlink exists -bool dirExists (const Zstring& dirpath ); //noexcept; check whether directory or dir-symlink exists -bool symlinkExists (const Zstring& linkname); //noexcept; check whether a symbolic link exists -bool somethingExists(const Zstring& objname ); //noexcept; check whether any object with this name exists - -enum class ProcSymlink -{ - DIRECT, - FOLLOW -}; - -void setFileTime(const Zstring& filepath, std::int64_t modificationTime, ProcSymlink procSl); //throw FileError - -//symlink handling: always evaluate target -std::uint64_t getFilesize(const Zstring& filepath); //throw FileError -std::uint64_t getFreeDiskSpace(const Zstring& path); //throw FileError - -bool removeFile(const Zstring& filepath); //throw FileError; return "false" if file is not existing -void removeDirectory(const Zstring& directory, //throw FileError - const std::function& onBeforeFileDeletion = nullptr, //optional; - const std::function& onBeforeDirDeletion = nullptr); //one call for each *existing* object! - -//rename file or directory: no copying!!! -void renameFile(const Zstring& oldName, const Zstring& newName); //throw FileError, ErrorDifferentVolume, ErrorTargetExisting - -bool supportsPermissions(const Zstring& dirpath); //throw FileError, dereferences symlinks - -//if parent directory not existing: create recursively: -void makeDirectory(const Zstring& directory, bool failIfExists = false); //throw FileError, ErrorTargetExisting - -//fail if already existing or parent directory not existing: -//directory should not end with path separator -//templateDir may be empty -void makeDirectoryPlain(const Zstring& directory, const Zstring& templateDir, bool copyFilePermissions); //throw FileError, ErrorTargetExisting, ErrorTargetPathMissing - -struct InSyncAttributes -{ - std::uint64_t fileSize; - std::int64_t modificationTime; //time_t UTC compatible - FileId sourceFileId; - FileId targetFileId; -}; - -void copyFile(const Zstring& sourceFile, //throw FileError, ErrorTargetPathMissing, ErrorFileLocked (Windows-only) - const Zstring& targetFile, //symlink handling: dereference source - bool copyFilePermissions, - bool transactionalCopy, - //if target is existing user needs to implement deletion: copyFile() NEVER overwrites target if already existing! - //if transactionalCopy == true, full read access on source had been proven at this point, so it's safe to delete it. - const std::function& onDeleteTargetFile, //may be nullptr; may throw! - //Linux: unconditionally - //Windows: first exception is swallowed, updateCopyStatus() is then called again where it should throw again and the exception will propagate as expected - //accummulated delta != file size! consider ADS, sparse, compressed files - const std::function& onUpdateCopyStatus, //may be nullptr; may throw! - - InSyncAttributes* newAttrib = nullptr); //return current attributes at the time of copy - -//Note: it MAY happen that copyFile() leaves temp files behind, e.g. temporary network drop. -// => clean them up at an appropriate time (automatically set sync directions to delete them). They have the following ending: - -const Zchar TEMP_FILE_ENDING[] = Zstr(".ffs_tmp"); //don't use Zstring as global constant: avoid static initialization order problem in global namespace! - -void copySymlink(const Zstring& sourceLink, const Zstring& targetLink, bool copyFilePermissions); //throw FileError -} - -#endif //FILE_HANDLING_H_8017341345614857 diff --git a/zen/file_id_def.h b/zen/file_id_def.h index daadd391..3b217b3d 100644 --- a/zen/file_id_def.h +++ b/zen/file_id_def.h @@ -8,7 +8,6 @@ #define FILE_ID_INTERNAL_HEADER_013287632486321493 #include -#include "assert_static.h" #ifdef ZEN_WIN #include "win.h" //includes "windows.h" @@ -44,9 +43,9 @@ FileId extractFileId(DWORD volumeSerialNumber, ULONGLONG fileIndex) FileId(volumeSerialNumber, fileIndex) : FileId(); } -assert_static(sizeof(FileId().first ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber)); -assert_static(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow)); -assert_static(sizeof(FileId().second) == sizeof(ULARGE_INTEGER)); +static_assert(sizeof(FileId().first ) == sizeof(BY_HANDLE_FILE_INFORMATION().dwVolumeSerialNumber), ""); +static_assert(sizeof(FileId().second) == sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexHigh) + sizeof(BY_HANDLE_FILE_INFORMATION().nFileIndexLow), ""); +static_assert(sizeof(FileId().second) == sizeof(ULARGE_INTEGER), ""); #elif defined ZEN_LINUX || defined ZEN_MAC diff --git a/zen/file_io.cpp b/zen/file_io.cpp index e11a1201..8f4b6cb2 100644 --- a/zen/file_io.cpp +++ b/zen/file_io.cpp @@ -187,7 +187,7 @@ size_t FileInput::read(void* buffer, size_t bytesToRead) //returns actual number FileOutput::FileOutput(FileHandle handle, const Zstring& filepath) : FileOutputBase(filepath), fileHandle(handle) {} -FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting +FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw FileError, ErrorTargetExisting FileOutputBase(filepath) { #ifdef ZEN_WIN @@ -246,8 +246,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil lastError == ERROR_ALREADY_EXISTS) //comment on msdn claims, this one is used on Windows Mobile 6 throw ErrorTargetExisting(errorMsg, errorDescr); - if (lastError == ERROR_PATH_NOT_FOUND) - throw ErrorTargetPathMissing(errorMsg, errorDescr); + //if (lastError == ERROR_PATH_NOT_FOUND) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } @@ -267,8 +266,7 @@ FileOutput::FileOutput(const Zstring& filepath, AccessFlag access) : //throw Fil if (lastError == EEXIST) throw ErrorTargetExisting(errorMsg, errorDescr); - if (lastError == ENOENT) - throw ErrorTargetPathMissing(errorMsg, errorDescr); + //if (lastError == ENOENT) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } @@ -348,7 +346,7 @@ size_t FileInputUnbuffered::read(void* buffer, size_t bytesToRead) //throw FileE } -FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode) : FileOutputBase(filepath) //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting +FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode) : FileOutputBase(filepath) //throw FileError, ErrorTargetExisting { //checkForUnsupportedType(filepath); -> not needed, open() + O_EXCL shoul fail fast @@ -363,8 +361,7 @@ FileOutputUnbuffered::FileOutputUnbuffered(const Zstring& filepath, mode_t mode) if (lastError == EEXIST) throw ErrorTargetExisting(errorMsg, errorDescr); - if (lastError == ENOENT) - throw ErrorTargetPathMissing(errorMsg, errorDescr); + //if (lastError == ENOENT) throw ErrorTargetPathMissing(errorMsg, errorDescr); throw FileError(errorMsg, errorDescr); } diff --git a/zen/file_io.h b/zen/file_io.h index 5ae6eb1c..7ccef05b 100644 --- a/zen/file_io.h +++ b/zen/file_io.h @@ -26,7 +26,7 @@ static const char LINE_BREAK[] = "\r\n"; static const char LINE_BREAK[] = "\n"; //since OS X apple uses newline, too #endif -//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support (following symlinks) +//buffered file IO optimized for sequential read/write accesses + better error reporting + long path support + following symlinks #ifdef ZEN_WIN typedef HANDLE FileHandle; @@ -41,8 +41,7 @@ public: FileInput(FileHandle handle, const Zstring& filepath); //takes ownership! ~FileInput(); - virtual size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read - //expected to fill buffer completely unless "end of file" + size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read private: FileHandle fileHandle; @@ -52,17 +51,19 @@ private: class FileOutput : public FileOutputBase { public: - FileOutput(const Zstring& filepath, AccessFlag access); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + FileOutput(const Zstring& filepath, AccessFlag access); //throw FileError, ErrorTargetExisting FileOutput(FileHandle handle, const Zstring& filepath); //takes ownership! ~FileOutput(); - virtual void write(const void* buffer, size_t bytesToWrite) override; //throw FileError + void write(const void* buffer, size_t bytesToWrite) override; //throw FileError private: FileHandle fileHandle; }; #if defined ZEN_LINUX || defined ZEN_MAC +warn_static("get rid of FileInputUnbuffered/FileOutputUnbuffered, use fdopen instead") + class FileInputUnbuffered : public FileInputBase { public: @@ -70,7 +71,7 @@ public: ~FileInputUnbuffered(); //considering safe-read.c it seems buffer size should be a multiple of 8192 - virtual size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read + size_t read(void* buffer, size_t bytesToRead) override; //throw FileError; returns actual number of bytes read //do NOT rely on partially filled buffer meaning EOF! int getDescriptor() { return fdFile;} @@ -83,11 +84,11 @@ class FileOutputUnbuffered : public FileOutputBase { public: //creates a new file (no overwrite allowed!) - FileOutputUnbuffered(const Zstring& filepath, mode_t mode); //throw FileError, ErrorTargetPathMissing, ErrorTargetExisting + FileOutputUnbuffered(const Zstring& filepath, mode_t mode); //throw FileError, ErrorTargetExisting FileOutputUnbuffered(int fd, const Zstring& filepath); //takes ownership! ~FileOutputUnbuffered(); - virtual void write(const void* buffer, size_t bytesToWrite) override; //throw FileError + void write(const void* buffer, size_t bytesToWrite) override; //throw FileError int getDescriptor() { return fdFile;} private: diff --git a/zen/file_io_base.h b/zen/file_io_base.h index 8e9bf12e..e56ea189 100644 --- a/zen/file_io_base.h +++ b/zen/file_io_base.h @@ -21,8 +21,8 @@ protected: ~FileBase() {} private: - FileBase(const FileBase&); //=delete - FileBase& operator=(const FileBase&); // + FileBase (const FileBase&) = delete; + FileBase& operator=(const FileBase&) = delete; const Zstring filename_; }; diff --git a/zen/file_traverser.cpp b/zen/file_traverser.cpp index d8f2d0cf..7b423faa 100644 --- a/zen/file_traverser.cpp +++ b/zen/file_traverser.cpp @@ -6,15 +6,13 @@ #include "file_traverser.h" #include "sys_error.h" -#include "assert_static.h" #include "symlink_target.h" #include "int64.h" #ifdef ZEN_WIN #include "win_ver.h" #include "long_path_prefix.h" -#include "dst_hack.h" -#include "file_handling.h" //remove this huge dependency when getting rid of DST hack!! until then we need "setFileTime" +#include "file_access.h" #include "dll.h" #include "FindFilePlus/find_file_plus.h" @@ -30,7 +28,7 @@ using namespace zen; - + namespace { //implement "retry" in a generic way: @@ -179,8 +177,8 @@ struct Win32Traverser { struct DirHandle { - DirHandle(HANDLE hnd, const WIN32_FIND_DATA& d) : searchHandle(hnd), haveData(true), data(d) {} - explicit DirHandle(HANDLE hnd) : searchHandle(hnd), haveData(false) {} + DirHandle(HANDLE hnd, const WIN32_FIND_DATA& d) : searchHandle(hnd), haveData(true), data(d) {} + explicit DirHandle(HANDLE hnd) : searchHandle(hnd), haveData(false) {} HANDLE searchHandle; bool haveData; @@ -193,7 +191,7 @@ struct Win32Traverser { const Zstring& dirpathPf = appendSeparator(dirpath); - WIN32_FIND_DATA fileData = {}; + WIN32_FIND_DATA fileData = {}; HANDLE hnd = ::FindFirstFile(applyLongPathPrefix(dirpathPf + L'*').c_str(), &fileData); //no noticable performance difference compared to FindFirstFileEx with FindExInfoBasic, FIND_FIRST_EX_CASE_SENSITIVE and/or FIND_FIRST_EX_LARGE_FETCH if (hnd == INVALID_HANDLE_VALUE) @@ -208,7 +206,7 @@ struct Win32Traverser } throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"FindFirstFile", lastError); } - return DirHandle(hnd, fileData); + return DirHandle(hnd, fileData); } static void destroy(const DirHandle& hnd) { ::FindClose(hnd.searchHandle); } //throw() @@ -271,11 +269,11 @@ struct FilePlusTraverser static DirHandle create(const Zstring& dirpath) //throw FileError { - const findplus::FindHandle hnd = ::openDir(applyLongPathPrefix(dirpath).c_str()); + const findplus::FindHandle hnd = ::openDir(applyLongPathPrefix(dirpath).c_str()); if (!hnd) - throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"openDir", getLastError()); + throwFileError(replaceCpy(_("Cannot open directory %x."), L"%x", fmtFileName(dirpath)), L"openDir", getLastError()); - return DirHandle(hnd); + return DirHandle(hnd); } static void destroy(DirHandle hnd) { ::closeDir(hnd.searchHandle); } //throw() @@ -325,13 +323,13 @@ struct FilePlusTraverser class DirTraverser { public: - static void execute(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) + static void execute(const Zstring& baseDirectory, TraverseCallback& sink) { - DirTraverser(baseDirectory, sink, dstCallback); + DirTraverser(baseDirectory, sink); } private: - DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback); + DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink); DirTraverser (const DirTraverser&) = delete; DirTraverser& operator=(const DirTraverser&) = delete; @@ -340,58 +338,6 @@ private: template void traverseWithException(const Zstring& dirpath, TraverseCallback& sink, DWORD volumeSerial /*may be 0!*/); //throw FileError, NeedFallbackToWin32Traverser - - //####################################### DST hack ########################################### - void applyDstHack(zen::DstHackCallback& dstCallback) - { - int failedAttempts = 0; - int filesToValidate = 50; //don't let data verification become a performance issue - - for (auto it = markForDstHack.begin(); it != markForDstHack.end(); ++it) - { - if (failedAttempts >= 10) //some cloud storages don't support changing creation/modification times => don't waste (a lot of) time trying to - return; - - dstCallback.requestUiRefresh(it->first); - - try - { - //set modification time including DST hack: this function is too clever to not introduce this dependency - setFileTime(it->first, it->second, ProcSymlink::FOLLOW); //throw FileError - } - catch (FileError&) - { - ++failedAttempts; - assert(false); //don't throw exceptions due to dst hack here - continue; - } - - //even at this point it's not sure whether data was written correctly, again cloud storages tend to lie about success status - if (filesToValidate-- > 0) - { - const dst::RawTime encodedTime = dst::fatEncodeUtcTime(timetToFileTime(it->second)); //throw std::runtime_error - - //dst hack: verify data written; attention: this check may fail for "sync.ffs_lock" - WIN32_FILE_ATTRIBUTE_DATA debugeAttr = {}; - ::GetFileAttributesEx(zen::applyLongPathPrefix(it->first).c_str(), //__in LPCTSTR lpFileName, - GetFileExInfoStandard, //__in GET_FILEEX_INFO_LEVELS fInfoLevelId, - &debugeAttr); //__out LPVOID lpFileInformation - - if (::CompareFileTime(&debugeAttr.ftCreationTime, &encodedTime.createTimeRaw) != 0 || - ::CompareFileTime(&debugeAttr.ftLastWriteTime, &encodedTime.writeTimeRaw) != 0) - { - ++failedAttempts; - assert(false); //don't throw exceptions due to dst hack here - continue; - } - } - } - } - - const bool needDstHack; - typedef std::vector> FilenameTimeList; - FilenameTimeList markForDstHack; - //####################################### DST hack ########################################### }; @@ -420,8 +366,7 @@ void DirTraverser::traverse(const Zstring& dirpath, TraverseC inline -DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) : - needDstHack(dstCallback ? dst::isFatDrive(baseDirectory) : false) +DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink) { try //traversing certain folders with restricted permissions requires this privilege! (but copying these files may still fail) { @@ -433,11 +378,6 @@ DirTraverser::DirTraverser(const Zstring& baseDirectory, TraverseCallback& sink, traverse(baseDirectory, sink, retrieveVolumeSerial(baseDirectory)); //retrieveVolumeSerial returns 0 on error else //fallback traverse(baseDirectory, sink, 0); - - //apply daylight saving time hack AFTER file traversing, to give separate feedback to user - if (needDstHack) - if (dstCallback) //bound if "needDstHack == true" - applyDstHack(*dstCallback); } @@ -507,20 +447,7 @@ void DirTraverser::traverseWithException(const Zstring& dirpath, TraverseCallbac } else //a file { - TraverseCallback::FileInfo fileInfo = Trav::extractFileInfo(findData, volumeSerial); - - //####################################### DST hack ########################################### - if (needDstHack) - { - const dst::RawTime rawTime(Trav::getCreateTimeRaw(findData), Trav::getModTimeRaw(findData)); - - if (dst::fatHasUtcEncoded(rawTime)) //throw std::runtime_error - fileInfo.lastWriteTime = filetimeToTimeT(dst::fatDecodeUtcTime(rawTime)); //return real UTC time; throw std::runtime_error - else - markForDstHack.push_back(std::make_pair(itempath, filetimeToTimeT(rawTime.writeTimeRaw))); - } - //####################################### DST hack ########################################### - + const TraverseCallback::FileInfo fileInfo = Trav::extractFileInfo(findData, volumeSerial); sink.onFile(shortName, itempath, fileInfo); } } @@ -531,13 +458,13 @@ void DirTraverser::traverseWithException(const Zstring& dirpath, TraverseCallbac class DirTraverser { public: - static void execute(const Zstring& baseDirectory, TraverseCallback& sink, DstHackCallback* dstCallback) + static void execute(const Zstring& baseDirectory, TraverseCallback& sink) { - DirTraverser(baseDirectory, sink, dstCallback); + DirTraverser(baseDirectory, sink); } private: - DirTraverser(const Zstring& baseDirectory, zen::TraverseCallback& sink, zen::DstHackCallback* dstCallback) + DirTraverser(const Zstring& baseDirectory, zen::TraverseCallback& sink) { const Zstring directoryFormatted = //remove trailing slash baseDirectory.size() > 1 && endsWith(baseDirectory, FILE_NAME_SEPARATOR) ? //exception: allow '/' @@ -701,12 +628,4 @@ private: } -void zen::traverseFolder(const Zstring& dirpath, TraverseCallback& sink, DstHackCallback* dstCallback) -{ - warn_static("let's tentatively disable the DST hack:") - DirTraverser::execute(dirpath, sink, nullptr); - return; -#if 0 - DirTraverser::execute(dirpath, sink, dstCallback); -#endif -} +void zen::traverseFolder(const Zstring& dirpath, TraverseCallback& sink) { DirTraverser::execute(dirpath, sink); } diff --git a/zen/file_traverser.h b/zen/file_traverser.h index f240d8c7..c9efbdb1 100644 --- a/zen/file_traverser.h +++ b/zen/file_traverser.h @@ -58,23 +58,10 @@ struct TraverseCallback virtual HandleError reportItemError(const std::wstring& msg, size_t retryNumber, const Zchar* shortName) = 0; //failed to get data for single file/dir/symlink only! }; - -#ifdef ZEN_WIN -struct DstHackCallback -{ - virtual ~DstHackCallback() {} - virtual void requestUiRefresh(const Zstring& filepath) = 0; //applying DST hack imposes significant one-time performance drawback => callback to inform user -}; -#elif defined ZEN_LINUX || defined ZEN_MAC -struct DstHackCallback; //DST hack not required on Unix -#endif - //custom traverser with detail information about files //- client needs to handle duplicate file reports! (FilePlusTraverser fallback, retrying to read directory contents, ...) //- directory may end with PATH_SEPARATOR -void traverseFolder(const Zstring& dirpath, //throw() - TraverseCallback& sink, - DstHackCallback* dstCallback = nullptr); //apply DST hack if callback is supplied +void traverseFolder(const Zstring& dirpath, TraverseCallback& sink); //noexcept } #endif // FILETRAVERSER_H_INCLUDED diff --git a/zen/fixed_list.h b/zen/fixed_list.h index 63ef3f2f..627584df 100644 --- a/zen/fixed_list.h +++ b/zen/fixed_list.h @@ -11,22 +11,15 @@ namespace zen { -//std::list(C++11) compatible class for inplace element construction supporting non-copyable/movable types +//std::list(C++11)-like class for inplace element construction supporting non-copyable/movable types //may be replaced by C++11 std::list when available...or never... template class FixedList { struct Node { - Node() : next(nullptr), val() {} - //no variadic templates on VC2010... :( - template Node(A&& a) : next(nullptr), val(std::forward(a)) {} - template Node(A&& a, B&& b) : next(nullptr), val(std::forward(a), std::forward(b)) {} - template Node(A&& a, B&& b, C&& c) : next(nullptr), val(std::forward(a), std::forward(b), std::forward(c)) {} - template Node(A&& a, B&& b, C&& c, D&& d) : next(nullptr), val(std::forward(a), std::forward(b), std::forward(c), std::forward(d)) {} - template Node(A&& a, B&& b, C&& c, D&& d, E&& e) : next(nullptr), val(std::forward(a), std::forward(b), std::forward(c), std::forward(d), std::forward(e)) {} - template Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) : next(nullptr), val(std::forward(a), std::forward(b), std::forward(c), std::forward(d), std::forward(e), std::forward(f)) {} - template Node(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f, G&& g) : next(nullptr), val(std::forward(a), std::forward(b), std::forward(c), std::forward(d), std::forward(e), std::forward(f), std::forward(g)) {} + template + Node(Args&& ... args) : next(nullptr), val(std::forward(args)...) {} Node* next; //singly linked list is sufficient T val; @@ -48,20 +41,20 @@ public: ListIterator& operator++() { iter = iter->next; return *this; } inline friend bool operator==(const ListIterator& lhs, const ListIterator& rhs) { return lhs.iter == rhs.iter; } inline friend bool operator!=(const ListIterator& lhs, const ListIterator& rhs) { return !(lhs == rhs); } - U& operator* () { return iter->val; } - U* operator->() { return &iter->val; } + U& operator* () const { return iter->val; } + U* operator->() const { return &iter->val; } private: NodeT* iter; }; typedef T value_type; - typedef ListIterator iterator; + typedef ListIterator< Node, T> iterator; typedef ListIterator const_iterator; - typedef T& reference; + typedef T& reference; typedef const T& const_reference; iterator begin() { return firstInsert; } - iterator end() { return iterator(); } + iterator end () { return iterator(); } const_iterator begin() const { return firstInsert; } const_iterator end () const { return const_iterator(); } @@ -75,14 +68,8 @@ public: reference& back() { return lastInsert->val; } const_reference& back() const { return lastInsert->val; } - void emplace_back() { pushNode(new Node); } - template void emplace_back(A&& a) { pushNode(new Node(std::forward(a))); } - template void emplace_back(A&& a, B&& b) { pushNode(new Node(std::forward(a), std::forward(b))); } - template void emplace_back(A&& a, B&& b, C&& c) { pushNode(new Node(std::forward(a), std::forward(b), std::forward(c))); } - template void emplace_back(A&& a, B&& b, C&& c, D&& d) { pushNode(new Node(std::forward(a), std::forward(b), std::forward(c), std::forward(d))); } - template void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e) { pushNode(new Node(std::forward(a), std::forward(b), std::forward(c), std::forward(d), std::forward(e))); } - template void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f) { pushNode(new Node(std::forward(a), std::forward(b), std::forward(c), std::forward(d), std::forward(e), std::forward(f))); } - template void emplace_back(A&& a, B&& b, C&& c, D&& d, E&& e, F&& f, G&& g) { pushNode(new Node(std::forward(a), std::forward(b), std::forward(c), std::forward(d), std::forward(e), std::forward(f), std::forward(g))); } + template + void emplace_back(Args&& ... args) { pushNode(new Node(std::forward(args)...)); } template void remove_if(Predicate pred) @@ -126,6 +113,7 @@ public: } bool empty() const { return firstInsert == nullptr; } + size_t size() const { return sz; } private: diff --git a/zen/format_unit.h b/zen/format_unit.h index 237b9f04..9416e3e5 100644 --- a/zen/format_unit.h +++ b/zen/format_unit.h @@ -44,7 +44,7 @@ std::wstring includeNumberSeparator(const std::wstring& number); template inline std::wstring toGuiString(NumberType number) { - assert_static(IsInteger::value); + static_assert(IsInteger::value, ""); return ffs_Impl::includeNumberSeparator(zen::numberTo(number)); } } diff --git a/zen/guid.h b/zen/guid.h index b2dd90aa..83845e5c 100644 --- a/zen/guid.h +++ b/zen/guid.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef GUID_H_INCLUDED -#define GUID_H_INCLUDED +#ifndef GUID_H_INCLUDED_80425780237502345 +#define GUID_H_INCLUDED_80425780237502345 #include #include @@ -35,4 +35,4 @@ std::string generateGUID() //creates a 16 byte GUID } } -#endif // GUID_H_INCLUDED +#endif //GUID_H_INCLUDED_80425780237502345 diff --git a/zen/i18n.h b/zen/i18n.h index 3790e528..bf284c7f 100644 --- a/zen/i18n.h +++ b/zen/i18n.h @@ -37,7 +37,7 @@ struct TranslationHandler virtual std::wstring translate(const std::wstring& singular, const std::wstring& plural, std::int64_t n) = 0; }; -void setTranslator(TranslationHandler* newHandler = nullptr); //takes ownership +void setTranslator(std::unique_ptr&& newHandler = nullptr); //take ownership TranslationHandler* getTranslator(); @@ -58,7 +58,10 @@ namespace implementation inline std::wstring translate(const std::wstring& text) { - return getTranslator() ? getTranslator()->translate(text) : text; + if (TranslationHandler* t = getTranslator()) + return t->translate(text); + + return text; } //translate plural forms: "%x day" "%x days" @@ -68,13 +71,13 @@ std::wstring translate(const std::wstring& singular, const std::wstring& plural, { assert(contains(plural, L"%x")); - if (getTranslator()) + if (TranslationHandler* t = getTranslator()) { - std::wstring translation = getTranslator()->translate(singular, plural, n); + std::wstring translation = t->translate(singular, plural, n); assert(!contains(translation, L"%x")); return translation; } - else + return replaceCpy(std::abs(n) == 1 ? singular : plural, L"%x", toGuiString(n)); } @@ -94,7 +97,7 @@ std::unique_ptr& globalHandler() } inline -void setTranslator(TranslationHandler* newHandler) { implementation::globalHandler().reset(newHandler); } //takes ownership +void setTranslator(std::unique_ptr&& newHandler) { implementation::globalHandler() = std::move(newHandler); } inline TranslationHandler* getTranslator() { return implementation::globalHandler().get(); } diff --git a/zen/int64.h b/zen/int64.h index 4183c8da..671f3372 100644 --- a/zen/int64.h +++ b/zen/int64.h @@ -18,29 +18,29 @@ namespace zen { #ifdef ZEN_WIN inline - std::int64_t get64BitInt(DWORD low, LONG high) - { - static_assert(sizeof(low) + sizeof(high) == sizeof(std::int64_t), ""); +std::int64_t get64BitInt(DWORD low, LONG high) +{ + static_assert(sizeof(low) + sizeof(high) == sizeof(std::int64_t), ""); - LARGE_INTEGER cvt = {}; - cvt.LowPart = low; - cvt.HighPart = high; - return cvt.QuadPart; - } + LARGE_INTEGER cvt = {}; + cvt.LowPart = low; + cvt.HighPart = high; + return cvt.QuadPart; +} std::int64_t get64BitInt(std::uint64_t low, std::int64_t high) = delete; inline std::uint64_t get64BitUInt(DWORD low, DWORD high) - { - static_assert(sizeof(low) + sizeof(high) == sizeof(std::uint64_t), ""); +{ + static_assert(sizeof(low) + sizeof(high) == sizeof(std::uint64_t), ""); - ULARGE_INTEGER cvt = {}; - cvt.LowPart = low; - cvt.HighPart = high; - return cvt.QuadPart; - } + ULARGE_INTEGER cvt = {}; + cvt.LowPart = low; + cvt.HighPart = high; + return cvt.QuadPart; +} std::int64_t get64BitUInt(std::uint64_t low, std::uint64_t high) = delete; @@ -52,15 +52,15 @@ std::int64_t get64BitUInt(std::uint64_t low, std::uint64_t high) = delete; inline std::int64_t filetimeToTimeT(const FILETIME& ft) { - return static_cast(get64BitUInt(ft.dwLowDateTime, ft.dwHighDateTime) / 10000000U) - get64BitInt(3054539008UL, 2); //caveat: signed/unsigned arithmetics! + return static_cast(get64BitUInt(ft.dwLowDateTime, ft.dwHighDateTime) / 10000000U) - get64BitInt(3054539008UL, 2); //caveat: signed/unsigned arithmetics! //timeshift between ansi C time and FILETIME in seconds == 11644473600s } inline FILETIME timetToFileTime(std::int64_t utcTime) -{ - ULARGE_INTEGER cvt = {}; - cvt.QuadPart = (utcTime + get64BitInt(3054539008UL, 2)) * 10000000U; //caveat: signed/unsigned arithmetics! +{ + ULARGE_INTEGER cvt = {}; + cvt.QuadPart = (utcTime + get64BitInt(3054539008UL, 2)) * 10000000U; //caveat: signed/unsigned arithmetics! const FILETIME output = { cvt.LowPart, cvt.HighPart }; return output; diff --git a/zen/notify_removal.cpp b/zen/notify_removal.cpp index 45a39c48..639264a6 100644 --- a/zen/notify_removal.cpp +++ b/zen/notify_removal.cpp @@ -169,7 +169,7 @@ private: Pimpl (const Pimpl&) = delete; Pimpl& operator=(const Pimpl&) = delete; - virtual void onMessage(UINT message, WPARAM wParam, LPARAM lParam) //throw()! + void onMessage(UINT message, WPARAM wParam, LPARAM lParam) override //throw()! { //DBT_DEVICEQUERYREMOVE example: http://msdn.microsoft.com/en-us/library/aa363427(v=VS.85).aspx if (message == WM_DEVICECHANGE) @@ -205,10 +205,7 @@ private: //#################################################################################################### -NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir) -{ - pimpl.reset(new Pimpl(*this, hDir)); -} +NotifyRequestDeviceRemoval::NotifyRequestDeviceRemoval(HANDLE hDir) : pimpl(zen::make_unique(*this, hDir)) {} NotifyRequestDeviceRemoval::~NotifyRequestDeviceRemoval() {} //make sure ~unique_ptr() works with complete type diff --git a/zen/notify_removal.h b/zen/notify_removal.h index aca0912f..ba560f96 100644 --- a/zen/notify_removal.h +++ b/zen/notify_removal.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef NOTIFY_H_INCLUDED -#define NOTIFY_H_INCLUDED +#ifndef NOTIFY_H_INCLUDED_257804267562 +#define NOTIFY_H_INCLUDED_257804267562 #include #include "win.h" //includes "windows.h" @@ -24,12 +24,11 @@ private: //NOTE: onRemovalFinished is NOT guaranteed to execute after onRequestRemoval()! but most likely will virtual void onRemovalFinished(HANDLE hnd, bool successful) = 0; //throw()! - NotifyRequestDeviceRemoval(NotifyRequestDeviceRemoval&); //no copying - void operator=(NotifyRequestDeviceRemoval&); // + NotifyRequestDeviceRemoval (NotifyRequestDeviceRemoval&) = delete; + NotifyRequestDeviceRemoval& operator=(NotifyRequestDeviceRemoval&) = delete; class Pimpl; std::unique_ptr pimpl; }; - -#endif // NOTIFY_H_INCLUDED +#endif //NOTIFY_H_INCLUDED_257804267562 diff --git a/zen/osx_string.h b/zen/osx_string.h deleted file mode 100644 index ba83ca27..00000000 --- a/zen/osx_string.h +++ /dev/null @@ -1,82 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_STRING_1873641732143214324 -#define OSX_STRING_1873641732143214324 - -#include //CFString -#include "zstring.h" - -namespace osx -{ -Zstring cfStringToZstring(const CFStringRef& cfStr); - -CFStringRef createCFString (const char* utf8Str); //returns nullptr on error -CFMutableStringRef createMutableCFString(const char* utf8Str); //pass ownership! => ZEN_ON_SCOPE_EXIT(::CFRelease(str)); - - - - - - - - - - - - - -//################# implementation ##################### -inline -Zstring cfStringToZstring(const CFStringRef& cfStr) -{ - if (cfStr) - { - //perf: try to get away cheap: - if (const char* utf8Str = ::CFStringGetCStringPtr(cfStr, kCFStringEncodingUTF8)) - return utf8Str; - - CFIndex length = ::CFStringGetLength(cfStr); - if (length > 0) - { - CFIndex bufferSize = ::CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); - Zstring buffer; - buffer.resize(bufferSize); - - if (::CFStringGetCString(cfStr, &*buffer.begin(), bufferSize, kCFStringEncodingUTF8)) - { - buffer.resize(zen::strLength(buffer.c_str())); //caveat: memory consumption of returned string! - return buffer; - } - } - } - return Zstring(); -} - - -inline -CFStringRef createCFString(const char* utf8Str) -{ - //don't bother with CFStringCreateWithBytes: it's slightly slower, despite passing length info - return ::CFStringCreateWithCString(nullptr, //CFAllocatorRef alloc, - utf8Str, //const char *cStr, - kCFStringEncodingUTF8); //CFStringEncoding encoding -} - - -inline -CFMutableStringRef createMutableCFString(const char* utf8Str) -{ - if (CFMutableStringRef strRef = ::CFStringCreateMutable(NULL, 0)) - { - ::CFStringAppendCString(strRef, utf8Str, kCFStringEncodingUTF8); - return strRef; - } - return nullptr; -} -} - -#endif //OSX_STRING_1873641732143214324 diff --git a/zen/osx_throw_exception.h b/zen/osx_throw_exception.h deleted file mode 100644 index 07e3af3e..00000000 --- a/zen/osx_throw_exception.h +++ /dev/null @@ -1,56 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef OSX_EXCEPTION_89274305834255 -#define OSX_EXCEPTION_89274305834255 - -#import -#include "sys_error.h" -#include "utf.h" - -namespace osx -{ -//for use in Objective C implementation files only! -void throwSysError(NSException* e); //throw SysError - -#define ZEN_OSX_ASSERT(obj) ZEN_OSX_ASSERT_IMPL(obj, #obj) //throw SysError -/* -Example: ZEN_OSX_ASSERT(obj); - -Equivalent to: - if (!obj) - throw zen::SysError(L"Assertion failed: \"obj\"."); -*/ - - - - - - -//######################## implmentation ############################ -inline -void throwSysError(NSException* e) //throw SysError -{ - std::string msg; - if (const char* name = [[e name ] cStringUsingEncoding:NSUTF8StringEncoding]) //"const char*" NOT owned by us! - msg += name; - if (const char* descr = [[e reason] cStringUsingEncoding:NSUTF8StringEncoding]) - { - msg += "\n"; - msg += descr; - } - throw zen::SysError(zen::utfCvrtTo(msg)); - /* - e.g. - NSInvalidArgumentException - *** +[NSString stringWithCString:encoding:]: NULL cString - */ -} -} - -#define ZEN_OSX_ASSERT_IMPL(obj, txt) if (!(obj)) throw zen::SysError(std::wstring(L"Assertion failed: \"") + L ## txt + L"\"."); - -#endif //OSX_EXCEPTION_89274305834255 diff --git a/zen/privilege.cpp b/zen/privilege.cpp deleted file mode 100644 index c2db4701..00000000 --- a/zen/privilege.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "privilege.h" -#include -//#include -#include "win.h" //includes "windows.h" -#include "thread.h" -#include "zstring.h" -#include "scope_guard.h" -#include "win_ver.h" - -using namespace zen; - - -namespace -{ -bool privilegeIsActive(const wchar_t* privilege) //throw FileError -{ - HANDLE hToken = nullptr; - if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, - TOKEN_QUERY, //__in DWORD DesiredAccess, - &hToken)) //__out PHANDLE TokenHandle - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError()); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); - - LUID luid = {}; - if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, - privilege, //__in LPCTSTR lpName, - &luid )) //__out PLUID lpLuid - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError()); - - PRIVILEGE_SET priv = {}; - priv.PrivilegeCount = 1; - priv.Control = PRIVILEGE_SET_ALL_NECESSARY; - priv.Privilege[0].Luid = luid; - priv.Privilege[0].Attributes = SE_PRIVILEGE_ENABLED; - - BOOL alreadyGranted = FALSE; - if (!::PrivilegeCheck(hToken, //__in HANDLE ClientToken, - &priv, //__inout PPRIVILEGE_SET RequiredPrivileges, - &alreadyGranted)) //__out LPBOOL pfResult - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"PrivilegeCheck", getLastError()); - - return alreadyGranted != FALSE; -} - - -void setPrivilege(const wchar_t* privilege, bool enable) //throw FileError -{ - HANDLE hToken = nullptr; - if (!::OpenProcessToken(::GetCurrentProcess(), //__in HANDLE ProcessHandle, - TOKEN_ADJUST_PRIVILEGES, //__in DWORD DesiredAccess, - &hToken)) //__out PHANDLE TokenHandle - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"OpenProcessToken", getLastError()); - ZEN_ON_SCOPE_EXIT(::CloseHandle(hToken)); - - LUID luid = {}; - if (!::LookupPrivilegeValue(nullptr, //__in_opt LPCTSTR lpSystemName, - privilege, //__in LPCTSTR lpName, - &luid )) //__out PLUID lpLuid - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"LookupPrivilegeValue", getLastError()); - - TOKEN_PRIVILEGES tp = {}; - tp.PrivilegeCount = 1; - tp.Privileges[0].Luid = luid; - tp.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; - - if (!::AdjustTokenPrivileges(hToken, //__in HANDLE TokenHandle, - false, //__in BOOL DisableAllPrivileges, - &tp, //__in_opt PTOKEN_PRIVILEGES NewState, - 0, //__in DWORD BufferLength, - nullptr, //__out_opt PTOKEN_PRIVILEGES PreviousState, - nullptr)) //__out_opt PDWORD ReturnLength - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", getLastError()); - - DWORD lastError = ::GetLastError(); //copy before directly or indirectly making other system calls! - if (lastError == ERROR_NOT_ALL_ASSIGNED) //check although previous function returned with success! - { -#ifdef __MINGW32__ //Shobjidl.h -#define ERROR_ELEVATION_REQUIRED 740L -#endif - if (vistaOrLater()) //replace this useless error code with what it *really* means! - lastError = ERROR_ELEVATION_REQUIRED; - - throwFileError(replaceCpy(_("Cannot set privilege %x."), L"%x", std::wstring(L"\"") + privilege + L"\""), L"AdjustTokenPrivileges", lastError); - } -} - - -class Privileges -{ -public: - static Privileges& getInstance() - { - //meyers singleton: avoid static initialization order problem in global namespace! - static Privileges inst; - return inst; - } - - void ensureActive(const wchar_t* privilege) //throw FileError - { - boost::lock_guard dummy(lockPrivileges); - - if (activePrivileges.find(privilege) != activePrivileges.end()) - return; //privilege already active - - if (privilegeIsActive(privilege)) //privilege was already active before starting this tool - activePrivileges.insert(std::make_pair(privilege, false)); - else - { - setPrivilege(privilege, true); - activePrivileges.insert(std::make_pair(privilege, true)); - } - } - -private: - Privileges() {} - Privileges (const Privileges&) = delete; - Privileges& operator=(const Privileges&) = delete; - - ~Privileges() //clean up: deactivate all privileges that have been activated by this application - { - for (const auto& priv : activePrivileges) - if (priv.second) - { - try - { - setPrivilege(priv.first.c_str(), false); //throw FileError - } - catch (FileError&) {} - } - } - - std::map activePrivileges; //bool: enabled by this application -boost::mutex lockPrivileges; -}; - -//caveat: function scope static initialization is not thread-safe in VS 2010! -auto& dummy = Privileges::getInstance(); -} - - -void zen::activatePrivilege(const wchar_t* privilege) //throw FileError -{ - Privileges::getInstance().ensureActive(privilege); -} diff --git a/zen/privilege.h b/zen/privilege.h deleted file mode 100644 index e9b83be9..00000000 --- a/zen/privilege.h +++ /dev/null @@ -1,17 +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) Zenju (zenju AT gmx DOT de) - All Rights Reserved * -// ************************************************************************** - -#ifndef PRIVILEGE_H_INCLUDED -#define PRIVILEGE_H_INCLUDED - -#include "file_error.h" - -namespace zen -{ -void activatePrivilege(const wchar_t* privilege); //throw FileError; thread-safe!!! -} - -#endif // PRIVILEGE_H_INCLUDED diff --git a/zen/recycler.cpp b/zen/recycler.cpp index 930b05ac..5b5e44d4 100644 --- a/zen/recycler.cpp +++ b/zen/recycler.cpp @@ -5,12 +5,11 @@ // ************************************************************************** #include "recycler.h" -#include "file_handling.h" +#include "file_access.h" #ifdef ZEN_WIN #include "thread.h" #include "dll.h" -#include "assert_static.h" #include "win_ver.h" #include "long_path_prefix.h" #include "IFileOperation/file_op.h" @@ -46,11 +45,10 @@ Nevertheless, let's use IFileOperation for better error reporting (including det struct CallbackData { CallbackData(const std::function& notifyDeletionStatus) : - notifyDeletionStatus_(notifyDeletionStatus), - exceptionInUserCallback(false) {} + notifyDeletionStatus_(notifyDeletionStatus) {} - const std::function& notifyDeletionStatus_; //optional! - bool exceptionInUserCallback; + const std::function& notifyDeletionStatus_; //in, optional + std::exception_ptr exception; //out }; @@ -65,7 +63,7 @@ bool onRecyclerCallback(const wchar_t* itempath, void* sink) } catch (...) { - cbd.exceptionInUserCallback = true; //try again outside the C call stack! + cbd.exception = std::current_exception(); return false; } return true; @@ -85,8 +83,8 @@ void zen::recycleOrDelete(const std::vector& itempaths, const std::func if (vistaOrLater()) //new recycle bin usage: available since Vista { #define DEF_DLL_FUN(name) const DllFun name(fileop::getDllName(), fileop::funName_##name); -DEF_DLL_FUN(moveToRecycleBin); -DEF_DLL_FUN(getLastErrorMessage); + DEF_DLL_FUN(moveToRecycleBin); + DEF_DLL_FUN(getLastErrorMessage); #undef DEF_DLL_FUN if (!moveToRecycleBin || !getLastErrorMessage) @@ -100,12 +98,8 @@ DEF_DLL_FUN(getLastErrorMessage); CallbackData cbd(notifyDeletionStatus); if (!moveToRecycleBin(&cNames[0], cNames.size(), onRecyclerCallback, &cbd)) { - if (cbd.exceptionInUserCallback) - { - assert(notifyDeletionStatus); - notifyDeletionStatus(Zstring()); //should throw again!!! - assert(false); - } + if (cbd.exception) + std::rethrow_exception(cbd.exception); std::wstring itempathFmt = fmtFileName(itempaths[0]); //probably not the correct file name for file lists larger than 1! if (itempaths.size() > 1) @@ -188,7 +182,7 @@ bool zen::recycleOrDelete(const Zstring& itempath) //throw FileError #elif defined ZEN_MAC //we cannot use FSPathMoveObjectToTrashSync directly since it follows symlinks! - assert_static(sizeof(Zchar) == sizeof(char)); + static_assert(sizeof(Zchar) == sizeof(char), ""); const UInt8* itempathUtf8 = reinterpret_cast(itempath.c_str()); auto throwFileError = [&](OSStatus oss) diff --git a/zen/recycler.h b/zen/recycler.h index e900dfa3..b406408f 100644 --- a/zen/recycler.h +++ b/zen/recycler.h @@ -39,7 +39,6 @@ bool recycleOrDelete(const Zstring& itempath); //throw FileError, return "true" bool recycleBinExists(const Zstring& pathName, const std::function& onUpdateGui); //throw FileError void recycleOrDelete(const std::vector& filepaths, //throw FileError, return "true" if file/dir was actually deleted - //may throw: first exception is swallowed, updateStatus() is then called again where it should throw again and the exception will propagate as expected const std::function& notifyDeletionStatus); //optional; currentItem may be empty #endif } diff --git a/zen/scope_guard.h b/zen/scope_guard.h index d48eb922..761c6aea 100644 --- a/zen/scope_guard.h +++ b/zen/scope_guard.h @@ -40,8 +40,8 @@ protected: bool isDismissed() const { return dismissed_; } private: - ScopeGuardBase (const ScopeGuardBase&); // = delete - ScopeGuardBase& operator=(const ScopeGuardBase&); // + ScopeGuardBase (const ScopeGuardBase&) = delete; + ScopeGuardBase& operator=(const ScopeGuardBase&) = delete; bool dismissed_; }; @@ -52,7 +52,7 @@ class ScopeGuardImpl : public ScopeGuardBase { public: explicit ScopeGuardImpl(const F& fun) : fun_(fun) {} - explicit ScopeGuardImpl(F&& fun) : fun_(std::move(fun)) {} + explicit ScopeGuardImpl( F&& fun) : fun_(std::move(fun)) {} ScopeGuardImpl(ScopeGuardImpl&& other) : ScopeGuardBase(std::move(other)), fun_(std::move(other.fun_)) {} ~ScopeGuardImpl() diff --git a/zen/serialize.h b/zen/serialize.h index 64df0329..cd427c9c 100644 --- a/zen/serialize.h +++ b/zen/serialize.h @@ -7,6 +7,7 @@ #ifndef SERIALIZE_H_INCLUDED_83940578357 #define SERIALIZE_H_INCLUDED_83940578357 +#include #include #include "string_base.h" #include "file_io.h" @@ -54,8 +55,8 @@ private: //---------------------------------------------------------------------- //functions based on binary container abstraction -template void saveBinStream(const Zstring& filepath, const BinContainer& cont); //throw FileError -template BinContainer loadBinStream(const Zstring& filepath); //throw FileError +template void saveBinStream(const Zstring& filepath, const BinContainer& cont, const std::function& onUpdateStatus); //throw FileError +template BinContainer loadBinStream(const Zstring& filepath, const std::function& onUpdateStatus); //throw FileError /* @@ -135,20 +136,38 @@ template < class BinInputStream> void readArray (BinInputStream& stre //-----------------------implementation------------------------------- template inline -void saveBinStream(const Zstring& filepath, const BinContainer& cont) //throw FileError +void saveBinStream(const Zstring& filepath, //throw FileError + const BinContainer& cont, + const std::function& onUpdateStatus) //optional { - assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further) + static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes (until further) FileOutput fileOut(filepath, zen::FileOutput::ACC_OVERWRITE); //throw FileError if (!cont.empty()) - fileOut.write(&*cont.begin(), cont.size()); //throw FileError + { + const size_t blockSize = 128 * 1024; + auto bytePtr = &*cont.begin(); + size_t bytesLeft = cont.size(); + + while (bytesLeft > blockSize) + { + fileOut.write(bytePtr, blockSize); //throw FileError + bytePtr += blockSize; + bytesLeft -= blockSize; + if (onUpdateStatus) onUpdateStatus(blockSize); + } + + fileOut.write(bytePtr, bytesLeft); //throw FileError + if (onUpdateStatus) onUpdateStatus(bytesLeft); + } } template inline -BinContainer loadBinStream(const Zstring& filepath) //throw FileError +BinContainer loadBinStream(const Zstring& filepath, //throw FileError + const std::function& onUpdateStatus) //optional { - assert_static(sizeof(typename BinContainer::value_type) == 1); //expect: bytes (until further) + static_assert(sizeof(typename BinContainer::value_type) == 1, ""); //expect: bytes (until further) FileInput fileIn(filepath); //throw FileError @@ -161,6 +180,8 @@ BinContainer loadBinStream(const Zstring& filepath) //throw FileError const size_t bytesRead = fileIn.read(&*contOut.begin() + contOut.size() - blockSize, blockSize); //throw FileError if (bytesRead < blockSize) contOut.resize(contOut.size() - (blockSize - bytesRead)); //caveat: unsigned arithmetics + + if (onUpdateStatus) onUpdateStatus(bytesRead); } while (!fileIn.eof()); @@ -180,7 +201,7 @@ void writeArray(BinOutputStream& stream, const void* data, size_t len) template inline void writeNumber(BinOutputStream& stream, const N& num) { - assert_static((IsArithmetic::value || IsSameType::value)); + static_assert(IsArithmetic::value || IsSameType::value, ""); writeArray(stream, &num, sizeof(N)); } @@ -198,7 +219,7 @@ void writeContainer(BinOutputStream& stream, const C& cont) //don't even conside template inline void readArray(BinInputStream& stream, void* data, size_t len) //throw UnexpectedEndOfStreamError { - //expect external write of len bytes: + //expect external write of len bytes: const char* const src = static_cast(stream.requestRead(len)); //throw UnexpectedEndOfStreamError std::copy(src, src + len, static_cast(data)); } @@ -207,7 +228,7 @@ void readArray(BinInputStream& stream, void* data, size_t len) //throw Unexpecte template inline N readNumber(BinInputStream& stream) //throw UnexpectedEndOfStreamError { - assert_static((IsArithmetic::value || IsSameType::value)); + static_assert(IsArithmetic::value || IsSameType::value, ""); N num = 0; readArray(stream, &num, sizeof(N)); //throw UnexpectedEndOfStreamError return num; @@ -219,8 +240,8 @@ C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError { C cont; auto strLength = readNumber(stream); - if (strLength > 0) - { + if (strLength > 0) + { try { cont.resize(strLength); //throw std::bad_alloc @@ -229,8 +250,8 @@ C readContainer(BinInputStream& stream) //throw UnexpectedEndOfStreamError { throw UnexpectedEndOfStreamError(); } - readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError - } + readArray(stream, &*cont.begin(), sizeof(typename C::value_type) * strLength); //throw UnexpectedEndOfStreamError + } return cont; } } diff --git a/zen/shell_execute.h b/zen/shell_execute.h index 9f99315a..c2ccd837 100644 --- a/zen/shell_execute.h +++ b/zen/shell_execute.h @@ -31,7 +31,7 @@ enum ExecutionType namespace { -void shellExecute2(const Zstring& command, ExecutionType type) //throw FileError +void shellExecute(const Zstring& command, ExecutionType type) //throw FileError { #ifdef ZEN_WIN //parse commandline diff --git a/zen/stl_tools.h b/zen/stl_tools.h index 38752e67..eb94b4a1 100644 --- a/zen/stl_tools.h +++ b/zen/stl_tools.h @@ -9,13 +9,6 @@ #include #include -#if defined _MSC_VER && _MSC_VER <= 1600 -#include -#include -#else -#include -#include -#endif //enhancements for @@ -38,8 +31,8 @@ template V& map_add_or_update(M& map, const K& key, const V& value); //efficient add or update without "default-constructible" requirement (Effective STL, item 24) //binary search returning an iterator -template -ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp); +template +ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less); template BidirectionalIterator find_last(BidirectionalIterator first, BidirectionalIterator last, const T& value); @@ -53,26 +46,9 @@ template bool equal(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2); -//hash container: proper name + mitigate MSVC performance bug -template class hash_set; -template class hash_map; - -template -std::unique_ptr make_unique(Arg1&& arg1); //should eventually make it into the std at some time - - - - - - - - - - - - - - +//until std::make_unique is available in GCC: +template inline +std::unique_ptr make_unique(Args&& ... args) { return std::unique_ptr(new T(std::forward(args)...)); } @@ -127,11 +103,11 @@ V& map_add_or_update(M& map, const K& key, const V& value) //efficient add or up } -template inline -ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, Compare comp) +template inline +ForwardIterator binary_search(ForwardIterator first, ForwardIterator last, const T& value, CompLess less) { - first = std::lower_bound(first, last, value, comp); - if (first != last && !comp(value, *first)) + first = std::lower_bound(first, last, value, less); + if (first != last && !less(value, *first)) return first; else return last; @@ -187,32 +163,10 @@ bool equal(InputIterator1 first1, InputIterator1 last1, } -#if defined _MSC_VER && _MSC_VER <= 1600 //VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11 -template class hash_set : public std::set {}; -template class hash_map : public std::map {}; -#else -template class hash_set : public std::unordered_set {}; -template class hash_map : public std::unordered_map {}; -//C++11: -//template using hash_set = std::unordered_set; -//template using hash_map = std::unordered_map; +#if defined _MSC_VER && _MSC_VER <= 1600 +//VS2010 performance bug in std::unordered_set<>: http://drdobbs.com/blogs/cpp/232200410 -> should be fixed in VS11 +static_assert(false, ""); #endif - -//as long as variadic templates are not available in MSVC -template inline std::unique_ptr make_unique() { return std::unique_ptr(new T); } -template inline std::unique_ptr make_unique(Arg1&& arg1) { return std::unique_ptr(new T(std::forward(arg1))); } -template inline std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2) { return std::unique_ptr(new T(std::forward(arg1), std::forward(arg2))); } -template inline std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3) { return std::unique_ptr(new T(std::forward(arg1), std::forward(arg2), std::forward(arg3))); } -template inline std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4) { return std::unique_ptr(new T(std::forward(arg1), std::forward(arg2), std::forward(arg3), std::forward(arg4))); } -template inline std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5) { return std::unique_ptr(new T(std::forward(arg1), std::forward(arg2), std::forward(arg3), std::forward(arg4), std::forward(arg5))); } -template inline std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5, Arg6&& arg6) { return std::unique_ptr(new T(std::forward(arg1), std::forward(arg2), std::forward(arg3), std::forward(arg4), std::forward(arg5), std::forward(arg6))); } -template inline std::unique_ptr make_unique(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5, Arg6&& arg6, Arg7&& arg7) { return std::unique_ptr(new T(std::forward(arg1), std::forward(arg2), std::forward(arg3), std::forward(arg4), std::forward(arg5), std::forward(arg6), std::forward(arg7))); } - -//template inline -//std::unique_ptr make_unique(Args&& ...args) -//{ -// return std::unique_ptr(new T( std::forward(args)... )); -//} } #endif //STL_TOOLS_HEADER_84567184321434 diff --git a/zen/string_base.h b/zen/string_base.h index a458ef15..0f9ad479 100644 --- a/zen/string_base.h +++ b/zen/string_base.h @@ -21,7 +21,7 @@ namespace zen Allocator Policy: ----------------- void* allocate(size_t size) //throw std::bad_alloc - void deallocate(void* ptr) //must handle deallocate(nullptr)! + void deallocate(void* ptr) size_t calcCapacity(size_t length) */ class AllocatorOptimalSpeed //exponential growth + min size @@ -53,7 +53,7 @@ template = minCapacity); - Descriptor* const newDescr = static_cast(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); - - newDescr->length = size; - newDescr->capacity = newCapacity; + Descriptor* const newDescr = static_cast(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc + new (newDescr) Descriptor(size, newCapacity); return reinterpret_cast(newDescr + 1); //alignment note: "newDescr + 1" is Descriptor-aligned, which is larger than alignment for Char-array! => no problem! } - static Char* clone(Char* ptr) + Char* clone(Char* ptr) { - Char* newData = create(length(ptr)); + Char* newData = create(length(ptr)); //throw std::bad_alloc std::copy(ptr, ptr + length(ptr) + 1, newData); return newData; } - static void destroy(Char* ptr) { AP::deallocate(descr(ptr)); } //should support destroy(nullptr)! + void destroy(Char* ptr) + { + if (!ptr) return; //support "destroy(nullptr)" + + Descriptor* const d = descr(ptr); + d->~Descriptor(); + this->deallocate(d); + } //this needs to be checked before writing to "ptr" static bool canWrite(const Char* ptr, size_t minCapacity) { return minCapacity <= descr(ptr)->capacity; } @@ -103,6 +108,10 @@ protected: private: struct Descriptor { + Descriptor(size_t len, size_t cap) : + length (static_cast(len)), + capacity(static_cast(cap)) {} + std::uint32_t length; std::uint32_t capacity; //allocated size without null-termination }; @@ -119,42 +128,44 @@ class StorageRefCountThreadSafe : public AP protected: ~StorageRefCountThreadSafe() {} - static Char* create(size_t size) { return create(size, size); } - static Char* create(size_t size, size_t minCapacity) + Char* create(size_t size) { return create(size, size); } + Char* create(size_t size, size_t minCapacity) { assert(size <= minCapacity); const size_t newCapacity = AP::calcCapacity(minCapacity); assert(newCapacity >= minCapacity); - Descriptor* const newDescr = static_cast(AP::allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); - new (newDescr) Descriptor(1, size, newCapacity); + Descriptor* const newDescr = static_cast(this->allocate(sizeof(Descriptor) + (newCapacity + 1) * sizeof(Char))); //throw std::bad_alloc + new (newDescr) Descriptor(size, newCapacity); return reinterpret_cast(newDescr + 1); } static Char* clone(Char* ptr) { - assert(descr(ptr)->refCount > 0); ++descr(ptr)->refCount; return ptr; } - static void destroy(Char* ptr) + void destroy(Char* ptr) { - if (!ptr) return; //support destroy(nullptr) - assert(descr(ptr)->refCount > 0); - if (--descr(ptr)->refCount == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation! + if (!ptr) return; //support "destroy(nullptr)" + + Descriptor* const d = descr(ptr); + + if (--(d->refCount) == 0) //operator--() is overloaded to decrement and evaluate in a single atomic operation! { - descr(ptr)->~Descriptor(); - AP::deallocate(descr(ptr)); + d->~Descriptor(); + this->deallocate(d); } } static bool canWrite(const Char* ptr, size_t minCapacity) //needs to be checked before writing to "ptr" { - assert(descr(ptr)->refCount > 0); - return descr(ptr)->refCount == 1 && minCapacity <= descr(ptr)->capacity; + const Descriptor* const d = descr(ptr); + assert(d->refCount > 0); + return d->refCount == 1 && minCapacity <= d->capacity; } static size_t length(const Char* ptr) { return descr(ptr)->length; } @@ -168,14 +179,14 @@ protected: private: struct Descriptor { - Descriptor(int rc, size_t len, size_t cap) : + Descriptor(size_t len, size_t cap) : + refCount(1), length (static_cast(len)), - capacity(static_cast(cap)), - refCount(rc) { assert_static(ATOMIC_INT_LOCK_FREE == 2); } //2: "the types are always lock-free" + capacity(static_cast(cap)) { static_assert(ATOMIC_INT_LOCK_FREE == 2, ""); } //2: "the types are always lock-free" + std::atomic refCount; std::uint32_t length; std::uint32_t capacity; //allocated size without null-termination - std::atomic refCount; //practically no perf loss: ~0.2%! (FFS comparison) }; static Descriptor* descr( Char* ptr) { return reinterpret_cast< Descriptor*>(ptr) - 1; } @@ -237,7 +248,7 @@ public: Zbase& assign(const Char* source, size_t len); Zbase& append(const Char* source, size_t len); void resize(size_t newSize, Char fillChar = 0); - void swap(Zbase& other); + void swap(Zbase& other); //make noexcept in C++11 void push_back(Char val) { operator+=(val); } //STL access Zbase& operator=(const Zbase& source); @@ -251,10 +262,10 @@ public: static const size_t npos = static_cast(-1); private: - Zbase(int); // - Zbase& operator=(int); //detect usage errors by creating an intentional ambiguity with "Char" - Zbase& operator+=(int); // - void push_back(int); // + Zbase (int) = delete; // + Zbase& operator= (int) = delete; //detect usage errors by creating an intentional ambiguity with "Char" + Zbase& operator+=(int) = delete; // + void push_back (int) = delete; // Char* rawStr; }; @@ -626,8 +637,7 @@ Zbase& Zbase::operator=(const Zbase& o template class SP, class AP> inline Zbase& Zbase::operator=(Zbase&& tmp) { - //don't use unifying assignment but save one move-construction in the r-value case instead! - swap(tmp); + swap(tmp); //don't use unifying assignment but save one move-construction in the r-value case instead! return *this; } diff --git a/zen/string_tools.h b/zen/string_tools.h index a0b02d14..addf2bd5 100644 --- a/zen/string_tools.h +++ b/zen/string_tools.h @@ -82,7 +82,7 @@ bool isWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; } template inline bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()! { - assert_static((IsSameType::value || IsSameType::value)); + static_assert(IsSameType::value || IsSameType::value, ""); return static_cast('0') <= ch && ch <= static_cast('9'); } @@ -90,7 +90,7 @@ bool isDigit(Char ch) //similar to implmenetation of std::::isdigit()! template inline bool startsWith(const S& str, const T& prefix) { - assert_static(IsStringLike::value && IsStringLike::value); + static_assert(IsStringLike::value && IsStringLike::value, ""); typedef typename GetCharType::Type CharType; const size_t pfLength = strLength(prefix); @@ -106,7 +106,7 @@ bool startsWith(const S& str, const T& prefix) template inline bool endsWith(const S& str, const T& postfix) { - assert_static(IsStringLike::value && IsStringLike::value); + static_assert(IsStringLike::value && IsStringLike::value, ""); typedef typename GetCharType::Type CharType; const size_t strLen = strLength(str); @@ -123,7 +123,7 @@ bool endsWith(const S& str, const T& postfix) template inline bool contains(const S& str, const T& term) { - assert_static(IsStringLike::value && IsStringLike::value); + static_assert(IsStringLike::value && IsStringLike::value, ""); typedef typename GetCharType::Type CharType; const size_t strLen = strLength(str); @@ -144,7 +144,7 @@ bool contains(const S& str, const T& term) template inline S afterLast(const S& str, const T& term) { - assert_static(IsStringLike::value); + static_assert(IsStringLike::value, ""); typedef typename GetCharType::Type CharType; const size_t termLen = strLength(term); @@ -167,7 +167,7 @@ S afterLast(const S& str, const T& term) template inline S beforeLast(const S& str, const T& term) { - assert_static(IsStringLike::value); + static_assert(IsStringLike::value, ""); typedef typename GetCharType::Type CharType; const CharType* const strFirst = strBegin(str); @@ -187,7 +187,7 @@ S beforeLast(const S& str, const T& term) template inline S afterFirst(const S& str, const T& term) { - assert_static(IsStringLike::value); + static_assert(IsStringLike::value, ""); typedef typename GetCharType::Type CharType; const size_t termLen = strLength(term); @@ -209,7 +209,7 @@ S afterFirst(const S& str, const T& term) template inline S beforeFirst(const S& str, const T& term) { - assert_static(IsStringLike::value); + static_assert(IsStringLike::value, ""); typedef typename GetCharType::Type CharType; const CharType* const strFirst = strBegin(str); @@ -223,7 +223,7 @@ S beforeFirst(const S& str, const T& term) template inline std::vector split(const S& str, const T& delimiter) { - assert_static(IsStringLike::value); + static_assert(IsStringLike::value, ""); typedef typename GetCharType::Type CharType; std::vector output; @@ -245,7 +245,7 @@ std::vector split(const S& str, const T& delimiter) const CharType* const blockEnd = std::search(blockStart, strLast, delimFirst, delimLast); - output.push_back(S(blockStart, blockEnd - blockStart)); + output.emplace_back(blockStart, blockEnd - blockStart); if (blockEnd == strLast) break; blockStart = blockEnd + delimLen; @@ -271,7 +271,7 @@ typename EnableIf::value>::Type stringAppend(S& str, const template inline S replaceCpy(const S& str, const T& oldTerm, const U& newTerm, bool replaceAll) { - assert_static(IsStringLike::value && IsStringLike::value); + static_assert(IsStringLike::value && IsStringLike::value, ""); typedef typename GetCharType::Type CharType; const size_t oldLen = strLength(oldTerm); @@ -395,10 +395,10 @@ int saferPrintf(wchar_t* buffer, size_t bufferSize, const wchar_t* format, const template inline S printNumber(const T& format, const Num& number) //format a single number using ::sprintf { - assert_static(IsStringLike::value); - assert_static((IsSameType< - typename GetCharType::Type, - typename GetCharType::Type>::value)); + static_assert(IsStringLike::value, ""); + static_assert(IsSameType< + typename GetCharType::Type, + typename GetCharType::Type>::value, ""); typedef typename GetCharType::Type CharType; diff --git a/zen/string_traits.h b/zen/string_traits.h index 69c1fbc8..ba97397c 100644 --- a/zen/string_traits.h +++ b/zen/string_traits.h @@ -8,8 +8,6 @@ #define STRING_TRAITS_HEADER_813274321443234 #include "type_tools.h" -#include "assert_static.h" - //uniform access to string-like types, both classes and character arrays namespace zen @@ -165,7 +163,11 @@ namespace implementation template inline size_t cStringLength(const C* str) //naive implementation seems somewhat faster than "optimized" strlen/wcslen! { - assert_static((IsSameType::value || IsSameType::value)); +#if defined _MSC_VER && _MSC_VER > 1800 + static_assert(false, "strlen/wcslen are vectorized in VS14 CTP3 -> test again!"); +#endif + + static_assert(IsSameType::value || IsSameType::value, ""); size_t len = 0; while (*str++ != 0) ++len; @@ -184,8 +186,8 @@ inline const char* strBegin(const char* str) { return str; } inline const wchar_t* strBegin(const wchar_t* str) { return str; } inline const char* strBegin(const char& ch) { return &ch; } inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; } -inline const char* strBegin(const StringRef& ref) { return ref.data(); } -inline const wchar_t* strBegin(const StringRef& ref) { return ref.data(); } +inline const char* strBegin(const StringRef& ref) { return ref.data(); } +inline const wchar_t* strBegin(const StringRef& ref) { return ref.data(); } template inline diff --git a/zen/sys_error.h b/zen/sys_error.h index 8afab138..9cfc762f 100644 --- a/zen/sys_error.h +++ b/zen/sys_error.h @@ -35,7 +35,7 @@ typedef int ErrorCode; ErrorCode getLastError(); std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError); - +std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError, const std::wstring& lastErrorMsg); //A low-level exception class giving (non-translated) detail information only - same conceptional level like "GetLastError()"! class SysError @@ -75,11 +75,7 @@ std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastE { const ErrorCode currentError = getLastError(); //not necessarily == lastError - //determine error code if none was specified - if (lastError == 0) - lastError = currentError; - - std::wstring output = replaceCpy(_("Error Code %x:"), L"%x", numberTo(lastError)); + std::wstring lastErrorMsg; #ifdef ZEN_WIN ZEN_ON_SCOPE_EXIT(::SetLastError(currentError)); //this function must not change active system error variable! @@ -92,22 +88,37 @@ std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastE if (buffer) //"don't trust nobody" { ZEN_ON_SCOPE_EXIT(::LocalFree(buffer)); - output += L" "; - output += buffer; + lastErrorMsg = buffer; } #elif defined ZEN_LINUX || defined ZEN_MAC ZEN_ON_SCOPE_EXIT(errno = currentError); - output += L" "; - output += utfCvrtTo(::strerror(lastError)); + lastErrorMsg = utfCvrtTo(::strerror(lastError)); #endif + + return formatSystemError(functionName, lastError, lastErrorMsg); +} + + +inline +std::wstring formatSystemError(const std::wstring& functionName, ErrorCode lastError, const std::wstring& lastErrorMsg) +{ + std::wstring output = replaceCpy(_("Error Code %x:"), L"%x", numberTo(lastError)); + + if (!lastErrorMsg.empty()) + { + output += L" "; + output += lastErrorMsg; + } + if (!endsWith(output, L" ")) //Windows messages seem to end with a blank... output += L" "; output += L"(" + functionName + L")"; return output; } + } #endif //SYS_ERROR_H_3284791347018951324534 diff --git a/zen/thread.h b/zen/thread.h index 8c72e43f..a834f070 100644 --- a/zen/thread.h +++ b/zen/thread.h @@ -88,13 +88,15 @@ private: #error just some paranoia check... #endif -template inline -auto async2(Function fun) -> boost::unique_future //support for workaround of VS2010 bug: bool (*fun)(); decltype(fun()) == int! +template inline +auto async(Function fun) -> boost::unique_future { + typedef decltype(fun()) ResultType; + #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK //mirror "boost/thread/future.hpp", hopefully they know what they're doing - boost::packaged_task pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ + boost::packaged_task pt(std::move(fun)); //packaged task seems to even require r-value reference: https://sourceforge.net/p/freefilesync/bugs/234/ #else - boost::packaged_task pt(std::move(fun)); + boost::packaged_task pt(std::move(fun)); #endif auto fut = pt.get_future(); boost::thread(std::move(pt)).detach(); //we have to explicitly detach since C++11: [thread.thread.destr] ~thread() calls std::terminate() if joinable()!!! @@ -102,10 +104,6 @@ auto async2(Function fun) -> boost::unique_future //support for workaround of } -template inline -auto async(Function fun) -> boost::unique_future { return async2(fun); } - - template inline bool wait_for_all_timed(InputIterator first, InputIterator last, const Duration& wait_duration) { diff --git a/zen/tick_count.h b/zen/tick_count.h index 482689d7..56179b62 100644 --- a/zen/tick_count.h +++ b/zen/tick_count.h @@ -64,9 +64,9 @@ public: #ifdef ZEN_WIN return numeric::dist(lhs.val_.QuadPart, rhs.val_.QuadPart); //std::abs(a - b) can lead to overflow! #elif defined ZEN_LINUX -//structure timespec documented with members: -// time_t tv_sec seconds -// long tv_nsec nanoseconds + //structure timespec documented with members: + // time_t tv_sec seconds + // long tv_nsec nanoseconds const int64_t deltaSec = lhs.val_.tv_sec - rhs.val_.tv_sec; const int64_t deltaNsec = lhs.val_.tv_nsec - rhs.val_.tv_nsec; return numeric::abs(deltaSec * 1000000000 + deltaNsec); @@ -113,9 +113,9 @@ int64_t ticksPerSec() //return 0 on error mach_timebase_info_data_t tbi = {}; if (::mach_timebase_info(&tbi) != KERN_SUCCESS) return 0; -//structure mach_timebase_info_data_t documented with members: -// uint32_t numer; -// uint32_t denom; + //structure mach_timebase_info_data_t documented with members: + // uint32_t numer; + // uint32_t denom; return static_cast(1000000000) * tbi.denom / tbi.numer; #endif } @@ -126,10 +126,10 @@ TickVal getTicks() //return !isValid() on error { #ifdef ZEN_WIN LARGE_INTEGER now = {}; - if (!::QueryPerformanceCounter(&now)) + if (!::QueryPerformanceCounter(&now)) return TickVal(); - //detailed info about QPC: http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx - //- MSDN: "No need to set the thread affinity" + //detailed info about QPC: http://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx + //- MSDN: "No need to set the thread affinity" #elif defined ZEN_LINUX //gettimeofday() seems fine but is deprecated diff --git a/zen/time.h b/zen/time.h index e94300a0..e53e4c6e 100644 --- a/zen/time.h +++ b/zen/time.h @@ -49,8 +49,8 @@ const struct FormatIsoDateTimeTag {} FORMAT_ISO_DATE_TIME = {}; //%Y-%m-%d %H:%M //---------------------------------------------------------------------------------------------------------------------------------- -template -bool parseTime(const String& format, const String& str, TimeComp& comp); //similar to ::strptime(), return true on success +template +bool parseTime(const String& format, const String2& str, TimeComp& comp); //similar to ::strptime(), return true on success //---------------------------------------------------------------------------------------------------------------------------------- @@ -272,10 +272,11 @@ String formatTime(const String2& format, const TimeComp& comp) } -template -bool parseTime(const String& format, const String& str, TimeComp& comp) //return true on success +template +bool parseTime(const String& format, const String2& str, TimeComp& comp) //return true on success { typedef typename GetCharType::Type CharType; + static_assert(IsSameType::Type>::value, ""); const CharType* iterFmt = strBegin(format); const CharType* const fmtLast = iterFmt + strLength(format); @@ -285,7 +286,7 @@ bool parseTime(const String& format, const String& str, TimeComp& comp) //return auto extractNumber = [&](int& result, size_t digitCount) -> bool { - if (strLast - iterStr < digitCount) + if (strLast - iterStr < makeSigned(digitCount)) return false; if (std::any_of(iterStr, iterStr + digitCount, [](CharType c) { return !isDigit(c); })) diff --git a/zen/utf.h b/zen/utf.h index 281185d3..e742c1e2 100644 --- a/zen/utf.h +++ b/zen/utf.h @@ -104,7 +104,7 @@ size_t getUtf16Len(Char16 ch) //ch must be first code unit! returns 0 on error! template inline void utf16ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint { - assert_static(sizeof(typename std::iterator_traits::value_type) == 2); + static_assert(sizeof(typename std::iterator_traits::value_type) == 2, ""); for ( ; first != last; ++first) { @@ -201,7 +201,7 @@ bool decodeTrail(CharIterator& first, CharIterator last, CodePoint& cp) //decode template inline void utf8ToCodePoint(CharIterator first, CharIterator last, Function writeOutput) //"writeOutput" is a unary function taking a CodePoint { - assert_static(sizeof(typename std::iterator_traits::value_type) == 1); + static_assert(sizeof(typename std::iterator_traits::value_type) == 1, ""); for ( ; first != last; ++first) { @@ -337,12 +337,12 @@ size_t findUnicodePosWide(const WideString& str, size_t unicodePos, Int2Type<2>) if (utfPos >= strLen) return strLen; - size_t utf16len = getUtf16Len(strFirst[utfPos]); + size_t utf16len = getUtf16Len(strFirst[utfPos]); if (utf16len == 0) ++utf16len; //invalid utf16 character utfPos += utf16len; } - if (utfPos >= strLen) - return strLen; + if (utfPos >= strLen) + return strLen; return utfPos; } @@ -416,8 +416,8 @@ CharString wideToUtf8(const WideString& str, Int2Type<4>) //other OS: convert ut template inline WideString utf8ToWide(const CharString& str) { - assert_static((IsSameType::Type, char >::value)); - assert_static((IsSameType::Type, wchar_t>::value)); + static_assert(IsSameType::Type, char >::value, ""); + static_assert(IsSameType::Type, wchar_t>::value, ""); return implementation::utf8ToWide(str, Int2Type()); } @@ -426,8 +426,8 @@ WideString utf8ToWide(const CharString& str) template inline CharString wideToUtf8(const WideString& str) { - assert_static((IsSameType::Type, char >::value)); - assert_static((IsSameType::Type, wchar_t>::value)); + static_assert(IsSameType::Type, char >::value, ""); + static_assert(IsSameType::Type, wchar_t>::value, ""); return implementation::wideToUtf8(str, Int2Type()); } diff --git a/zen/win_ver.h b/zen/win_ver.h index 0d3f8d70..1d7ce7f0 100644 --- a/zen/win_ver.h +++ b/zen/win_ver.h @@ -7,24 +7,26 @@ #ifndef WINDOWS_VERSION_HEADER_238470348254325 #define WINDOWS_VERSION_HEADER_238470348254325 +#include #include #include "win.h" //includes "windows.h" namespace zen { - struct OsVersion - { - OsVersion() : major(), minor() {} - OsVersion(DWORD high, DWORD low) : major(high), minor(low) {} +struct OsVersion +{ + OsVersion() : major(), minor() {} + OsVersion(DWORD high, DWORD low) : major(high), minor(low) {} - DWORD major; - DWORD minor; - }; - inline bool operator< (const OsVersion& lhs, const OsVersion& rhs) { return lhs.major != rhs.major ? lhs.major < rhs.major : lhs.minor < rhs.minor; } - inline bool operator==(const OsVersion& lhs, const OsVersion& rhs) { return lhs.major == rhs.major && lhs.minor == rhs.minor; } + DWORD major; + DWORD minor; +}; +inline bool operator< (const OsVersion& lhs, const OsVersion& rhs) { return lhs.major != rhs.major ? lhs.major < rhs.major : lhs.minor < rhs.minor; } +inline bool operator==(const OsVersion& lhs, const OsVersion& rhs) { return lhs.major == rhs.major && lhs.minor == rhs.minor; } //version overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx +const OsVersion osVersionWin10 (6, 4); const OsVersion osVersionWin81 (6, 3); const OsVersion osVersionWin8 (6, 2); const OsVersion osVersionWin7 (6, 1); @@ -61,11 +63,18 @@ OsVersion getOsVersion() { OSVERSIONINFO osvi = {}; osvi.dwOSVersionInfoSize = sizeof(osvi); +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4996) //"'GetVersionExW': was declared deprecated" +#endif if (!::GetVersionEx(&osvi)) //38 ns per call! (yes, that's nano!) -> we do NOT miss C++11 thread-safe statics right now... - { - assert(false); - return OsVersion(); - } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + { + assert(false); + return OsVersion(); + } return OsVersion(osvi.dwMajorVersion, osvi.dwMinorVersion); } @@ -78,16 +87,16 @@ bool isRealOsVersion(const OsVersion& ver) verInfo.dwMajorVersion = ver.major; verInfo.dwMinorVersion = ver.minor; - //Syntax: http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491%28v=vs.85%29.aspx + //Syntax: http://msdn.microsoft.com/en-us/library/windows/desktop/ms725491%28v=vs.85%29.aspx DWORDLONG conditionMask = 0; VER_SET_CONDITION(conditionMask, VER_MAJORVERSION, VER_EQUAL); VER_SET_CONDITION(conditionMask, VER_MINORVERSION, VER_EQUAL); const bool rv = ::VerifyVersionInfo(&verInfo, VER_MAJORVERSION | VER_MINORVERSION, conditionMask) - == TRUE; //silence VC "performance warnings" - assert(rv || GetLastError() == ERROR_OLD_WIN_VERSION); + == TRUE; //silence VC "performance warnings" + assert(rv || GetLastError() == ERROR_OLD_WIN_VERSION); - return rv; + return rv; } } diff --git a/zen/xml_io.cpp b/zen/xml_io.cpp index 485d78bb..9ac4b87f 100644 --- a/zen/xml_io.cpp +++ b/zen/xml_io.cpp @@ -5,7 +5,7 @@ // ************************************************************************** #include "xml_io.h" -#include "file_handling.h" +#include "file_access.h" #include "file_io.h" #include "serialize.h" @@ -66,7 +66,7 @@ void zen::saveXmlDocument(const XmlDoc& doc, const Zstring& filepath) //throw Fi try { if (getFilesize(filepath) == stream.size()) //throw FileError - if (loadBinStream(filepath) == stream) //throw FileError + if (loadBinStream(filepath, nullptr) == stream) //throw FileError return; } catch (FileError&) {} diff --git a/zen/zstring.cpp b/zen/zstring.cpp index f16af6a0..a2a26f83 100644 --- a/zen/zstring.cpp +++ b/zen/zstring.cpp @@ -6,6 +6,7 @@ #include "zstring.h" #include +#include #ifdef ZEN_WIN #include "dll.h" @@ -40,7 +41,7 @@ public: void insert(const void* ptr, size_t size) { boost::lock_guard dummy(lockActStrings); - if (!activeStrings.insert(std::make_pair(ptr, size)).second) + if (!activeStrings.emplace(ptr, size).second) reportProblem("Serious Error: New memory points into occupied space: " + rawMemToString(ptr, size)); } @@ -96,7 +97,7 @@ private: } boost::mutex lockActStrings; - zen::hash_map activeStrings; + std::unordered_map activeStrings; }; //caveat: function scope static initialization is not thread-safe in VS 2010! @@ -148,15 +149,15 @@ const SysDllFun compareStringOrdinal = SysDllFun(sizeLhs), //__in int cchCount1, - rhs, //__in LPCWSTR lpString2, - static_cast(sizeRhs), //__in int cchCount2, - true); //__in BOOL bIgnoreCase + const int rv = compareStringOrdinal(lhs.c_str(), //__in LPCWSTR lpString1, + static_cast(lhs.size()), //__in int cchCount1, + rhs.c_str(), //__in LPCWSTR lpString2, + static_cast(rhs.size()), //__in int cchCount2, + true); //__in BOOL bIgnoreCase if (rv <= 0) throw std::runtime_error("Error comparing strings (CompareStringOrdinal)."); else @@ -167,31 +168,35 @@ int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_ //do NOT use "CompareString"; this function is NOT accurate (even with LOCALE_INVARIANT and SORT_STRINGSORT): for example "weiß" == "weiss"!!! //the only reliable way to compare filepaths (with XP) is to call "CharUpper" or "LCMapString": - auto copyToUpperCase = [](const wchar_t* strIn, wchar_t* strOut, size_t len) + const size_t sizeLhs = lhs.size(); + const size_t sizeRhs = rhs.size(); + + const auto minSize = std::min(sizeLhs, sizeRhs); + + if (minSize == 0) //LCMapString does not allow input sizes of 0! + return static_cast(sizeLhs) - static_cast(sizeRhs); + + auto copyToUpperCase = [&](const wchar_t* strIn, wchar_t* strOut) { //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString() - if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale, - LCMAP_UPPERCASE, //__in DWORD dwMapFlags, - strIn, //__in LPCTSTR lpSrcStr, - static_cast(len), //__in int cchSrc, - strOut, //__out LPTSTR lpDestStr, - static_cast(len)) == 0) //__in int cchDest + if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale, + LCMAP_UPPERCASE, //__in DWORD dwMapFlags, + strIn, //__in LPCTSTR lpSrcStr, + static_cast(minSize), //__in int cchSrc, + strOut, //__out LPTSTR lpDestStr, + static_cast(minSize)) == 0) //__in int cchDest throw std::runtime_error("Error comparing strings (LCMapString)."); }; - const auto minSize = std::min(sizeLhs, sizeRhs); - auto eval = [&](wchar_t* bufL, wchar_t* bufR) { - if (minSize > 0) //LCMapString does not allow input sizes of 0! - { - copyToUpperCase(lhs, bufL, minSize); - copyToUpperCase(rhs, bufR, minSize); - - const int rv = ::wmemcmp(bufL, bufR, minSize); - if (rv != 0) - return rv; - } + copyToUpperCase(lhs.c_str(), bufL); + copyToUpperCase(rhs.c_str(), bufR); + + const int rv = ::wmemcmp(bufL, bufR, minSize); + if (rv != 0) + return rv; + return static_cast(sizeLhs) - static_cast(sizeRhs); }; @@ -203,35 +208,55 @@ int z_impl::compareFilenamesNoCase(const wchar_t* lhs, const wchar_t* rhs, size_ } else //use freestore { - std::vector bufferL(minSize); - std::vector bufferR(minSize); - return eval(&bufferL[0], &bufferR[0]); + std::vector buffer(2 * minSize); + return eval(&buffer[0], &buffer[minSize]); } } } -void z_impl::makeFilenameUpperCase(wchar_t* str, size_t size) +Zstring makeUpperCopy(const Zstring& str) { - if (size == 0) //LCMapString does not allow input sizes of 0! - return; + const int len = static_cast(str.size()); + + if (len == 0) //LCMapString does not allow input sizes of 0! + return str; + + Zstring output; + output.resize(len); //use Windows' upper case conversion: faster than ::CharUpper() - if (::LCMapString(ZSTRING_INVARIANT_LOCALE, LCMAP_UPPERCASE, str, static_cast(size), str, static_cast(size)) == 0) - throw std::runtime_error("Error converting to upper case! (LCMapString)"); + if (::LCMapString(ZSTRING_INVARIANT_LOCALE, //__in LCID Locale, + LCMAP_UPPERCASE, //__in DWORD dwMapFlags, + str.c_str(), //__in LPCTSTR lpSrcStr, + len, //__in int cchSrc, + &*output.begin(), //__out LPTSTR lpDestStr, + len) == 0) //__in int cchDest + throw std::runtime_error("Error comparing strings (LCMapString)."); + + return output; } + #elif defined ZEN_MAC -int z_impl::compareFilenamesNoCase(const char* lhs, const char* rhs, size_t sizeLhs, size_t sizeRhs) +int cmpFileName(const Zstring& lhs, const Zstring& rhs) { - return ::strcasecmp(lhs, rhs); //locale-dependent! + return ::strcasecmp(lhs.c_str(), rhs.c_str()); //locale-dependent! } -void z_impl::makeFilenameUpperCase(char* str, size_t size) +Zstring makeUpperCopy(const Zstring& str) { - std::for_each(str, str + size, [](char& c) { c = static_cast(::toupper(static_cast(c))); }); //locale-dependent! + const size_t len = str.size(); + + Zstring output; + output.resize(len); + + std::transform(str.begin(), str.end(), output.begin(), [](char c) { return static_cast(::toupper(static_cast(c))); }); //locale-dependent! + //result of toupper() is an unsigned char mapped to int range, so the char representation is in the last 8 bits and we need not care about signedness! //this should work for UTF-8, too: all chars >= 128 are mapped upon themselves! + + return output; } #endif diff --git a/zen/zstring.h b/zen/zstring.h index 2152954d..94144386 100644 --- a/zen/zstring.h +++ b/zen/zstring.h @@ -4,8 +4,8 @@ // * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved * // ************************************************************************** -#ifndef ZSTRING_H_INCLUDED -#define ZSTRING_H_INCLUDED +#ifndef ZSTRING_H_INCLUDED_73425873425789 +#define ZSTRING_H_INCLUDED_73425873425789 #include "string_base.h" @@ -66,30 +66,26 @@ typedef zen::Zbase class SP, class AP> -int cmpFileName(const zen::Zbase& lhs, const zen::Zbase& rhs); +int cmpFileName(const Zstring& lhs, const Zstring& rhs); struct LessFilename //case-insensitive on Windows, case-sensitive on Linux { - template